<?php
/**
 * 
 * @package aowp.aspect.pointcut.designators
 */
/**
 * このポイントカットは、HTTPリクエスト処理 (HTTPリクエストの受け取りから、HTTPレスポンスの送信まで) のイベントを選択します。
 * 選択するジョインポイントは、{@link AOWP_ScriptExecutionJoinPoint}ですが、
 * 実行時評価 (フォームデータの値や、HTTPリクエストヘッダの値の評価) を伴って選択する為、
 * {@link AOWP_StaticPointcut}を継承しておらず、
 * {@link AOWP_ScriptExecutionPointcut}と異なります。
 * 
 * @package aowp.aspect.pointcut.designators
 */
class AOWP_RequestPointcut extends AOWP_Pointcut {
	
	private $_urlPattern;
	private $_formDataArray;
	private $_headerArray;
	private $_cookieArray;
	
	/**
	 *
	 * @param unknown_type $urlPattern プロジェクトのルートフォルダに対応するURLを/として記述
	 * @param array $formDataArray フォームデータの名前と値のパターン (mb_ereg)から成る連想配列
	 * @param array $headerArray ヘッダーの名前と値のパターン (mb_ereg)から成る連想配列。ヘッダーの名前は、$_SERVERと対応づけて記述。
	 */
	public function __construct($urlPattern, array $formDataArray = array(), array $headerArray = array(), array $cookieArray = array()) {
		$this->_urlPattern = $urlPattern;
		$this->_formDataArray = $formDataArray;
		$this->_headerArray = $headerArray;
		$this->_cookieArray = $cookieArray;
	}
	
	public function keyMatch($key) {
		if ($key instanceof AOWP_RequestPointcut) {
			if ($this->_urlPattern == $key->getURLPattern()) {
				$compareFormDataArray = $key->getFormDataArray();
				foreach ($this->_formDataArray as $formKey => $formValuePattern) {
					if ($formValuePattern !== $compareFormDataArray[$formKey]) {
						return false;
					}
				}
				$compareHeaderArray = $key->getHeaderArray();
				foreach ($this->_headerArray as $headerKey => $headerValuePattern) {
					if ($headerValuePattern !== $compareHeaderArray[$headerKey]) {
						return false;
					}
				}
				return true;
			}
			else {
				return false;
			}
		}
		else {
			return false;
		}
	}
	
	protected function _isJoinPointShadow(AOWP_JoinPoint $joinPoint) {
		if ($joinPoint instanceof AOWP_ScriptExecutionJoinPoint) {
			return $this->requestURLMatch($joinPoint->getFileName());
		}
		else if ($this->_advice instanceof AOWP_AfterAdvice && $joinPoint instanceof AOWP_ExitJoinPoint) {
//			AOWP_Logger::logging('RequestPointcut::_isJoinPointShadow: ' . get_class($this->_advice) . ' (' . $this->_advice->getIndex() . ')');
			return true;
		}
		else {
			return false;
		}
	}

	public function requestURLMatch($scriptPath) {
		return AOWP_Pointcut::patternMatch($this->_urlPattern, AOWP_RequestPointcut::_getRelativeURL($scriptPath));
	}
	private static function _getRelativeURL($sourcePath) {
		return strpos($sourcePath, '/') !== 0 ? $sourcePath : substr($sourcePath, strlen(AOWP_ConfigurationManager::getProjectHomePath()));
	}
	
	public function getURLPattern() {
		return $this->_urlPattern;
	}
	public function getFormDataArray() {
		return $this->_formDataArray;
	}
	public function getHeaderArray() {
		return $this->_headerArray;
	}
	
	public function existFormData($name, $valuePattern = null) {
		$this->_formDataArray[$name] = $valuePattern;
	}
	public function addHeaderPattern($name, $pattern) {
		$this->_headerArray[$name] = $pattern;
	}
	public function addCookiePattern($name, $valuePattern) {
		$this->_cookieArray[$name] = $valuePattern;
	}

	protected function _runtimeMatch(AOWP_JoinPoint $joinPoint) {
		if ($joinPoint instanceof AOWP_ScriptExecutionJoinPoint) {
			return AOWP_RequestPointcut::_checkTopLevelScriptExecution() && 
				$this->_runtimeFormDataMatch() && 
				$this->_runtimeHeaderMatch() && 
				$this->_runtimeCookieMatch();
		}
		else if ($joinPoint instanceof AOWP_ExitJoinPoint) {
			return $this->_runtimeURLMatch()&& 
				$this->_runtimeFormDataMatch() && 
				$this->_runtimeHeaderMatch() && 
				$this->_runtimeCookieMatch();
		}
		else {
			return false;
		}
	}
	
	private function _runtimeFormDataMatch() {
		foreach ($this->_formDataArray as $formDataName => $formDataValue) {
			if (!isset($_REQUEST[$formDataName]) || !mb_ereg_match($formDataValue, $_REQUEST[$formDataName])) {
				return false;
			}
		}
		return true;
	}
	private function _runtimeHeaderMatch() {
		foreach ($this->_headerArray as $headerName => $headerValue) {
			if (!isset($_SERVER[$headerName]) || !mb_ereg_match($headerValue, $_SERVER[$headerName])) {
				return false;
			}
		}
		return true;
	}
	private function _runtimeCookieMatch() {
		foreach ($this->_cookieArray as $cookieName => $cookieValue) {
			if (!isset($_COOKIE[$cookieName]) || !mb_ereg_match($cookieValue, $_COOKIE[$cookieName])) {
				return false;
			}
		}
		return true;
	}
	private function _runtimeURLMatch() {
		return $this->requestURLMatch($_SERVER['REQUEST_URI']);
	}
	
	private static function _checkTopLevelScriptExecution() {
		$traceArray = debug_backtrace();
		if (count($traceArray) > 0) {
			$lastTrace = $traceArray[count($traceArray) - 1];
			return isset($lastTrace['class']) && $lastTrace['class'] == 'AOWP_Aspect';
		}
		else {
			return false;
		}
	}
}
?>