YII的路由

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

对应的存储路径是:

01 ├── protected
02 │   ├── modules
03 │   │   └── testmod
04 │   │       ├── components
05 │   │       ├── controllers
06 │   │       │   └── DefaultController.php
07 │   │       ├── messages
08 │   │       ├── models
09 │   │       ├── 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可以自动切换到新格式和应用程序可以正确理解新的网址:

1 array(
2 ……
3 ‘components’=>array(
4 ……
5 ‘urlManager’=>array(
6 ‘urlFormat’=>’path’,
7 ),
8 ),
9 );

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

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

下面举例子说明用法:

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

01 <?php
02
03 class DefaultController extends Controller
04 {
05 public function actionIndex()
06 {
07 $route = ‘post/read’;
08 $params=array(‘id’=>100);
09 $url=$this->createUrl($route,$params);
10 exit($url);
11 $this->render(‘index’);
12 }
13 }

http://localhost/testwebap/index.php?r=testmod/default/index

结果如下:

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

01 <?php
02
03 class DefaultController extends Controller
04 {
05 public function actionIndex()
06 {
07 $route = ‘post/read’;
08 $params=array(‘id’=>100,‘name’=>‘张三’,‘#’=>‘anchor’);
09 $url=$this->createUrl($route,$params);
10 exit($url);
11 $this->render(‘index’);
12 }
13 }

http://localhost/testwebap/index.php?r=testmod/default/index

结果如下:

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

2. 自定义格式

具体实例,如下:

配置文件

01 // application components
02 ‘components’=>array(
03 ‘user’=>array(
04 // enable cookie-based authentication
05 ‘allowAutoLogin’=>true,
06 ),
07 ‘urlManager’=>array(
08 ‘urlFormat’=>’path’,
09 ‘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://localhost/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是

01 /*
02 ‘urlManager’=>array(
03 ‘urlFormat’=>’path’,
04 ‘rules’=>array(
05 ‘<controller:\w+>/<id:\d+>’=>’<controller>/view’,
06 ‘<controller:\w+>/<action:\w+>/<id:\d+>’=>’<controller>/<action>’,
07 ‘<controller:\w+>/<action:\w+>’=>’<controller>/<action>’,
08 ),
09 ),
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

路由是否区分大小写。

例如

01 // application components
02 ‘components’=>array(
03 ‘user’=>array(
04 // enable cookie-based authentication
05 ‘allowAutoLogin’=>true,
06 ),
07 ‘urlManager’=>array(
08 ‘caseSensitive’=>false,
09 ‘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://localhost/testwebap/index.php/TESTmod/default/index

urlSuffix

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

rules的值要求是

1 ‘rules’=>array(
2 ‘pattern1′=>’route1′,
3 ‘pattern2′=>’route2′,
4 ‘pattern3′=>’route3′,
5 ),

array the URL rules (pattern=>route).

路由规则=>路由

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

01 array(
02 ……
03 ‘components’=>array(
04 ……
05 ‘urlManager’=>array(
06 ‘urlFormat’=>’path’,
07 ‘rules’=>array(
08 ‘pattern1′=>’route1′,
09 ‘pattern2′=>’route2′,
10 ‘pattern3′=>’route3′,
11 ),
12 ),
13 ),
14 );

这些规则以一系列的路线格式对数组指定,每对对应于一个单一的规则。路线(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表示GET参数名字,

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

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

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

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

array(
    posts=>post/list,
    post/<id:\d+>=>post/read,
    post/<year:\d{4}>/<title>=>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. IndexIgnore */*
  3. RewriteEngine on
  4. # if a directory or a file exists, use it directly
  5. RewriteCond %{REQUEST_FILENAME} !-f
  6. RewriteCond %{REQUEST_FILENAME} !-d
  7. # otherwise forward it to index.php
  8. 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. <Directory /home/coder/adata/liuyuqiang/wamp/www/yii_dev>
  7. Options FollowSymLinks
  8. AllowOverride  All
  9. </Directory>
  10. </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中定义的两个接口。

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

记住,隐藏index.php.

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

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

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

yii之layout中column和main文件的关系

在yii创建应用成果之后,在view/layouts/目录下,会产生3个布局页面:

main.php

column1.php

column2.php

因为首次使用的是命令行Shell方式创建的应用,yii本身会创建一个控制器组件:Controller.php 继承了CController控制器,该文件位于/components目录下,

01 //componets/Controller.php文件内容如下
02
03 /**
04 * Controller is the customized base controller class.
05 * All controller classes for this application should extend from this base class.
06 */
07 class Controller extends CController
08 {
09 /**
10 * @var string the default layout for the controller view. Defaults to ‘//layouts/column1′,
11 * meaning using a single column layout. See ‘protected/views/layouts/column1.php’.
12 */
13 public $layout=’//layouts/column1′;
14 … …
15 }

文件里设置了layout的默认页面为: ‘//layouts/column1′,然后再view/layouts/中,column1再次调用main.php视图文件,包裹了两个div层.

1 //column1.php文件内容
2
3 <?php $this->beginContent(‘//layouts/main’); ?>
4 <div class=”container”>
5 <div id=”content”>
6 <?php echo $content; ?>
7 </div><!– content –>
8 </div>
9 <?php $this->endContent(); ?>

包裹的两个div层,默认class名称为container,这可能与自己命名的container有冲突,需要视实际情况做一些调整,加载完main.php文件之后,在包含index.php中的内容即$content中的内容.

如果控制器都是由Gii这个脚手架自动生成,那么所有的控制器都会继承都是继承于Controller而非官方所说的继承与CController控制器,在页面视图渲染,多了一层column1.php中间视图.

所以说yii在 $this->render(‘index’) 一个页面的时候,使用 column1.php 包含 main.php , 再由 main.php 包含 index.php,最后返回内容.(这是针对于继承Controller方式).

而至于column2.php只干什么的呢,貌似是个打酱油的,没有用到.

如果我们想更改默认的layout视图文件,要么直接在Components/Controller.php更改$layout = ‘//layouts/newlayout_name’,要么控制器继承时,直接 extends CController 而不是Controller,然后配置config/main.php:

1 //第二种方式 更改config/main.php,添加layout应用级别layout
2 return array(
3 … …
4 ‘name’ => ‘Web Application’,
5 ‘layout’ => ‘newlayout_name’,
6 … …
7 )

然后在控制器里调用:

1 //TestController 自定义一个测试控制器
2 class TestController extends CController{
3 … …
4 }

当然你也可以直接在控制器里设置layout属性,覆盖默认的layout,使得视图渲染更灵活.此处只是为说明 /view 下layouts/中,main.php和column1.php,以及index.php之间的关系.

yii之Active Record

Active Record

至于什么是AR,以及yii中AR的大致使用方法,这里不做笔墨,可参见yiiframework官方文档,在这里只是想对yii中的AR使用方法做一点示例,因为官方给出了方法,但是例子稍微逊色,自己趁练习之余笔记下来.

yii AR官方文档地址:    Active Record

为了做测试,创建了一张user表,共3个字段,并且插入了4条数据.如下:

01 CREATE TABLE IF NOT EXISTS `user` (
02 `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT ‘用户id’,
03 `username` char(30) NOT NULL COMMENT ‘用户名’,
04 `pwd` char(32) NOT NULL COMMENT ‘用户密码’,
05 PRIMARY KEY (`id`)
06 ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;
07
08 INSERT INTO `user` (`id`, `username`, `pwd`) VALUES
09 (1, ‘liangqi’, ‘123456′),
10 (2, ‘godsee’, ‘654321′),
11 (3, ‘webkit4′, ‘webkit4′),
12 (4, ‘yatou’, ‘hello_test’);

find($condition,$params) 找出匹配条件的一行数据,如果使用给定的查询条件在数据库中没有找到任何东西,null

调用 find 时,我们使用 $condition 和 $params 指定查询条件。此处 $condition 可以是 SQL 语句中的 WHERE 字符串,$params 则是一个参数数组,其中的值应绑定到 $condation 中的占位符。

1, 如果不加参数,会找出主键id=1的行数据

code:

1 $user = User::model()->find();
2 echo $user->id.’<br />’;
3 echo $user->username.’<br />’;
4 echo $user->pwd;

输出结果

1 1
2 liangqi
3 123456

2, 可以传入$condition条件,此参数,代表的是SQL语句中的WHERE条件

01 $user = User::model()->find(’id = 2′);
02 echo $user->id.’<br />’;
03 echo $user->username.’<br />’;
04 echo $user->pwd;
05 echo ‘################<br />’;
06
07 //查找id=3 ,username=”webkit4″
08 $user = User::model()->find(’id = 3 AND username=”webkit4″‘);
09 echo $user->id.’<br />’;
10 echo $user->username.’<br />’;
11 echo $user->pwd;

输出结果

1 2
2 godsee
3 654321
4 ################
5 3
6 webkit4
7 webkit4

不过直接为条件赋值,是不推荐的,最好的方式是使用预处理语句,绑定参数变量,作为占位符.使用的预处理的查询速度更快,并且可以防止SQL注入,所以官网文档推荐一下条件查找:

01 //查找id=1的行
02 $user = User::model()->find('id=:userId',array(’:userId’=>1));
03 echo $user->id.'<br />';
04 echo $user->username.'<br />';
05 echo $user->pwd;
06 echo '################<br />';
07
08 //查找id=3 ,username="webkit4"的行
09 $user = User::model()->find(
10                        'id=:userId AND username=:username',
11                        array(
12                             ':userId'=>3,
13                             ':username'=>’webkit4′
14                             )
15                        );
16 echo $user->id.'<br />';
17 echo $user->username.'<br />';
18 echo $user->pwd;

输出结果

1 1
2 liangqi
3 123456
4 ################
5 3
6 webkit4
7 webkit4