You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							837 lines
						
					
					
						
							27 KiB
						
					
					
				
			
		
		
	
	
							837 lines
						
					
					
						
							27 KiB
						
					
					
				| <?php | |
| /** | |
|  * UrlManager class file | |
|  * | |
|  * @link http://www.yiiframework.com/ | |
|  * @copyright Copyright © 2008 Yii Software LLC | |
|  * @license http://www.yiiframework.com/license/ | |
|  */ | |
|  | |
| namespace yii\base; | |
|  | |
| use \yii\base\Component; | |
|  | |
| /** | |
|  * UrlManager manages the URLs of Yii applications. | |
|  * | |
|  * It provides URL construction ({@link createUrl()}) as well as parsing ({@link parseUrl()}) functionality. | |
|  * | |
|  * URLs managed via UrlManager can be in one of the following two formats, | |
|  * by setting {@link setUrlFormat urlFormat} property: | |
|  * <ul> | |
|  * <li>'path' format: /path/to/EntryScript.php/name1/value1/name2/value2...</li> | |
|  * <li>'get' format:  /path/to/EntryScript.php?name1=value1&name2=value2...</li> | |
|  * </ul> | |
|  * | |
|  * When using 'path' format, UrlManager uses a set of {@link setRules rules} to: | |
|  * <ul> | |
|  * <li>parse the requested URL into a route ('ControllerID/ActionID') and GET parameters;</li> | |
|  * <li>create URLs based on the given route and GET parameters.</li> | |
|  * </ul> | |
|  * | |
|  * A rule consists of a route and a pattern. The latter is used by UrlManager to determine | |
|  * which rule is used for parsing/creating URLs. A pattern is meant to match the path info | |
|  * part of a URL. It may contain named parameters using the syntax '<ParamName:RegExp>'. | |
|  * | |
|  * When parsing a URL, a matching rule will extract the named parameters from the path info | |
|  * and put them into the $_GET variable; when creating a URL, a matching rule will extract | |
|  * the named parameters from $_GET and put them into the path info part of the created URL. | |
|  * | |
|  * If a pattern ends with '/*', it means additional GET parameters may be appended to the path | |
|  * info part of the URL; otherwise, the GET parameters can only appear in the query string part. | |
|  * | |
|  * To specify URL rules, set the {@link setRules rules} property as an array of rules (pattern=>route). | |
|  * For example, | |
|  * <pre> | |
|  * array( | |
|  *     'articles'=>'article/list', | |
|  *     'article/<id:\d+>/*'=>'article/read', | |
|  * ) | |
|  * </pre> | |
|  * Two rules are specified in the above: | |
|  * <ul> | |
|  * <li>The first rule says that if the user requests the URL '/path/to/index.php/articles', | |
|  *   it should be treated as '/path/to/index.php/article/list'; and vice versa applies | |
|  *   when constructing such a URL.</li> | |
|  * <li>The second rule contains a named parameter 'id' which is specified using | |
|  *   the <ParamName:RegExp> syntax. It says that if the user requests the URL | |
|  *   '/path/to/index.php/article/13', it should be treated as '/path/to/index.php/article/read?id=13'; | |
|  *   and vice versa applies when constructing such a URL.</li> | |
|  * </ul> | |
|  * | |
|  * The route part may contain references to named parameters defined in the pattern part. | |
|  * This allows a rule to be applied to different routes based on matching criteria. | |
|  * For example, | |
|  * <pre> | |
|  * array( | |
|  *      '<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>'=>'<_c>/<_a>', | |
|  *      '<_c:(post|comment)>/<id:\d+>'=>'<_c>/view', | |
|  *      '<_c:(post|comment)>s/*'=>'<_c>/list', | |
|  * ) | |
|  * </pre> | |
|  * In the above, we use two named parameters '<_c>' and '<_a>' in the route part. The '<_c>' | |
|  * parameter matches either 'post' or 'comment', while the '<_a>' parameter matches an action ID. | |
|  * | |
|  * Like normal rules, these rules can be used for both parsing and creating URLs. | |
|  * For example, using the rules above, the URL '/index.php/post/123/create' | |
|  * would be parsed as the route 'post/create' with GET parameter 'id' being 123. | |
|  * And given the route 'post/list' and GET parameter 'page' being 2, we should get a URL | |
|  * '/index.php/posts/page/2'. | |
|  * | |
|  * It is also possible to include hostname into the rules for parsing and creating URLs. | |
|  * One may extract part of the hostname to be a GET parameter. | |
|  * For example, the URL <code>http://admin.example.com/en/profile</code> may be parsed into GET parameters | |
|  * <code>user=admin</code> and <code>lang=en</code>. On the other hand, rules with hostname may also be used to | |
|  * create URLs with parameterized hostnames. | |
|  * | |
|  * In order to use parameterized hostnames, simply declare URL rules with host info, e.g.: | |
|  * <pre> | |
|  * array( | |
|  *     'http://<user:\w+>.example.com/<lang:\w+>/profile' => 'user/profile', | |
|  * ) | |
|  * </pre> | |
|  * | |
|  * If you want to customize URL generation and parsing you can write custom | |
|  * URL rule classes and use them for one or several URL rules. For example, | |
|  * <pre> | |
|  * array( | |
|  *   // a standard rule | |
|  *   '<action:(login|logout)>' => 'site/<action>', | |
|  *   // a custom rule using data in DB | |
|  *   array( | |
|  *     'class' => '\application\components\MyUrlRule', | |
|  *     'connectionID' => 'db', | |
|  *   ), | |
|  * ) | |
|  * </pre> | |
|  * Please note that the custom URL rule class should extend from {@link BaseUrlRule} and | |
|  * implement the following two methods, | |
|  * <ul> | |
|  *    <li>{@link BaseUrlRule::createUrl()}</li> | |
|  *    <li>{@link BaseUrlRule::parseUrl()}</li> | |
|  * </ul> | |
|  * | |
|  * UrlManager is a default application component that may be accessed via | |
|  * {@link \Yii::$application->urlManager}. | |
|  * | |
|  * @property string $baseUrl The base URL of the application (the part after host name and before query string). | |
|  * If {@link showScriptName} is true, it will include the script name part. | |
|  * Otherwise, it will not, and the ending slashes are stripped off. | |
|  * @property string $urlFormat The URL format. Defaults to 'path'. Valid values include 'path' and 'get'. | |
|  * Please refer to the guide for more details about the difference between these two formats. | |
|  * | |
|  * @author Qiang Xue <qiang.xue@gmail.com> | |
|  * @since 2.0 | |
|  */ | |
| class UrlManager extends Component | |
| { | |
| 	const CACHE_KEY='Yii.UrlManager.rules'; | |
| 	const GET_FORMAT='get'; | |
| 	const PATH_FORMAT='path'; | |
|  | |
| 	/** | |
| 	 * @var array the URL rules (pattern=>route). | |
| 	 */ | |
| 	public $rules=array(); | |
| 	/** | |
| 	 * @var string the URL suffix used when in 'path' format. | |
| 	 * For example, ".html" can be used so that the URL looks like pointing to a static HTML page. Defaults to empty. | |
| 	 */ | |
| 	public $urlSuffix=''; | |
| 	/** | |
| 	 * @var boolean whether to show entry script name in the constructed URL. Defaults to true. | |
| 	 */ | |
| 	public $showScriptName=true; | |
| 	/** | |
| 	 * @var boolean whether to append GET parameters to the path info part. Defaults to true. | |
| 	 * This property is only effective when {@link urlFormat} is 'path' and is mainly used when | |
| 	 * creating URLs. When it is true, GET parameters will be appended to the path info and | |
| 	 * separate from each other using slashes. If this is false, GET parameters will be in query part. | |
| 	 */ | |
| 	public $appendParams=true; | |
| 	/** | |
| 	 * @var string the GET variable name for route. Defaults to 'r'. | |
| 	 */ | |
| 	public $routeVar='r'; | |
| 	/** | |
| 	 * @var boolean whether routes are case-sensitive. Defaults to true. By setting this to false, | |
| 	 * the route in the incoming request will be turned to lower case first before further processing. | |
| 	 * As a result, you should follow the convention that you use lower case when specifying | |
| 	 * controller mapping ({@link CWebApplication::controllerMap}) and action mapping | |
| 	 * ({@link CController::actions}). Also, the directory names for organizing controllers should | |
| 	 * be in lower case. | |
| 	 */ | |
| 	public $caseSensitive=true; | |
| 	/** | |
| 	 * @var boolean whether the GET parameter values should match the corresponding | |
| 	 * sub-patterns in a rule before using it to create a URL. Defaults to false, meaning | |
| 	 * a rule will be used for creating a URL only if its route and parameter names match the given ones. | |
| 	 * If this property is set true, then the given parameter values must also match the corresponding | |
| 	 * parameter sub-patterns. Note that setting this property to true will degrade performance. | |
| 	 * @since 1.1.0 | |
| 	 */ | |
| 	public $matchValue=false; | |
| 	/** | |
| 	 * @var string the ID of the cache application component that is used to cache the parsed URL rules. | |
| 	 * Defaults to 'cache' which refers to the primary cache application component. | |
| 	 * Set this property to false if you want to disable caching URL rules. | |
| 	 */ | |
| 	public $cacheID='cache'; | |
| 	/** | |
| 	 * @var boolean whether to enable strict URL parsing. | |
| 	 * This property is only effective when {@link urlFormat} is 'path'. | |
| 	 * If it is set true, then an incoming URL must match one of the {@link rules URL rules}. | |
| 	 * Otherwise, it will be treated as an invalid request and trigger a 404 HTTP exception. | |
| 	 * Defaults to false. | |
| 	 */ | |
| 	public $useStrictParsing=false; | |
| 	/** | |
| 	 * @var string the class name or path alias for the URL rule instances. Defaults to 'CUrlRule'. | |
| 	 * If you change this to something else, please make sure that the new class must extend from | |
| 	 * {@link CBaseUrlRule} and have the same constructor signature as {@link CUrlRule}. | |
| 	 * It must also be serializable and autoloadable. | |
| 	 */ | |
| 	public $urlRuleClass='UrlRule'; | |
|  | |
| 	private $_urlFormat=self::GET_FORMAT; | |
| 	private $_rules=array(); | |
| 	private $_baseUrl; | |
|  | |
|  | |
| 	/** | |
| 	 * Initializes the application component. | |
| 	 */ | |
| 	public function init() | |
| 	{ | |
| 		parent::init(); | |
| 		$this->processRules(); | |
| 	} | |
|  | |
| 	/** | |
| 	 * Processes the URL rules. | |
| 	 */ | |
| 	protected function processRules() | |
| 	{ | |
| 		if(empty($this->rules) || $this->getUrlFormat()===self::GET_FORMAT) | |
| 			return; | |
| 		if($this->cacheID!==false && ($cache=\Yii::$application->getComponent($this->cacheID))!==null) | |
| 		{ | |
| 			$hash=md5(serialize($this->rules)); | |
| 			if(($data=$cache->get(self::CACHE_KEY))!==false && isset($data[1]) && $data[1]===$hash) | |
| 			{ | |
| 				$this->_rules=$data[0]; | |
| 				return; | |
| 			} | |
| 		} | |
| 		foreach($this->rules as $pattern=>$route) | |
| 			$this->_rules[]=$this->createUrlRule($route,$pattern); | |
| 		if(isset($cache)) | |
| 			$cache->set(self::CACHE_KEY,array($this->_rules,$hash)); | |
| 	} | |
|  | |
| 	/** | |
| 	 * Adds new URL rules. | |
| 	 * In order to make the new rules effective, this method must be called BEFORE | |
| 	 * {@link CWebApplication::processRequest}. | |
| 	 * @param array $rules new URL rules (pattern=>route). | |
| 	 * @param boolean $append whether the new URL rules should be appended to the existing ones. If false, | |
| 	 * they will be inserted at the beginning. | |
| 	 */ | |
| 	public function addRules($rules, $append=true) | |
| 	{ | |
| 		if ($append) | |
| 		{ | |
| 			foreach($rules as $pattern=>$route) | |
| 				$this->_rules[]=$this->createUrlRule($route,$pattern); | |
| 		} | |
| 		else | |
| 		{ | |
| 			foreach($rules as $pattern=>$route) | |
| 				array_unshift($this->_rules, $this->createUrlRule($route,$pattern)); | |
| 		} | |
| 	} | |
|  | |
| 	/** | |
| 	 * Creates a URL rule instance. | |
| 	 * The default implementation returns a CUrlRule object. | |
| 	 * @param mixed $route the route part of the rule. This could be a string or an array | |
| 	 * @param string $pattern the pattern part of the rule | |
| 	 * @return CUrlRule the URL rule instance | |
| 	 */ | |
| 	protected function createUrlRule($route,$pattern) | |
| 	{ | |
| 		if(is_array($route) && isset($route['class'])) | |
| 			return $route; | |
| 		else | |
| 			return new $this->urlRuleClass($route,$pattern); | |
| 	} | |
|  | |
| 	/** | |
| 	 * Constructs a URL. | |
| 	 * @param string $route the controller and the action (e.g. article/read) | |
| 	 * @param array $params list of GET parameters (name=>value). Both the name and value will be URL-encoded. | |
| 	 * If the name is '#', the corresponding value will be treated as an anchor | |
| 	 * and will be appended at the end of the URL. | |
| 	 * @param string $ampersand the token separating name-value pairs in the URL. Defaults to '&'. | |
| 	 * @return string the constructed URL | |
| 	 */ | |
| 	public function createUrl($route,$params=array(),$ampersand='&') | |
| 	{ | |
| 		unset($params[$this->routeVar]); | |
| 		foreach($params as $i=>$param) | |
| 			if($param===null) | |
| 				$params[$i]=''; | |
|  | |
| 		if(isset($params['#'])) | |
| 		{ | |
| 			$anchor='#'.$params['#']; | |
| 			unset($params['#']); | |
| 		} | |
| 		else | |
| 			$anchor=''; | |
| 		$route=trim($route,'/'); | |
| 		foreach($this->_rules as $i=>$rule) | |
| 		{ | |
| 			if(is_array($rule)) | |
| 				$this->_rules[$i]=$rule=Yii::createComponent($rule); | |
| 			if(($url=$rule->createUrl($this,$route,$params,$ampersand))!==false) | |
| 			{ | |
| 				if($rule->hasHostInfo) | |
| 					return $url==='' ? '/'.$anchor : $url.$anchor; | |
| 				else | |
| 					return $this->getBaseUrl().'/'.$url.$anchor; | |
| 			} | |
| 		} | |
| 		return $this->createUrlDefault($route,$params,$ampersand).$anchor; | |
| 	} | |
|  | |
| 	/** | |
| 	 * Creates a URL based on default settings. | |
| 	 * @param string $route the controller and the action (e.g. article/read) | |
| 	 * @param array $params list of GET parameters | |
| 	 * @param string $ampersand the token separating name-value pairs in the URL. | |
| 	 * @return string the constructed URL | |
| 	 */ | |
| 	protected function createUrlDefault($route,$params,$ampersand) | |
| 	{ | |
| 		if($this->getUrlFormat()===self::PATH_FORMAT) | |
| 		{ | |
| 			$url=rtrim($this->getBaseUrl().'/'.$route,'/'); | |
| 			if($this->appendParams) | |
| 			{ | |
| 				$url=rtrim($url.'/'.$this->createPathInfo($params,'/','/'),'/'); | |
| 				return $route==='' ? $url : $url.$this->urlSuffix; | |
| 			} | |
| 			else | |
| 			{ | |
| 				if($route!=='') | |
| 					$url.=$this->urlSuffix; | |
| 				$query=$this->createPathInfo($params,'=',$ampersand); | |
| 				return $query==='' ? $url : $url.'?'.$query; | |
| 			} | |
| 		} | |
| 		else | |
| 		{ | |
| 			$url=$this->getBaseUrl(); | |
| 			if(!$this->showScriptName) | |
| 				$url.='/'; | |
| 			if($route!=='') | |
| 			{ | |
| 				$url.='?'.$this->routeVar.'='.$route; | |
| 				if(($query=$this->createPathInfo($params,'=',$ampersand))!=='') | |
| 					$url.=$ampersand.$query; | |
| 			} | |
| 			else if(($query=$this->createPathInfo($params,'=',$ampersand))!=='') | |
| 				$url.='?'.$query; | |
| 			return $url; | |
| 		} | |
| 	} | |
|  | |
| 	/** | |
| 	 * Parses the user request. | |
| 	 * @param HttpRequest $request the request application component | |
| 	 * @return string the route (controllerID/actionID) and perhaps GET parameters in path format. | |
| 	 */ | |
| 	public function parseUrl($request) | |
| 	{ | |
| 		if($this->getUrlFormat()===self::PATH_FORMAT) | |
| 		{ | |
| 			$rawPathInfo=$request->getPathInfo(); | |
| 			$pathInfo=$this->removeUrlSuffix($rawPathInfo,$this->urlSuffix); | |
| 			foreach($this->_rules as $i=>$rule) | |
| 			{ | |
| 				if(is_array($rule)) | |
| 					$this->_rules[$i]=$rule=Yii::createComponent($rule); | |
| 				if(($r=$rule->parseUrl($this,$request,$pathInfo,$rawPathInfo))!==false) | |
| 					return isset($_GET[$this->routeVar]) ? $_GET[$this->routeVar] : $r; | |
| 			} | |
| 			if($this->useStrictParsing) | |
| 				throw new HttpException(404,Yii::t('yii:Unable to resolve the request "{route}".', | |
| 					array('{route}'=>$pathInfo))); | |
| 			else | |
| 				return $pathInfo; | |
| 		} | |
| 		else if(isset($_GET[$this->routeVar])) | |
| 			return $_GET[$this->routeVar]; | |
| 		else if(isset($_POST[$this->routeVar])) | |
| 			return $_POST[$this->routeVar]; | |
| 		else | |
| 			return ''; | |
| 	} | |
|  | |
| 	/** | |
| 	 * Parses a path info into URL segments and saves them to $_GET and $_REQUEST. | |
| 	 * @param string $pathInfo path info | |
| 	 */ | |
| 	public function parsePathInfo($pathInfo) | |
| 	{ | |
| 		if($pathInfo==='') | |
| 			return; | |
| 		$segs=explode('/',$pathInfo.'/'); | |
| 		$n=count($segs); | |
| 		for($i=0;$i<$n-1;$i+=2) | |
| 		{ | |
| 			$key=$segs[$i]; | |
| 			if($key==='') continue; | |
| 			$value=$segs[$i+1]; | |
| 			if(($pos=strpos($key,'['))!==false && ($m=preg_match_all('/\[(.*?)\]/',$key,$matches))>0) | |
| 			{ | |
| 				$name=substr($key,0,$pos); | |
| 				for($j=$m-1;$j>=0;--$j) | |
| 				{ | |
| 					if($matches[1][$j]==='') | |
| 						$value=array($value); | |
| 					else | |
| 						$value=array($matches[1][$j]=>$value); | |
| 				} | |
| 				if(isset($_GET[$name]) && is_array($_GET[$name])) | |
| 					$value=CMap::mergeArray($_GET[$name],$value); | |
| 				$_REQUEST[$name]=$_GET[$name]=$value; | |
| 			} | |
| 			else | |
| 				$_REQUEST[$key]=$_GET[$key]=$value; | |
| 		} | |
| 	} | |
|  | |
| 	/** | |
| 	 * Creates a path info based on the given parameters. | |
| 	 * @param array $params list of GET parameters | |
| 	 * @param string $equal the separator between name and value | |
| 	 * @param string $ampersand the separator between name-value pairs | |
| 	 * @param string $key this is used internally. | |
| 	 * @return string the created path info | |
| 	 */ | |
| 	public function createPathInfo($params,$equal,$ampersand, $key=null) | |
| 	{ | |
| 		$pairs = array(); | |
| 		foreach($params as $k => $v) | |
| 		{ | |
| 			if ($key!==null) | |
| 				$k = $key.'['.$k.']'; | |
|  | |
| 			if (is_array($v)) | |
| 				$pairs[]=$this->createPathInfo($v,$equal,$ampersand, $k); | |
| 			else | |
| 				$pairs[]=urlencode($k).$equal.urlencode($v); | |
| 		} | |
| 		return implode($ampersand,$pairs); | |
| 	} | |
|  | |
| 	/** | |
| 	 * Removes the URL suffix from path info. | |
| 	 * @param string $pathInfo path info part in the URL | |
| 	 * @param string $urlSuffix the URL suffix to be removed | |
| 	 * @return string path info with URL suffix removed. | |
| 	 */ | |
| 	public function removeUrlSuffix($pathInfo,$urlSuffix) | |
| 	{ | |
| 		if($urlSuffix!=='' && substr($pathInfo,-strlen($urlSuffix))===$urlSuffix) | |
| 			return substr($pathInfo,0,-strlen($urlSuffix)); | |
| 		else | |
| 			return $pathInfo; | |
| 	} | |
|  | |
| 	/** | |
| 	 * Returns the base URL of the application. | |
| 	 * @return string the base URL of the application (the part after host name and before query string). | |
| 	 * If {@link showScriptName} is true, it will include the script name part. | |
| 	 * Otherwise, it will not, and the ending slashes are stripped off. | |
| 	 */ | |
| 	public function getBaseUrl() | |
| 	{ | |
| 		if($this->_baseUrl!==null) | |
| 			return $this->_baseUrl; | |
| 		else | |
| 		{ | |
| 			if($this->showScriptName) | |
| 				$this->_baseUrl=\Yii::$application->getRequest()->getScriptUrl(); | |
| 			else | |
| 				$this->_baseUrl=\Yii::$application->getRequest()->getBaseUrl(); | |
| 			return $this->_baseUrl; | |
| 		} | |
| 	} | |
|  | |
| 	/** | |
| 	 * Sets the base URL of the application (the part after host name and before query string). | |
| 	 * This method is provided in case the {@link baseUrl} cannot be determined automatically. | |
| 	 * The ending slashes should be stripped off. And you are also responsible to remove the script name | |
| 	 * if you set {@link showScriptName} to be false. | |
| 	 * @param string $value the base URL of the application | |
| 	 */ | |
| 	public function setBaseUrl($value) | |
| 	{ | |
| 		$this->_baseUrl=$value; | |
| 	} | |
|  | |
| 	/** | |
| 	 * Returns the URL format. | |
| 	 * @return string the URL format. Defaults to 'path'. Valid values include 'path' and 'get'. | |
| 	 * Please refer to the guide for more details about the difference between these two formats. | |
| 	 */ | |
| 	public function getUrlFormat() | |
| 	{ | |
| 		return $this->_urlFormat; | |
| 	} | |
|  | |
| 	/** | |
| 	 * Sets the URL format. | |
| 	 * @param string $value the URL format. It must be either 'path' or 'get'. | |
| 	 */ | |
| 	public function setUrlFormat($value) | |
| 	{ | |
| 		if($value===self::PATH_FORMAT || $value===self::GET_FORMAT) | |
| 			$this->_urlFormat=$value; | |
| 		else | |
| 			throw new CException(Yii::t('yii:CUrlManager.UrlFormat must be either "path" or "get".')); | |
| 	} | |
| } | |
|  | |
|  | |
| /** | |
|  * CBaseUrlRule is the base class for a URL rule class. | |
|  * | |
|  * Custom URL rule classes should extend from this class and implement two methods: | |
|  * {@link createUrl} and {@link parseUrl}. | |
|  * | |
|  * @author Qiang Xue <qiang.xue@gmail.com> | |
|  */ | |
| abstract class CBaseUrlRule extends CComponent | |
| { | |
| 	/** | |
| 	 * @var boolean whether this rule will also parse the host info part. Defaults to false. | |
| 	 */ | |
| 	public $hasHostInfo=false; | |
| 	/** | |
| 	 * Creates a URL based on this rule. | |
| 	 * @param CUrlManager $manager the manager | |
| 	 * @param string $route the route | |
| 	 * @param array $params list of parameters (name=>value) associated with the route | |
| 	 * @param string $ampersand the token separating name-value pairs in the URL. | |
| 	 * @return mixed the constructed URL. False if this rule does not apply. | |
| 	 */ | |
| 	abstract public function createUrl($manager,$route,$params,$ampersand); | |
| 	/** | |
| 	 * Parses a URL based on this rule. | |
| 	 * @param UrlManager $manager the URL manager | |
| 	 * @param HttpRequest $request the request object | |
| 	 * @param string $pathInfo path info part of the URL (URL suffix is already removed based on {@link CUrlManager::urlSuffix}) | |
| 	 * @param string $rawPathInfo path info that contains the potential URL suffix | |
| 	 * @return mixed the route that consists of the controller ID and action ID. False if this rule does not apply. | |
| 	 */ | |
| 	abstract public function parseUrl($manager,$request,$pathInfo,$rawPathInfo); | |
| } | |
|  | |
| /** | |
|  * CUrlRule represents a URL formatting/parsing rule. | |
|  * | |
|  * It mainly consists of two parts: route and pattern. The former classifies | |
|  * the rule so that it only applies to specific controller-action route. | |
|  * The latter performs the actual formatting and parsing role. The pattern | |
|  * may have a set of named parameters. | |
|  * | |
|  * @author Qiang Xue <qiang.xue@gmail.com> | |
|  */ | |
| class CUrlRule extends CBaseUrlRule | |
| { | |
| 	/** | |
| 	 * @var string the URL suffix used for this rule. | |
| 	 * For example, ".html" can be used so that the URL looks like pointing to a static HTML page. | |
| 	 * Defaults to null, meaning using the value of {@link CUrlManager::urlSuffix}. | |
| 	 */ | |
| 	public $urlSuffix; | |
| 	/** | |
| 	 * @var boolean whether the rule is case sensitive. Defaults to null, meaning | |
| 	 * using the value of {@link CUrlManager::caseSensitive}. | |
| 	 */ | |
| 	public $caseSensitive; | |
| 	/** | |
| 	 * @var array the default GET parameters (name=>value) that this rule provides. | |
| 	 * When this rule is used to parse the incoming request, the values declared in this property | |
| 	 * will be injected into $_GET. | |
| 	 */ | |
| 	public $defaultParams=array(); | |
| 	/** | |
| 	 * @var boolean whether the GET parameter values should match the corresponding | |
| 	 * sub-patterns in the rule when creating a URL. Defaults to null, meaning using the value | |
| 	 * of {@link CUrlManager::matchValue}. When this property is false, it means | |
| 	 * a rule will be used for creating a URL if its route and parameter names match the given ones. | |
| 	 * If this property is set true, then the given parameter values must also match the corresponding | |
| 	 * parameter sub-patterns. Note that setting this property to true will degrade performance. | |
| 	 */ | |
| 	public $matchValue; | |
| 	/** | |
| 	 * @var string the HTTP verb (e.g. GET, POST, DELETE) that this rule should match. | |
| 	 * If this rule can match multiple verbs, please separate them with commas. | |
| 	 * If this property is not set, the rule can match any verb. | |
| 	 * Note that this property is only used when parsing a request. It is ignored for URL creation. | |
| 	 */ | |
| 	public $verb; | |
| 	/** | |
| 	 * @var boolean whether this rule is only used for request parsing. | |
| 	 * Defaults to false, meaning the rule is used for both URL parsing and creation. | |
| 	 */ | |
| 	public $parsingOnly=false; | |
| 	/** | |
| 	 * @var string the controller/action pair | |
| 	 */ | |
| 	public $route; | |
| 	/** | |
| 	 * @var array the mapping from route param name to token name (e.g. _r1=><1>) | |
| 	 */ | |
| 	public $references=array(); | |
| 	/** | |
| 	 * @var string the pattern used to match route | |
| 	 */ | |
| 	public $routePattern; | |
| 	/** | |
| 	 * @var string regular expression used to parse a URL | |
| 	 */ | |
| 	public $pattern; | |
| 	/** | |
| 	 * @var string template used to construct a URL | |
| 	 */ | |
| 	public $template; | |
| 	/** | |
| 	 * @var array list of parameters (name=>regular expression) | |
| 	 */ | |
| 	public $params=array(); | |
| 	/** | |
| 	 * @var boolean whether the URL allows additional parameters at the end of the path info. | |
| 	 */ | |
| 	public $append; | |
| 	/** | |
| 	 * @var boolean whether host info should be considered for this rule | |
| 	 */ | |
| 	public $hasHostInfo; | |
|  | |
| 	/** | |
| 	 * Constructor. | |
| 	 * @param string $route the route of the URL (controller/action) | |
| 	 * @param string $pattern the pattern for matching the URL | |
| 	 */ | |
| 	public function __construct($route,$pattern) | |
| 	{ | |
| 		if(is_array($route)) | |
| 		{ | |
| 			foreach(array('urlSuffix', 'caseSensitive', 'defaultParams', 'matchValue', 'verb', 'parsingOnly') as $name) | |
| 			{ | |
| 				if(isset($route[$name])) | |
| 					$this->$name=$route[$name]; | |
| 			} | |
| 			if(isset($route['pattern'])) | |
| 				$pattern=$route['pattern']; | |
| 			$route=$route[0]; | |
| 		} | |
| 		$this->route=trim($route,'/'); | |
|  | |
| 		$tr2['/']=$tr['/']='\\/'; | |
|  | |
| 		if(strpos($route,'<')!==false && preg_match_all('/<(\w+)>/',$route,$matches2)) | |
| 		{ | |
| 			foreach($matches2[1] as $name) | |
| 				$this->references[$name]="<$name>"; | |
| 		} | |
|  | |
| 		$this->hasHostInfo=!strncasecmp($pattern,'http://',7) || !strncasecmp($pattern,'https://',8); | |
|  | |
| 		if($this->verb!==null) | |
| 			$this->verb=preg_split('/[\s,]+/',strtoupper($this->verb),-1,PREG_SPLIT_NO_EMPTY); | |
|  | |
| 		if(preg_match_all('/<(\w+):?(.*?)?>/',$pattern,$matches)) | |
| 		{ | |
| 			$tokens=array_combine($matches[1],$matches[2]); | |
| 			foreach($tokens as $name=>$value) | |
| 			{ | |
| 				if($value==='') | |
| 					$value='[^\/]+'; | |
| 				$tr["<$name>"]="(?P<$name>$value)"; | |
| 				if(isset($this->references[$name])) | |
| 					$tr2["<$name>"]=$tr["<$name>"]; | |
| 				else | |
| 					$this->params[$name]=$value; | |
| 			} | |
| 		} | |
| 		$p=rtrim($pattern,'*'); | |
| 		$this->append=$p!==$pattern; | |
| 		$p=trim($p,'/'); | |
| 		$this->template=preg_replace('/<(\w+):?.*?>/','<$1>',$p); | |
| 		$this->pattern='/^'.strtr($this->template,$tr).'\/'; | |
| 		if($this->append) | |
| 			$this->pattern.='/u'; | |
| 		else | |
| 			$this->pattern.='$/u'; | |
|  | |
| 		if($this->references!==array()) | |
| 			$this->routePattern='/^'.strtr($this->route,$tr2).'$/u'; | |
|  | |
| 		if(YII_DEBUG && @preg_match($this->pattern,'test')===false) | |
| 			throw new CException(Yii::t('yii:The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.', | |
| 				array('{route}'=>$route,'{pattern}'=>$pattern))); | |
| 	} | |
|  | |
| 	/** | |
| 	 * Creates a URL based on this rule. | |
| 	 * @param CUrlManager $manager the manager | |
| 	 * @param string $route the route | |
| 	 * @param array $params list of parameters | |
| 	 * @param string $ampersand the token separating name-value pairs in the URL. | |
| 	 * @return mixed the constructed URL or false on error | |
| 	 */ | |
| 	public function createUrl($manager,$route,$params,$ampersand) | |
| 	{ | |
| 		if($this->parsingOnly) | |
| 			return false; | |
|  | |
| 		if($manager->caseSensitive && $this->caseSensitive===null || $this->caseSensitive) | |
| 			$case=''; | |
| 		else | |
| 			$case='i'; | |
|  | |
| 		$tr=array(); | |
| 		if($route!==$this->route) | |
| 		{ | |
| 			if($this->routePattern!==null && preg_match($this->routePattern.$case,$route,$matches)) | |
| 			{ | |
| 				foreach($this->references as $key=>$name) | |
| 					$tr[$name]=$matches[$key]; | |
| 			} | |
| 			else | |
| 				return false; | |
| 		} | |
|  | |
| 		foreach($this->defaultParams as $key=>$value) | |
| 		{ | |
| 			if(isset($params[$key])) | |
| 			{ | |
| 				if($params[$key]==$value) | |
| 					unset($params[$key]); | |
| 				else | |
| 					return false; | |
| 			} | |
| 		} | |
|  | |
| 		foreach($this->params as $key=>$value) | |
| 			if(!isset($params[$key])) | |
| 				return false; | |
|  | |
| 		if($manager->matchValue && $this->matchValue===null || $this->matchValue) | |
| 		{ | |
| 			foreach($this->params as $key=>$value) | |
| 			{ | |
| 				if(!preg_match('/\A'.$value.'\z/u'.$case,$params[$key])) | |
| 					return false; | |
| 			} | |
| 		} | |
|  | |
| 		foreach($this->params as $key=>$value) | |
| 		{ | |
| 			$tr["<$key>"]=urlencode($params[$key]); | |
| 			unset($params[$key]); | |
| 		} | |
|  | |
| 		$suffix=$this->urlSuffix===null ? $manager->urlSuffix : $this->urlSuffix; | |
|  | |
| 		$url=strtr($this->template,$tr); | |
|  | |
| 		if($this->hasHostInfo) | |
| 		{ | |
| 			$hostInfo=\Yii::$application->getRequest()->getHostInfo(); | |
| 			if(stripos($url,$hostInfo)===0) | |
| 				$url=substr($url,strlen($hostInfo)); | |
| 		} | |
|  | |
| 		if(empty($params)) | |
| 			return $url!=='' ? $url.$suffix : $url; | |
|  | |
| 		if($this->append) | |
| 			$url.='/'.$manager->createPathInfo($params,'/','/').$suffix; | |
| 		else | |
| 		{ | |
| 			if($url!=='') | |
| 				$url.=$suffix; | |
| 			$url.='?'.$manager->createPathInfo($params,'=',$ampersand); | |
| 		} | |
|  | |
| 		return $url; | |
| 	} | |
|  | |
| 	/** | |
| 	 * Parses a URL based on this rule. | |
| 	 * @param UrlManager $manager the URL manager | |
| 	 * @param HttpRequest $request the request object | |
| 	 * @param string $pathInfo path info part of the URL | |
| 	 * @param string $rawPathInfo path info that contains the potential URL suffix | |
| 	 * @return mixed the route that consists of the controller ID and action ID or false on error | |
| 	 */ | |
| 	public function parseUrl($manager,$request,$pathInfo,$rawPathInfo) | |
| 	{ | |
| 		if($this->verb!==null && !in_array($request->getRequestType(), $this->verb, true)) | |
| 			return false; | |
|  | |
| 		if($manager->caseSensitive && $this->caseSensitive===null || $this->caseSensitive) | |
| 			$case=''; | |
| 		else | |
| 			$case='i'; | |
|  | |
| 		if($this->urlSuffix!==null) | |
| 			$pathInfo=$manager->removeUrlSuffix($rawPathInfo,$this->urlSuffix); | |
|  | |
| 		// URL suffix required, but not found in the requested URL | |
| 		if($manager->useStrictParsing && $pathInfo===$rawPathInfo) | |
| 		{ | |
| 			$urlSuffix=$this->urlSuffix===null ? $manager->urlSuffix : $this->urlSuffix; | |
| 			if($urlSuffix!='' && $urlSuffix!=='/') | |
| 				return false; | |
| 		} | |
|  | |
| 		if($this->hasHostInfo) | |
| 			$pathInfo=strtolower($request->getHostInfo()).rtrim('/'.$pathInfo,'/'); | |
|  | |
| 		$pathInfo.='/'; | |
|  | |
| 		if(preg_match($this->pattern.$case,$pathInfo,$matches)) | |
| 		{ | |
| 			foreach($this->defaultParams as $name=>$value) | |
| 			{ | |
| 				if(!isset($_GET[$name])) | |
| 					$_REQUEST[$name]=$_GET[$name]=$value; | |
| 			} | |
| 			$tr=array(); | |
| 			foreach($matches as $key=>$value) | |
| 			{ | |
| 				if(isset($this->references[$key])) | |
| 					$tr[$this->references[$key]]=$value; | |
| 				else if(isset($this->params[$key])) | |
| 					$_REQUEST[$key]=$_GET[$key]=$value; | |
| 			} | |
| 			if($pathInfo!==$matches[0]) // there're additional GET params | |
| 				$manager->parsePathInfo(ltrim(substr($pathInfo,strlen($matches[0])),'/')); | |
| 			if($this->routePattern!==null) | |
| 				return strtr($this->route,$tr); | |
| 			else | |
| 				return $this->route; | |
| 		} | |
| 		else | |
| 			return false; | |
| 	} | |
| } |