xiaoguozi's Blog
Pay it forword - 我并不觉的自豪,我所尝试的事情都失败了······习惯原本生活的人不容易改变,就算现状很糟,他们也很难改变,在过程中,他们还是放弃了······他们一放弃,大家就都是输家······让爱传出去,很困难,也无法预料,人们需要更细心的观察别人,要随时注意才能保护别人,因为他们未必知道自己要什么·····

分类: YII 2011-11-21 16:57 737人阅读 评论(0) 收藏 举报

        http://www.localyii.com/testwebap/index.php?r=testmod/default/index。根据以前的知识,我们知道此url是访问的testmod module的default controller下的index action。

         对应的存储路径是:

       

  1. ├── protected   
  2. │   ├── modules  
  3. │   │   └── testmod  
  4. │   │       ├── components  
  5. │   │       ├── controllers  
  6. │   │       │   └── DefaultController.php  
  7. │   │       ├── messages  
  8. │   │       ├── models  
  9. │   │       ├── TestmodModule.php  
  10. │   │       └── views  
  11. │   │           ├── default  
  12. │   │           │   └── index.php  

           这里我们把testmod/default/index叫做路由。

      注意: 默认情况下,路由是大小写敏感的,从版本 1.0.1 开始,可以通过设置应用配置中的CUrlManager::caseSensitive 为 false 使路由对大小写不敏感。当在大小写不敏感模式中时, 要确保你遵循了相应的规则约定,即:包含控制器类文件的目录名小写,且 控制器映射 和 动作映射 中使用的键为小写。

    

     

    有时候我们可能需要自己定义url,以便创建的url能被框架理解或者有时候框架提供的url格式并不是我们想要的,我们可以自己进行自定义。在YII中,提供了CUrlManager来辅助完成。

    来看看CUrlManager,看看如何实现的,以及我们如何使用CUrlManager提供的方法创建我们自己的url。代码放在文档末尾。

   通过代码的注释CUrlManager.php,我们可以了解到此类的使用方法。

1. Creating URLs(创建网址)

public function createUrl($route,$params=array(),$ampersand='&')

/**
* 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. This anchor feature has been available since version 1.0.1.
* @param string $ampersand the token separating name-value pairs in the URL. Defaults to '&'.
* @return string the constructed URL
*/

虽然URL可被硬编码在控制器的视图(view)文件,但往往可以很灵活地动态创建它们:

$url=$this->createUrl($route,$params);

$this指的是控制器实例; $route指定请求的route 的要求;$params 列出了附加在网址中的GET参数。

默认情况下,URL以get格式使用createUrl 创建。例如,提供$route='post/read'$params=array('id'=>100) ,我们将获得以下网址:

/index.php?r=post/read&id=100 

参数以一系列Name=Value通过符号串联起来出现在请求字符串,r参数指的是请求的route 。这种URL格式用户友好性不是很好,因为它需要一些非字字符。

我们可以使上述网址看起来更简洁,更不言自明,通过采用所谓的'path格式,省去查询字符串和把GET参数加到路径信息,作为网址的一部分:

/index.php/post/read/id/100 

要更改URL格式,我们应该配置urlManager应用元件,以便createUrl可以自动切换到新格式和应用程序可以正确理解新的网址:

array(     ......     'components'=>array(         ......         'urlManager'=>array(             'urlFormat'=>'path',         ),     ), );

请注意,我们不需要指定的urlManager元件的类,因为它在CWebApplication预声明为CUrlManager

提示:此网址通过createurl方法所产生的是一个相对地址。为了得到一个绝对的url ,我们可以用前缀yii::app()->hostInfo ,或调用createAbsoluteUrl 。

下面举例子说明用法:

/yii_dev/testwebap/protected/modules/testmod/controllers/DefaultController.php

  1. <?php  
  2.   
  3. class DefaultController extends Controller  
  4. {  
  5.     public function actionIndex()  
  6.     {  
  7.         $route = 'post/read';  
  8.         $params=array('id'=>100);  
  9.         $url=$this->createUrl($route,$params);  
  10.         exit($url);  
  11.         $this->render('index');  
  12.     }  
  13. }  

http://www.localyii.com/testwebap/index.php?r=testmod/default/index

结果如下:

/testwebap/index.php?r=testmod/post/read&id=100

  1. <?php  
  2.   
  3. class DefaultController extends Controller  
  4. {  
  5.     public function actionIndex()  
  6.     {  
  7.         $route = 'post/read';  
  8.         $params=array('id'=>100,'name'=>'张三','#'=>'anchor');  
  9.         $url=$this->createUrl($route,$params);  
  10.         exit($url);  
  11.         $this->render('index');  
  12.     }  
  13. }  

http://www.localyii.com/testwebap/index.php?r=testmod/default/index

结果如下:

/testwebap/index.php?r=testmod/post/read&id=100&name=%E5%BC%A0%E4%B8%89#anchor



  1. <?php  
  2.   
  3. class DefaultController extends Controller  
  4. {  
  5.     public function actionIndex()  
  6.     {  
  7.         $route = 'post/read';  
  8.         $params=array('id'=>100,'name'=>'张三','#'=>'anchor');  
  9.         $url=$this->createAbsoluteUrl($route,$params);  
  10.         exit($url);  
  11.         $this->render('index');  
  12.     }  
  13. }  


http://www.localyii.com/testwebap/index.php?r=testmod/post/read&id=100&name=%E5%BC%A0%E4%B8%89#anchor



2. 自定义格式

具体实例,如下:

配置文件

  1. // application components  
  2. 'components'=>array(  
  3.     'user'=>array(  
  4.         // enable cookie-based authentication  
  5.         'allowAutoLogin'=>true,  
  6.     ),  
  7.     'urlManager'=>array(  
  8.         'urlFormat'=>'path',  
  9.             'rules'=>array(  
  10.             '<module:\w+>/<controller:\w+>/<action:\w+>'=>'<module>/<controller>/<action>',  
  11.         ),  
  12.     ),  

  1. <?php  
  2.   
  3. class DefaultController extends Controller  
  4. {  
  5.     public function actionIndex()  
  6.     {   
  7.         $this->render('index');  
  8.     }  
  9. }  

访问方式

http://www.localyii.com/testwebap/index.php/testmod/default/index

自定义格式的目的提供一个好记简短的单词提供给用户,这种url是对用户有好的。我们称之为user-friendly Urls

3. User-friendly URLs(用户友好的URL)

当用path格式URL,我们可以指定某些URL规则使我们的网址更用户友好性。例如,我们可以产生一个短短的URL/post/100 ,而不是冗长/index.php/post/read/id/100。网址创建和解析都是通过CUrlManager指定网址规则。

配置文件以及路由规则细说:

配置文件

在/yii_dev/testwebap/protected/config/main.php

yii给出的配置demo是

 

  1. /* 
  2.         'urlManager'=>array( 
  3.             'urlFormat'=>'path', 
  4.             'rules'=>array( 
  5.                 '<controller:\w+>/<id:\d+>'=>'<controller>/view', 
  6.                 '<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>', 
  7.                 '<controller:\w+>/<action:\w+>'=>'<controller>/<action>', 
  8.             ), 
  9.         ), 
  10. */  

 urlFromat的值 必须是 "path" 或 "get".

'path' format: /path/to/EntryScript.php/name1/value1/name2/value2...

'get'   format:  /path/to/EntryScript.php?name1=value1&name2=value2...

caseSensitive的值是true false

路由是否区分大小写。

例如

  1. // application components  
  2. 'components'=>array(  
  3.     'user'=>array(  
  4.         // enable cookie-based authentication  
  5.         'allowAutoLogin'=>true,  
  6.     ),  
  7.     'urlManager'=>array(  
  8.         'caseSensitive'=>false,  
  9.         'urlFormat'=>'path',  
  10.         'rules'=>array(  
  11.             '<module:\w+>/<controller:\w+>/<action:\w+>/<id:\d+>'=>'<module>/<controller>/<action>',  
  12.             '<module:\w+>/<controller:\w+>/<action:\w+>'=>'<module>/<controller>/<action>',  
  13.         ),  
  14.     ),  

http://www.localyii.com/testwebap/index.php/TESTmod/default/index

urlSuffix

是为路由设置一个后缀名称例如.html

rules的值要求是

'rules'=>array(                 'pattern1'=>'route1',                 'pattern2'=>'route2',                 'pattern3'=>'route3',             ),

array the URL rules (pattern=>route).

路由规则=>路由 

要指定的URL规则,我们必须设定urlManager 应用元件的属性rules

array(     ......     'components'=>array(         ......         'urlManager'=>array(             'urlFormat'=>'path',             'rules'=>array(                 'pattern1'=>'route1',                 'pattern2'=>'route2',                 'pattern3'=>'route3',             ),         ),     ), );

这些规则以一系列的路线格式对数组指定,每对对应于一个单一的规则。路线(route)的格式必须是有效的正则表达式,没有分隔符和修饰语。它是用于匹配网址的路径信息部分。还有route应指向一个有效的路线控制器。

规则可以绑定少量的GET参数。这些出现在规则格式的GET参数,以一种特殊令牌格式表现如下:

'pattern1'=>array('route1', 'urlSuffix'=>'.xml', 'caseSensitive'=>false)

In the above, the array contains a list of customized options. As of version 1.1.0, the following options are available:

  • urlSuffix: the URL suffix used specifically for this rule. Defaults to null, meaning using the value of CUrlManager::urlSuffix.

    后缀

  • caseSensitive: whether this rule is case sensitive. Defaults to null, meaning using the value of CUrlManager::caseSensitive.

    是否区分大小写

  • defaultParams: 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.

    提供默认的get参数值

  • matchValue: 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 CUrlManager::matchValue. If 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.


    GET参数值是否应匹配相应的规则中的子模式,创建一个URL时。默认为null,这意味着使用CUrlManager的matchValue。如果此属性为false,这意味着将按照它的路线和参数名称匹配的规则创建一个URL。如果此属性设置为true,给定的参数值也必须符合相应的参数子模式。请注意,此属性设置为true会降低性能。

Using Named Parameters(使用命名参数)

A rule can be associated with a few GET parameters. These GET parameters appear in the rule's pattern as special tokens in the following format:

一个规则可以有多个GET参数。这些GET参数可以通过特殊的参数令牌来对号入座。格式如下:

<ParamName:ParamPattern> 

ParamName表示GET参数名字,

可选项ParamPattern表示将用于匹配GET参数值的正则表达式。

当生成一个网址(URL)时,这些参数令牌将被相应的参数值替换;

当解析一个网址时,相应的GET参数将通过解析结果来生成。

我们使用一些例子来解释网址工作规则。我们假设我们的规则包括如下三个:

array(     'posts'=>'post/list',     'post/<id:\d+>'=>'post/read',     'post/<year:\d{4}>/<title>'=>'post/read', )
  • 调用$this->createUrl('post/list')生成/index.php/posts。第一个规则适用。

  • 调用$this->createUrl('post/read',array('id'=>100))生成/index.php/post/100。第二个规则适用。

  • 调用$this->createUrl('post/read',array('year'=>2008,'title'=>'a sample post'))生成/index.php/post/2008/a%20sample%20post。第三个规则适用。

  • 调用$this->createUrl('post/read')产生/index.php/post/read。请注意,没有规则适用。

总之,当使用createUrl生成网址,路线和传递给该方法的GET参数被用来决定哪些网址规则适用。如果关联规则中的每个参数可以在GET参数找到的,将被传递给createUrl ,如果路线的规则也匹配路线参数,规则将用来生成网址。

如果GET参数传递到createUrl是以上所要求的一项规则,其他参数将出现在查询字符串。

例如,如果我们调用$this->createUrl('post/read',array('id'=>100,'year'=>2008)) ,我们将获得/index.php/post/100?year=2008。为了使这些额外参数出现在路径信息的一部分,我们应该给规则附加/*。 因此,该规则post/<id:\d+>/* ,我们可以获取网址/index.php/post/100/year/2008 。

正如我们提到的,URL规则的其他用途是解析请求网址。当然,这是URL生成的一个逆过程。例如, 当用户请求/index.php/post/100 ,上面例子的第二个规则将适用来解析路线post/read和GET参数array('id'=>100) (可通过$_GET获得) 。

注:使用的URL规则将降低应用的性能。这是因为当解析请求的URL ,[ CUrlManager ]尝试使用每个规则来匹配它,直到某个规则可以适用。因此,高流量网站应用应尽量减少其使用的URL规则。

Parameterizing Routes路由参数

Starting from version 1.0.5, we may reference named parameters in the route part of a rule. This allows a rule to be applied to multiple routes based on matching criteria. It may also help reduce the number of rules needed for an application, and thus improve the overall performance.

从版本1.0.5开始,我们可能在一个路由规则中使用参数令牌。这样可以允许一个规则可以匹配多个路由。 有助于减少应用程序所需的规则的数量,从而提高整体性能。

We use the following example rules to illustrate how to parameterize routes with named parameters:

我们用下面的例子来说明如何使用:

array(     '<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>' => '<_c>/<_a>',     '<_c:(post|comment)>/<id:\d+>' => '<_c>/read',     '<_c:(post|comment)>s' => '<_c>/list', )

In the above, we use two named parameters in the route part of the rules: _c and _a. The former matches a controller ID to be either post or comment, while the latter matches an action ID to be createupdate or delete. You may name the parameters differently as long as they do not conflict with GET parameters that may appear in URLs.

在上面,我们使用了两种命名参数:_c和_a。前者相匹配的controllerID,要么 post 要么是 comment,而后者相匹配的actionID是createupdate 或者 delete

您可能随便定义参数的名称,只要他们不冲突即不会在网址的中出现即可。

Using the aboving rules, the URL /index.php/post/123/create would be parsed as the route post/create with GET parameter id=123. And given the route comment/list and GET parameter page=2, we can create a URL /index.php/comments?page=2.

使用上面的规则,

使用/ index.php/post/123/create会被解析为 post/create路由,GET参数id= 123。 

使用/index.php/comments?page=2.会被解析为  comment/list 和get参数page= 2

Parameterizing Hostnames主机参数

Starting from version 1.0.11, 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 http://admin.example.com/en/profile may be parsed into GET parameters user=admin and lang=en. On the other hand, rules with hostname may also be used to create URLs with paratermized hostnames.

从版本1.0.11开始,路由规则中也可能包括主机名。将主机的名称作为get参数的一部分。例如, http://admin.example.com/en/profile可能被解析成 user=admin and lang=en

另一方面,主机名的规则也可能被用来创建URL。

In order to use parameterized hostnames, simply declare URL rules with host info, e.g.:

array(     'http://<user:\w+>.example.com/<lang:\w+>/profile' => 'user/profile', )

The above example says that the first segment in the hostname should be treated as userparameter while the first segment in the path info should be lang parameter. The rule corresponds to the user/profile route.

上面的例子中,其中的url路径中的user、lang可以被解析成get参数的user和lang。该规则对应的路由是user/profile。

Note that CUrlManager::showScriptName will not take effect when a URL is being created using a rule with parameterized hostname.

注意如果在路由规则包含主机参数, CUrlManager::showScriptName 将不会有效。

Also note that the rule with parameterized hostname should NOT contain the sub-folder if the application is under a sub-folder of the Web root. For example, if the application is under http://www.example.com/sandbox/blog, then we should still use the same URL rule as described above without the sub-folder sandbox/blog.

还要注意的是主机参数的规则不应该包含的子文件夹,如果应用程序是一个子文件夹中的Web根下。例如,如果应用程序是根据http://www.example.com/sandbox/blog,那么我们使用上述URL规则解析sandbox/blog将不会得到正确的结果

4. 隐藏 index.php

还有一点,我们可以做进一步清理我们的网址,即在URL中藏匿index.php入口脚本。这就要求我们配置Web服务器,以及urlManager应用程序元件。

我们首先需要配置Web服务器,这样一个URL没有入口脚本仍然可以处理入口脚本。如果是Apache HTTP server,可以通过打开网址重写引擎和指定一些重写规则。这两个操作可以在包含入口脚本的目录下的.htaccess文件里实现。下面是一个示例:

Options +FollowSymLinks IndexIgnore */* RewriteEngine on  # if a directory or a file exists, use it directly RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d  # otherwise forward it to index.php RewriteRule . index.php 

然后,我们设定urlManager元件的showScriptName属性为 false

现在,如果我们调用$this->createUrl('post/read',array('id'=>100)) ,我们将获取网址/post/100。更重要的是,这个URL可以被我们的Web应用程序正确解析。

http://www.localyii.com/testwebap/site/login


.htaccess
 
  1. options +FollowSymLinks  
  2.   
  3. IndexIgnore */*  
  4. RewriteEngine on  
  5.   
  6. # if a directory or a file exists, use it directly  
  7. RewriteCond %{REQUEST_FILENAME} !-f  
  8. RewriteCond %{REQUEST_FILENAME} !-d  
  9.   
  10. # otherwise forward it to index.php  
  11. RewriteRule . index.php  


apache.conf
  1. <VirtualHost *:80>  
  2.         ServerAdmin webmaster@localhost  
  3.         ServerName  www.localyii.com  
  4.         ServerAlias www.localyii.com  
  5.         DocumentRoot /home/coder/adata/liuyuqiang/wamp/www/yii_dev  
  6.           
  7.         <Directory /home/coder/adata/liuyuqiang/wamp/www/yii_dev>  
  8.                 Options FollowSymLinks  
  9.                 AllowOverride  All  
  10.         </Directory>  
  11. </VirtualHost>  

必须是
  1. AllowOverride  All  

apache必须安装了rewrite模块

5.Faking URL Suffix(伪造URL后缀)

上面已经说过我们可以添加一些网址的后缀。例如,我们可以用/post/100.html来替代/post/100 。这使得它看起来更像一个静态网页URL。为了做到这一点,只需配置urlManager元件的urlSuffix属性为你所喜欢的后缀。这里不再举例说明。

6. 使用自定义URL规则设置类 

注意: Yii从1.1.8版本起支持自定义URL规则类

默认情况下,每个URL规则都通过CUrlManager来声明为一个CUrlRule对象,这个对象会解析当前请求并根据具体的规则来生成URL。 虽然CUrlRule可以处理大部分URL格式,但在某些特殊情况下仍旧有改进余地。

比如,在一个汽车销售网站上,可能会需要支持类似/Manufacturer/Model这样的URL格式, 其中Manufacturer 和 Model 都各自对应数据库中的一个表。此时CUrlRule就无能为力了。

我们可以通过继承CUrlRule的方式来创造一个新的URL规则类。并且使用这个类解析一个或者多个规则。 以上面提到的汽车销售网站为例,我们可以声明下面的URL规则。

array(     // 一个标准的URL规则,将 '/' 对应到 'site/index'     '' => 'site/index',       // 一个标准的URL规则,将 '/login' 对应到 'site/login', 等等     '<action:(login|logout|about)>' => 'site/<action>',       // 一个自定义URL规则,用来处理 '/Manufacturer/Model'     array(         'class' => 'application.components.CarUrlRule',         'connectionID' => 'db',     ),       // 一个标准的URL规则,用来处理 'post/update' 等     '<controller:\w+>/<action:\w+>' => '<controller>/<action>', ),

从以上可以看到,我们自定义了一个URL规则类CarUrlRule来处理类似/Manufacturer/Model这样的URL规则。 这个类可以这么写:

class CarUrlRule extends CBaseUrlRule {     public $connectionID = 'db';       public function createUrl($manager,$route,$params,$ampersand)     {         if ($route==='car/index')         {             if (isset($params['manufacturer'], $params['model']))                 return $params['manufacturer'] . '/' . $params['model'];             else if (isset($params['manufacturer']))                 return $params['manufacturer'];         }         return false;  // this rule does not apply     }       public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)     {         if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches))         {             // check $matches[1] and $matches[3] to see             // if they match a manufacturer and a model in the database             // If so, set $_GET['manufacturer'] and/or $_GET['model']             // and return 'car/index'         }         return false;  // this rule does not apply     } }

自定义URL规则类必须实现在CBaseUrlRule中定义的两个接口。

  • [CBaseUrlRule::createUrl()|createUrl()]
  • [CBaseUrlRule::parseUrl()|parseUrl()]

除了这种典型用法,自定义URL规则类还可以有其他的用途。比如,我们可以写一个规则类来记录有关URL解析和UEL创建的请求。 这对于正在开发中的网站来说很有用。我们还可以写一个规则类来在其他URL规则都匹配失败的时候显示一个自定义404页面。 注意,这种用法要求规则类在所有其他规则的最后声明。


记住,隐藏index.php.

 然后配置文件中使用如下:

  1. 'urlManager'=>array(  
  2.             'caseSensitive'=>false,  
  3.             'urlFormat'=>'path',  
  4.             'rules'=>array(  
  5.                   
  6.             ),  
  7.         ),  

你的地址栏就已经非常完美,非常清净。你不用去考虑定义烦人的url,不用使用让你恼怒的rule。一样可以赢得用户的心,得到搜索引擎的力挺。


/////////////////////////////代码区域/////////////////////////////////// 

CUrlManager.php

  1. <?php  
  2. /** 
  3.  * CUrlManager class file 
  4.  * 
  5.  * @author Qiang Xue <qiang.xue@gmail.com> 
  6.  * @link http://www.yiiframework.com/ 
  7.  * @copyright Copyright © 2008-2011 Yii Software LLC 
  8.  * @license http://www.yiiframework.com/license/ 
  9.  */  
  10.   
  11. /** 
  12.  * CUrlManager manages the URLs of Yii Web applications. 
  13.  * 
  14.  * It provides URL construction ({@link createUrl()}) as well as parsing ({@link parseUrl()}) functionality. 
  15.  * 
  16.  * URLs managed via CUrlManager can be in one of the following two formats, 
  17.  * by setting {@link setUrlFormat urlFormat} property: 
  18.  * <ul> 
  19.  * <li>'path' format: /path/to/EntryScript.php/name1/value1/name2/value2...</li> 
  20.  * <li>'get' format:  /path/to/EntryScript.php?name1=value1&name2=value2...</li> 
  21.  * </ul> 
  22.  * 
  23.  * When using 'path' format, CUrlManager uses a set of {@link setRules rules} to: 
  24.  * <ul> 
  25.  * <li>parse the requested URL into a route ('ControllerID/ActionID') and GET parameters;</li> 
  26.  * <li>create URLs based on the given route and GET parameters.</li> 
  27.  * </ul> 
  28.  * 
  29.  * A rule consists of a route and a pattern. The latter is used by CUrlManager to determine 
  30.  * which rule is used for parsing/creating URLs. A pattern is meant to match the path info 
  31.  * part of a URL. It may contain named parameters using the syntax '<ParamName:RegExp>'. 
  32.  * 
  33.  * When parsing a URL, a matching rule will extract the named parameters from the path info 
  34.  * and put them into the $_GET variable; when creating a URL, a matching rule will extract 
  35.  * the named parameters from $_GET and put them into the path info part of the created URL. 
  36.  * 
  37.  * If a pattern ends with '/*', it means additional GET parameters may be appended to the path 
  38.  * info part of the URL; otherwise, the GET parameters can only appear in the query string part. 
  39.  * 
  40.  * To specify URL rules, set the {@link setRules rules} property as an array of rules (pattern=>route). 
  41.  * For example, 
  42.  * <pre> 
  43.  * array( 
  44.  *     'articles'=>'article/list', 
  45.  *     'article/<id:\d+>/*'=>'article/read', 
  46.  * ) 
  47.  * </pre> 
  48.  * Two rules are specified in the above: 
  49.  * <ul> 
  50.  * <li>The first rule says that if the user requests the URL '/path/to/index.php/articles', 
  51.  *   it should be treated as '/path/to/index.php/article/list'; and vice versa applies 
  52.  *   when constructing such a URL.</li> 
  53.  * <li>The second rule contains a named parameter 'id' which is specified using 
  54.  *   the <ParamName:RegExp> syntax. It says that if the user requests the URL 
  55.  *   '/path/to/index.php/article/13', it should be treated as '/path/to/index.php/article/read?id=13'; 
  56.  *   and vice versa applies when constructing such a URL.</li> 
  57.  * </ul> 
  58.  * 
  59.  * Starting from version 1.0.5, the route part may contain references to named parameters defined 
  60.  * in the pattern part. This allows a rule to be applied to different routes based on matching criteria. 
  61.  * For example, 
  62.  * <pre> 
  63.  * array( 
  64.  *      '<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>'=>'<_c>/<_a>', 
  65.  *      '<_c:(post|comment)>/<id:\d+>'=>'<_c>/view', 
  66.  *      '<_c:(post|comment)>s/*'=>'<_c>/list', 
  67.  * ) 
  68.  * </pre> 
  69.  * In the above, we use two named parameters '<_c>' and '<_a>' in the route part. The '<_c>' 
  70.  * parameter matches either 'post' or 'comment', while the '<_a>' parameter matches an action ID. 
  71.  * 
  72.  * Like normal rules, these rules can be used for both parsing and creating URLs. 
  73.  * For example, using the rules above, the URL '/index.php/post/123/create' 
  74.  * would be parsed as the route 'post/create' with GET parameter 'id' being 123. 
  75.  * And given the route 'post/list' and GET parameter 'page' being 2, we should get a URL 
  76.  * '/index.php/posts/page/2'. 
  77.  * 
  78.  * Starting from version 1.0.11, it is also possible to include hostname into the rules 
  79.  * for parsing and creating URLs. One may extract part of the hostname to be a GET parameter. 
  80.  * For example, the URL <code>http://admin.example.com/en/profile</code> may be parsed into GET parameters 
  81.  * <code>user=admin</code> and <code>lang=en</code>. On the other hand, rules with hostname may also be used to 
  82.  * create URLs with parameterized hostnames. 
  83.  * 
  84.  * In order to use parameterized hostnames, simply declare URL rules with host info, e.g.: 
  85.  * <pre> 
  86.  * array( 
  87.  *     'http://<user:\w+>.example.com/<lang:\w+>/profile' => 'user/profile', 
  88.  * ) 
  89.  * </pre> 
  90.  * 
  91.  * Starting from version 1.1.8, one can write custom URL rule classes and use them for one or several URL rules. 
  92.  * For example, 
  93.  * <pre> 
  94.  * array( 
  95.  *   // a standard rule 
  96.  *   '<action:(login|logout)>' => 'site/<action>', 
  97.  *   // a custom rule using data in DB 
  98.  *   array( 
  99.  *     'class' => 'application.components.MyUrlRule', 
  100.  *     'connectionID' => 'db', 
  101.  *   ), 
  102.  * ) 
  103.  * </pre> 
  104.  * Please note that the custom URL rule class should extend from {@link CBaseUrlRule} and 
  105.  * implement the following two methods, 
  106.  * <ul> 
  107.  *    <li>{@link CBaseUrlRule::createUrl()}</li> 
  108.  *    <li>{@link CBaseUrlRule::parseUrl()}</li> 
  109.  * </ul> 
  110.  * 
  111.  * CUrlManager is a default application component that may be accessed via 
  112.  * {@link CWebApplication::getUrlManager()}. 
  113.  * 
  114.  * @author Qiang Xue <qiang.xue@gmail.com> 
  115.  * @version $Id: CUrlManager.php 3237 2011-05-25 13:13:26Z qiang.xue $ 
  116.  * @package system.web 
  117.  * @since 1.0 
  118.  */  
  119. class CUrlManager extends CApplicationComponent  
  120. {  
  121.     const CACHE_KEY='Yii.CUrlManager.rules';  
  122.     const GET_FORMAT='get';  
  123.     const PATH_FORMAT='path';  
  124.   
  125.     /** 
  126.      * @var array the URL rules (pattern=>route). 
  127.      */  
  128.     public $rules=array();  
  129.     /** 
  130.      * @var string the URL suffix used when in 'path' format. 
  131.      * For example, ".html" can be used so that the URL looks like pointing to a static HTML page. Defaults to empty. 
  132.      */  
  133.     public $urlSuffix='';  
  134.     /** 
  135.      * @var boolean whether to show entry script name in the constructed URL. Defaults to true. 
  136.      */  
  137.     public $showScriptName=true;  
  138.     /** 
  139.      * @var boolean whether to append GET parameters to the path info part. Defaults to true. 
  140.      * This property is only effective when {@link urlFormat} is 'path' and is mainly used when 
  141.      * creating URLs. When it is true, GET parameters will be appended to the path info and 
  142.      * separate from each other using slashes. If this is false, GET parameters will be in query part. 
  143.      * @since 1.0.3 
  144.      */  
  145.     public $appendParams=true;  
  146.     /** 
  147.      * @var string the GET variable name for route. Defaults to 'r'. 
  148.      */  
  149.     public $routeVar='r';  
  150.     /** 
  151.      * @var boolean whether routes are case-sensitive. Defaults to true. By setting this to false, 
  152.      * the route in the incoming request will be turned to lower case first before further processing. 
  153.      * As a result, you should follow the convention that you use lower case when specifying 
  154.      * controller mapping ({@link CWebApplication::controllerMap}) and action mapping 
  155.      * ({@link CController::actions}). Also, the directory names for organizing controllers should 
  156.      * be in lower case. 
  157.      * @since 1.0.1 
  158.      */  
  159.     public $caseSensitive=true;  
  160.     /** 
  161.      * @var boolean whether the GET parameter values should match the corresponding 
  162.      * sub-patterns in a rule before using it to create a URL. Defaults to false, meaning 
  163.      * a rule will be used for creating a URL only if its route and parameter names match the given ones. 
  164.      * If this property is set true, then the given parameter values must also match the corresponding 
  165.      * parameter sub-patterns. Note that setting this property to true will degrade performance. 
  166.      * @since 1.1.0 
  167.      */  
  168.     public $matchValue=false;  
  169.     /** 
  170.      * @var string the ID of the cache application component that is used to cache the parsed URL rules. 
  171.      * Defaults to 'cache' which refers to the primary cache application component. 
  172.      * Set this property to false if you want to disable caching URL rules. 
  173.      * @since 1.0.3 
  174.      */  
  175.     public $cacheID='cache';  
  176.     /** 
  177.      * @var boolean whether to enable strict URL parsing. 
  178.      * This property is only effective when {@link urlFormat} is 'path'. 
  179.      * If it is set true, then an incoming URL must match one of the {@link rules URL rules}. 
  180.      * Otherwise, it will be treated as an invalid request and trigger a 404 HTTP exception. 
  181.      * Defaults to false. 
  182.      * @since 1.0.6 
  183.      */  
  184.     public $useStrictParsing=false;  
  185.     /** 
  186.      * @var string the class name or path alias for the URL rule instances. Defaults to 'CUrlRule'. 
  187.      * If you change this to something else, please make sure that the new class must extend from 
  188.      * {@link CBaseUrlRule} and have the same constructor signature as {@link CUrlRule}. 
  189.      * It must also be serializable and autoloadable. 
  190.      * @since 1.1.8 
  191.      */  
  192.     public $urlRuleClass='CUrlRule';  
  193.   
  194.     private $_urlFormat=self::GET_FORMAT;  
  195.     private $_rules=array();  
  196.     private $_baseUrl;  
  197.   
  198.   
  199.     /** 
  200.      * Initializes the application component. 
  201.      */  
  202.     public function init()  
  203.     {  
  204.         parent::init();  
  205.         $this->processRules();  
  206.     }  
  207.   
  208.     /** 
  209.      * Processes the URL rules. 
  210.      */  
  211.     protected function processRules()  
  212.     {  
  213.         if(emptyempty($this->rules) || $this->getUrlFormat()===self::GET_FORMAT)  
  214.             return;  
  215.         if($this->cacheID!==false && ($cache=Yii::app()->getComponent($this->cacheID))!==null)  
  216.         {  
  217.             $hash=md5(serialize($this->rules));  
  218.             if(($data=$cache->get(self::CACHE_KEY))!==false && isset($data[1]) && $data[1]===$hash)  
  219.             {  
  220.                 $this->_rules=$data[0];  
  221.                 return;  
  222.             }  
  223.         }  
  224.         foreach($this->rules as $pattern=>$route)  
  225.             $this->_rules[]=$this->createUrlRule($route,$pattern);  
  226.         if(isset($cache))  
  227.             $cache->set(self::CACHE_KEY,array($this->_rules,$hash));  
  228.     }  
  229.   
  230.     /** 
  231.      * Adds new URL rules. 
  232.      * In order to make the new rules effective, this method must be called BEFORE 
  233.      * {@link CWebApplication::processRequest}. 
  234.      * @param array $rules new URL rules (pattern=>route). 
  235.      * @since 1.1.4 
  236.      */  
  237.     public function addRules($rules)  
  238.     {  
  239.         foreach($rules as $pattern=>$route)  
  240.             $this->_rules[]=$this->createUrlRule($route,$pattern);  
  241.     }  
  242.   
  243.     /** 
  244.      * Creates a URL rule instance. 
  245.      * The default implementation returns a CUrlRule object. 
  246.      * @param mixed $route the route part of the rule. This could be a string or an array 
  247.      * @param string $pattern the pattern part of the rule 
  248.      * @return CUrlRule the URL rule instance 
  249.      * @since 1.1.0 
  250.      */  
  251.     protected function createUrlRule($route,$pattern)  
  252.     {  
  253.         if(is_array($route) && isset($route['class']))  
  254.             return $route;  
  255.         else  
  256.             return new $this->urlRuleClass($route,$pattern);  
  257.     }  
  258.   
  259.     /** 
  260.      * Constructs a URL. 
  261.      * @param string $route the controller and the action (e.g. article/read) 
  262.      * @param array $params list of GET parameters (name=>value). Both the name and value will be URL-encoded. 
  263.      * If the name is '#', the corresponding value will be treated as an anchor 
  264.      * and will be appended at the end of the URL. This anchor feature has been available since version 1.0.1. 
  265.      * @param string $ampersand the token separating name-value pairs in the URL. Defaults to '&'. 
  266.      * @return string the constructed URL 
  267.      */  
  268.     public function createUrl($route,$params=array(),$ampersand='&')  
  269.     {  
  270.         unset($params[$this->routeVar]);  
  271.         foreach($params as &$param)  
  272.             if($param===null)  
  273.                 $param='';  
  274.         if(isset($params['#']))  
  275.         {  
  276.             $anchor='#'.$params['#'];  
  277.             unset($params['#']);  
  278.         }  
  279.         else  
  280.             $anchor='';  
  281.         $route=trim($route,'/');  
  282.         foreach($this->_rules as $i=>$rule)  
  283.         {  
  284.             if(is_array($rule))  
  285.                 $this->_rules[$i]=$rule=Yii::createComponent($rule);  
  286.             if(($url=$rule->createUrl($this,$route,$params,$ampersand))!==false)  
  287.             {  
  288.                 if($rule->hasHostInfo)  
  289.                     return $url==='' ? '/'.$anchor : $url.$anchor;  
  290.                 else  
  291.                     return $this->getBaseUrl().'/'.$url.$anchor;  
  292.             }  
  293.         }  
  294.         return $this->createUrlDefault($route,$params,$ampersand).$anchor;  
  295.     }  
  296.   
  297.     /** 
  298.      * Creates a URL based on default settings. 
  299.      * @param string $route the controller and the action (e.g. article/read) 
  300.      * @param array $params list of GET parameters 
  301.      * @param string $ampersand the token separating name-value pairs in the URL. 
  302.      * @return string the constructed URL 
  303.      */  
  304.     protected function createUrlDefault($route,$params,$ampersand)  
  305.     {  
  306.         if($this->getUrlFormat()===self::PATH_FORMAT)  
  307.         {  
  308.             $url=rtrim($this->getBaseUrl().'/'.$route,'/');  
  309.             if($this->appendParams)  
  310.             {  
  311.                 $url=rtrim($url.'/'.$this->createPathInfo($params,'/','/'),'/');  
  312.                 return $route==='' ? $url : $url.$this->urlSuffix;  
  313.             }  
  314.             else  
  315.             {  
  316.                 if($route!=='')  
  317.                     $url.=$this->urlSuffix;  
  318.                 $query=$this->createPathInfo($params,'=',$ampersand);  
  319.                 return $query==='' ? $url : $url.'?'.$query;  
  320.             }  
  321.         }  
  322.         else  
  323.         {  
  324.             $url=$this->getBaseUrl();  
  325.             if(!$this->showScriptName)  
  326.                 $url.='/';  
  327.             if($route!=='')  
  328.             {  
  329.                 $url.='?'.$this->routeVar.'='.$route;  
  330.                 if(($query=$this->createPathInfo($params,'=',$ampersand))!=='')  
  331.                     $url.=$ampersand.$query;  
  332.             }  
  333.             else if(($query=$this->createPathInfo($params,'=',$ampersand))!=='')  
  334.                 $url.='?'.$query;  
  335.             return $url;  
  336.         }  
  337.     }  
  338.   
  339.     /** 
  340.      * Parses the user request. 
  341.      * @param CHttpRequest $request the request application component 
  342.      * @return string the route (controllerID/actionID) and perhaps GET parameters in path format. 
  343.      */  
  344.     public function parseUrl($request)  
  345.     {  
  346.         if($this->getUrlFormat()===self::PATH_FORMAT)  
  347.         {  
  348.             $rawPathInfo=$request->getPathInfo();  
  349.             $pathInfo=$this->removeUrlSuffix($rawPathInfo,$this->urlSuffix);  
  350.             foreach($this->_rules as $i=>$rule)  
  351.             {  
  352.                 if(is_array($rule))  
  353.                     $this->_rules[$i]=$rule=Yii::createComponent($rule);  
  354.                 if(($r=$rule->parseUrl($this,$request,$pathInfo,$rawPathInfo))!==false)  
  355.                     return isset($_GET[$this->routeVar]) ? $_GET[$this->routeVar] : $r;  
  356.             }  
  357.             if($this->useStrictParsing)  
  358.                 throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".',  
  359.                     array('{route}'=>$pathInfo)));  
  360.             else  
  361.                 return $pathInfo;  
  362.         }  
  363.         else if(isset($_GET[$this->routeVar]))  
  364.             return $_GET[$this->routeVar];  
  365.         else if(isset($_POST[$this->routeVar]))  
  366.             return $_POST[$this->routeVar];  
  367.         else  
  368.             return '';  
  369.     }  
  370.   
  371.     /** 
  372.      * Parses a path info into URL segments and saves them to $_GET and $_REQUEST. 
  373.      * @param string $pathInfo path info 
  374.      * @since 1.0.3 
  375.      */  
  376.     public function parsePathInfo($pathInfo)  
  377.     {  
  378.         if($pathInfo==='')  
  379.             return;  
  380.         $segs=explode('/',$pathInfo.'/');  
  381.         $n=count($segs);  
  382.         for($i=0;$i<$n-1;$i+=2)  
  383.         {  
  384.             $key=$segs[$i];  
  385.             if($key==='') continue;  
  386.             $value=$segs[$i+1];  
  387.             if(($pos=strpos($key,'['))!==false && ($m=preg_match_all('/\[(.*?)\]/',$key,$matches))>0)  
  388.             {  
  389.                 $name=substr($key,0,$pos);  
  390.                 for($j=$m-1;$j>=0;--$j)  
  391.                 {  
  392.                     if($matches[1][$j]==='')  
  393.                         $value=array($value);  
  394.                     else  
  395.                         $value=array($matches[1][$j]=>$value);  
  396.                 }  
  397.                 if(isset($_GET[$name]) && is_array($_GET[$name]))  
  398.                     $value=CMap::mergeArray($_GET[$name],$value);  
  399.                 $_REQUEST[$name]=$_GET[$name]=$value;  
  400.             }  
  401.             else  
  402.                 $_REQUEST[$key]=$_GET[$key]=$value;  
  403.         }  
  404.     }  
  405.   
  406.     /** 
  407.      * Creates a path info based on the given parameters. 
  408.      * @param array $params list of GET parameters 
  409.      * @param string $equal the separator between name and value 
  410.      * @param string $ampersand the separator between name-value pairs 
  411.      * @param string $key this is used internally. 
  412.      * @return string the created path info 
  413.      * @since 1.0.3 
  414.      */  
  415.     public function createPathInfo($params,$equal,$ampersand, $key=null)  
  416.     {  
  417.         $pairs = array();  
  418.         foreach($params as $k => $v)  
  419.         {  
  420.             if ($key!==null)  
  421.                 $k = $key.'['.$k.']';  
  422.   
  423.             if (is_array($v))  
  424.                 $pairs[]=$this->createPathInfo($v,$equal,$ampersand, $k);  
  425.             else  
  426.                 $pairs[]=urlencode($k).$equal.urlencode($v);  
  427.         }  
  428.         return implode($ampersand,$pairs);  
  429.     }  
  430.   
  431.     /** 
  432.      * Removes the URL suffix from path info. 
  433.      * @param string $pathInfo path info part in the URL 
  434.      * @param string $urlSuffix the URL suffix to be removed 
  435.      * @return string path info with URL suffix removed. 
  436.      */  
  437.     public function removeUrlSuffix($pathInfo,$urlSuffix)  
  438.     {  
  439.         if($urlSuffix!=='' && substr($pathInfo,-strlen($urlSuffix))===$urlSuffix)  
  440.             return substr($pathInfo,0,-strlen($urlSuffix));  
  441.         else  
  442.             return $pathInfo;  
  443.     }  
  444.   
  445.     /** 
  446.      * Returns the base URL of the application. 
  447.      * @return string the base URL of the application (the part after host name and before query string). 
  448.      * If {@link showScriptName} is true, it will include the script name part. 
  449.      * Otherwise, it will not, and the ending slashes are stripped off. 
  450.      */  
  451.     public function getBaseUrl()  
  452.     {  
  453.         if($this->_baseUrl!==null)  
  454.             return $this->_baseUrl;  
  455.         else  
  456.         {  
  457.             if($this->showScriptName)  
  458.                 $this->_baseUrl=Yii::app()->getRequest()->getScriptUrl();  
  459.             else  
  460.                 $this->_baseUrl=Yii::app()->getRequest()->getBaseUrl();  
  461.             return $this->_baseUrl;  
  462.         }  
  463.     }  
  464.   
  465.     /** 
  466.      * Sets the base URL of the application (the part after host name and before query string). 
  467.      * This method is provided in case the {@link baseUrl} cannot be determined automatically. 
  468.      * The ending slashes should be stripped off. And you are also responsible to remove the script name 
  469.      * if you set {@link showScriptName} to be false. 
  470.      * @param string $value the base URL of the application 
  471.      * @since 1.1.1 
  472.      */  
  473.     public function setBaseUrl($value)  
  474.     {  
  475.         $this->_baseUrl=$value;  
  476.     }  
  477.   
  478.     /** 
  479.      * Returns the URL format. 
  480.      * @return string the URL format. Defaults to 'path'. Valid values include 'path' and 'get'. 
  481.      * Please refer to the guide for more details about the difference between these two formats. 
  482.      */  
  483.     public function getUrlFormat()  
  484.     {  
  485.         return $this->_urlFormat;  
  486.     }  
  487.   
  488.     /** 
  489.      * Sets the URL format. 
  490.      * @param string $value the URL format. It must be either 'path' or 'get'. 
  491.      */  
  492.     public function setUrlFormat($value)  
  493.     {  
  494.         if($value===self::PATH_FORMAT || $value===self::GET_FORMAT)  
  495.             $this->_urlFormat=$value;  
  496.         else  
  497.             throw new CException(Yii::t('yii','CUrlManager.UrlFormat must be either "path" or "get".'));  
  498.     }  
  499. }  
  500.   
  501.   
  502. /** 
  503.  * CBaseUrlRule is the base class for a URL rule class. 
  504.  * 
  505.  * Custom URL rule classes should extend from this class and implement two methods: 
  506.  * {@link createUrl} and {@link parseUrl}. 
  507.  * 
  508.  * @author Qiang Xue <qiang.xue@gmail.com> 
  509.  * @version $Id: CUrlManager.php 3237 2011-05-25 13:13:26Z qiang.xue $ 
  510.  * @package system.web 
  511.  * @since 1.1.8 
  512.  */  
  513. abstract class CBaseUrlRule extends CComponent  
  514. {  
  515.     /** 
  516.      * @var boolean whether this rule will also parse the host info part. Defaults to false. 
  517.      */  
  518.     public $hasHostInfo=false;  
  519.     /** 
  520.      * Creates a URL based on this rule. 
  521.      * @param CUrlManager $manager the manager 
  522.      * @param string $route the route 
  523.      * @param array $params list of parameters (name=>value) associated with the route 
  524.      * @param string $ampersand the token separating name-value pairs in the URL. 
  525.      * @return mixed the constructed URL. False if this rule does not apply. 
  526.      */  
  527.     abstract public function createUrl($manager,$route,$params,$ampersand);  
  528.     /** 
  529.      * Parses a URL based on this rule. 
  530.      * @param CUrlManager $manager the URL manager 
  531.      * @param CHttpRequest $request the request object 
  532.      * @param string $pathInfo path info part of the URL (URL suffix is already removed based on {@link CUrlManager::urlSuffix}) 
  533.      * @param string $rawPathInfo path info that contains the potential URL suffix 
  534.      * @return mixed the route that consists of the controller ID and action ID. False if this rule does not apply. 
  535.      */  
  536.     abstract public function parseUrl($manager,$request,$pathInfo,$rawPathInfo);  
  537. }  
  538.   
  539. /** 
  540.  * CUrlRule represents a URL formatting/parsing rule. 
  541.  * 
  542.  * It mainly consists of two parts: route and pattern. The former classifies 
  543.  * the rule so that it only applies to specific controller-action route. 
  544.  * The latter performs the actual formatting and parsing role. The pattern 
  545.  * may have a set of named parameters. 
  546.  * 
  547.  * @author Qiang Xue <qiang.xue@gmail.com> 
  548.  * @version $Id: CUrlManager.php 3237 2011-05-25 13:13:26Z qiang.xue $ 
  549.  * @package system.web 
  550.  * @since 1.0 
  551.  */  
  552. class CUrlRule extends CBaseUrlRule  
  553. {  
  554.     /** 
  555.      * @var string the URL suffix used for this rule. 
  556.      * For example, ".html" can be used so that the URL looks like pointing to a static HTML page. 
  557.      * Defaults to null, meaning using the value of {@link CUrlManager::urlSuffix}. 
  558.      * @since 1.0.6 
  559.      */  
  560.     public $urlSuffix;  
  561.     /** 
  562.      * @var boolean whether the rule is case sensitive. Defaults to null, meaning 
  563.      * using the value of {@link CUrlManager::caseSensitive}. 
  564.      * @since 1.0.1 
  565.      */  
  566.     public $caseSensitive;  
  567.     /** 
  568.      * @var array the default GET parameters (name=>value) that this rule provides. 
  569.      * When this rule is used to parse the incoming request, the values declared in this property 
  570.      * will be injected into $_GET. 
  571.      * @since 1.0.8 
  572.      */  
  573.     public $defaultParams=array();  
  574.     /** 
  575.      * @var boolean whether the GET parameter values should match the corresponding 
  576.      * sub-patterns in the rule when creating a URL. Defaults to null, meaning using the value 
  577.      * of {@link CUrlManager::matchValue}. When this property is false, it means 
  578.      * a rule will be used for creating a URL if its route and parameter names match the given ones. 
  579.      * If this property is set true, then the given parameter values must also match the corresponding 
  580.      * parameter sub-patterns. Note that setting this property to true will degrade performance. 
  581.      * @since 1.1.0 
  582.      */  
  583.     public $matchValue;  
  584.     /** 
  585.      * @var string the HTTP verb (e.g. GET, POST, DELETE) that this rule should match. 
  586.      * If this rule can match multiple verbs, please separate them with commas. 
  587.      * If this property is not set, the rule can match any verb. 
  588.      * Note that this property is only used when parsing a request. It is ignored for URL creation. 
  589.      * @since 1.1.7 
  590.      */  
  591.     public $verb;  
  592.     /** 
  593.      * @var boolean whether this rule is only used for request parsing. 
  594.      * Defaults to false, meaning the rule is used for both URL parsing and creation. 
  595.      * @since 1.1.7 
  596.      */  
  597.     public $parsingOnly=false;  
  598.     /** 
  599.      * @var string the controller/action pair 
  600.      */  
  601.     public $route;  
  602.     /** 
  603.      * @var array the mapping from route param name to token name (e.g. _r1=><1>) 
  604.      * @since 1.0.5 
  605.      */  
  606.     public $references=array();  
  607.     /** 
  608.      * @var string the pattern used to match route 
  609.      * @since 1.0.5 
  610.      */  
  611.     public $routePattern;  
  612.     /** 
  613.      * @var string regular expression used to parse a URL 
  614.      */  
  615.     public $pattern;  
  616.     /** 
  617.      * @var string template used to construct a URL 
  618.      */  
  619.     public $template;  
  620.     /** 
  621.      * @var array list of parameters (name=>regular expression) 
  622.      */  
  623.     public $params=array();  
  624.     /** 
  625.      * @var boolean whether the URL allows additional parameters at the end of the path info. 
  626.      */  
  627.     public $append;  
  628.     /** 
  629.      * @var boolean whether host info should be considered for this rule 
  630.      * @since 1.0.11 
  631.      */  
  632.     public $hasHostInfo;  
  633.   
  634.     /** 
  635.      * Constructor. 
  636.      * @param string $route the route of the URL (controller/action) 
  637.      * @param string $pattern the pattern for matching the URL 
  638.      */  
  639.     public function __construct($route,$pattern)  
  640.     {  
  641.         if(is_array($route))  
  642.         {  
  643.             foreach(array('urlSuffix', 'caseSensitive', 'defaultParams', 'matchValue', 'verb', 'parsingOnly') as $name)  
  644.             {  
  645.                 if(isset($route[$name]))  
  646.                     $this->$name=$route[$name];  
  647.             }  
  648.             if(isset($route['pattern']))  
  649.                 $pattern=$route['pattern'];  
  650.             $route=$route[0];  
  651.         }  
  652.         $this->route=trim($route,'/');  
  653.   
  654.         $tr2['/']=$tr['/']='\\/';  
  655.   
  656.         if(strpos($route,'<')!==false && preg_match_all('/<(\w+)>/',$route,$matches2))  
  657.         {  
  658.             foreach($matches2[1] as $name)  
  659.                 $this->references[$name]="<$name>";  
  660.         }  
  661.   
  662.         $this->hasHostInfo=!strncasecmp($pattern,'http://',7) || !strncasecmp($pattern,'https://',8);  
  663.   
  664.         if($this->verb!==null)  
  665.             $this->verb=preg_split('/[\s,]+/',strtoupper($this->verb),-1,PREG_SPLIT_NO_EMPTY);  
  666.   
  667.         if(preg_match_all('/<(\w+):?(.*?)?>/',$pattern,$matches))  
  668.         {  
  669.             $tokens=array_combine($matches[1],$matches[2]);  
  670.             foreach($tokens as $name=>$value)  
  671.             {  
  672.                 if($value==='')  
  673.                     $value='[^\/]+';  
  674.                 $tr["<$name>"]="(?P<$name>$value)";  
  675.                 if(isset($this->references[$name]))  
  676.                     $tr2["<$name>"]=$tr["<$name>"];  
  677.                 else  
  678.                     $this->params[$name]=$value;  
  679.             }  
  680.         }  
  681.         $p=rtrim($pattern,'*');  
  682.         $this->append=$p!==$pattern;  
  683.         $p=trim($p,'/');  
  684.         $this->template=preg_replace('/<(\w+):?.*?>/','<$1>',$p);  
  685.         $this->pattern='/^'.strtr($this->template,$tr).'\/';  
  686.         if($this->append)  
  687.             $this->pattern.='/u';  
  688.         else  
  689.             $this->pattern.='$/u';  
  690.   
  691.         if($this->references!==array())  
  692.             $this->routePattern='/^'.strtr($this->route,$tr2).'$/u';  
  693.   
  694.         if(YII_DEBUG && @preg_match($this->pattern,'test')===false)  
  695.             throw new CException(Yii::t('yii','The URL pattern "{pattern}" for route "{route}" is not a valid regular expression.',  
  696.                 array('{route}'=>$route,'{pattern}'=>$pattern)));  
  697.     }  
  698.   
  699.     /** 
  700.      * Creates a URL based on this rule. 
  701.      * @param CUrlManager $manager the manager 
  702.      * @param string $route the route 
  703.      * @param array $params list of parameters 
  704.      * @param string $ampersand the token separating name-value pairs in the URL. 
  705.      * @return mixed the constructed URL or false on error 
  706.      */  
  707.     public function createUrl($manager,$route,$params,$ampersand)  
  708.     {  
  709.         if($this->parsingOnly)  
  710.             return false;  
  711.   
  712.         if($manager->caseSensitive && $this->caseSensitive===null || $this->caseSensitive)  
  713.             $case='';  
  714.         else  
  715.             $case='i';  
  716.   
  717.         $tr=array();  
  718.         if($route!==$this->route)  
  719.         {  
  720.             if($this->routePattern!==null && preg_match($this->routePattern.$case,$route,$matches))  
  721.             {  
  722.                 foreach($this->references as $key=>$name)  
  723.                     $tr[$name]=$matches[$key];  
  724.             }  
  725.             else  
  726.                 return false;  
  727.         }  
  728.   
  729.         foreach($this->defaultParams as $key=>$value)  
  730.         {  
  731.             if(isset($params[$key]))  
  732.             {  
  733.                 if($params[$key]==$value)  
  734.                     unset($params[$key]);  
  735.                 else  
  736.                     return false;  
  737.             }  
  738.         }  
  739.   
  740.         foreach($this->params as $key=>$value)  
  741.             if(!isset($params[$key]))  
  742.                 return false;  
  743.   
  744.         if($manager->matchValue && $this->matchValue===null || $this->matchValue)  
  745.         {  
  746.             foreach($this->params as $key=>$value)  
  747.             {  
  748.                 if(!preg_match('/'.$value.'/'.$case,$params[$key]))  
  749.                     return false;  
  750.             }  
  751.         }  
  752.   
  753.         foreach($this->params as $key=>$value)  
  754.         {  
  755.             $tr["<$key>"]=urlencode($params[$key]);  
  756.             unset($params[$key]);  
  757.         }  
  758.   
  759.         $suffix=$this->urlSuffix===null ? $manager->urlSuffix : $this->urlSuffix;  
  760.   
  761.         $url=strtr($this->template,$tr);  
  762.   
  763.         if($this->hasHostInfo)  
  764.         {  
  765.             $hostInfo=Yii::app()->getRequest()->getHostInfo();  
  766.             if(stripos($url,$hostInfo)===0)  
  767.                 $url=substr($url,strlen($hostInfo));  
  768.         }  
  769.   
  770.         if(emptyempty($params))  
  771.             return $url!=='' ? $url.$suffix : $url;  
  772.   
  773.         if($this->append)  
  774.             $url.='/'.$manager->createPathInfo($params,'/','/').$suffix;  
  775.         else  
  776.         {  
  777.             if($url!=='')  
  778.                 $url.=$suffix;  
  779.             $url.='?'.$manager->createPathInfo($params,'=',$ampersand);  
  780.         }  
  781.   
  782.         return $url;  
  783.     }  
  784.   
  785.     /** 
  786.      * Parses a URL based on this rule. 
  787.      * @param CUrlManager $manager the URL manager 
  788.      * @param CHttpRequest $request the request object 
  789.      * @param string $pathInfo path info part of the URL 
  790.      * @param string $rawPathInfo path info that contains the potential URL suffix 
  791.      * @return mixed the route that consists of the controller ID and action ID or false on error 
  792.      */  
  793.     public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)  
  794.     {  
  795.         if($this->verb!==null && !in_array($request->getRequestType(), $this->verb, true))  
  796.             return false;  
  797.   
  798.         if($manager->caseSensitive && $this->caseSensitive===null || $this->caseSensitive)  
  799.             $case='';  
  800.         else  
  801.             $case='i';  
  802.   
  803.         if($this->urlSuffix!==null)  
  804.             $pathInfo=$manager->removeUrlSuffix($rawPathInfo,$this->urlSuffix);  
  805.   
  806.         // URL suffix required, but not found in the requested URL  
  807.         if($manager->useStrictParsing && $pathInfo===$rawPathInfo)  
  808.         {  
  809.             $urlSuffix=$this->urlSuffix===null ? $manager->urlSuffix : $this->urlSuffix;  
  810.             if($urlSuffix!='' && $urlSuffix!=='/')  
  811.                 return false;  
  812.         }  
  813.   
  814.         if($this->hasHostInfo)  
  815.             $pathInfo=strtolower($request->getHostInfo()).rtrim('/'.$pathInfo,'/');  
  816.   
  817.         $pathInfo.='/';  
  818.   
  819.         if(preg_match($this->pattern.$case,$pathInfo,$matches))  
  820.         {  
  821.             foreach($this->defaultParams as $name=>$value)  
  822.             {  
  823.                 if(!isset($_GET[$name]))  
  824.                     $_REQUEST[$name]=$_GET[$name]=$value;  
  825.             }  
  826.             $tr=array();  
  827.             foreach($matches as $key=>$value)  
  828.             {  
  829.                 if(isset($this->references[$key]))  
  830.                     $tr[$this->references[$key]]=$value;  
  831.                 else if(isset($this->params[$key]))  
  832.                     $_REQUEST[$key]=$_GET[$key]=$value;  
  833.             }  
  834.             if($pathInfo!==$matches[0]) // there're additional GET params  
  835.                 $manager->parsePathInfo(ltrim(substr($pathInfo,strlen($matches[0])),'/'));  
  836.             if($this->routePattern!==null)  
  837.                 return strtr($this->route,$tr);  
  838.             else  
  839.                 return $this->route;  
  840.         }  
  841.         else  
  842.             return false;  
  843.     }  
  844. }  








































 


 

YII Framework学习教程-YII的路由-2011-11-21
posted on 2012-03-20 20:59 小果子 阅读(6040) 评论(1)  编辑 收藏 引用 所属分类: 框架开源

FeedBack:
# re: yii 路由
2014-03-18 11:41 |
yii路由被get数据替换:
URL:localhost/index.php?r=poc/index
用form表单get方式提交数据,提交后的URL "r=poc/index"被get数据替换
URL:localhost/index.php/id=5
而正确的URL应该是localhost/index.php?r=poc/index&id=5
这样的情况应该怎么解决呢?求指教  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理