合规国际互联网加速 OSASE为企业客户提供高速稳定SD-WAN国际加速解决方案。 广告
# 快速入门(二):URL和路由 本章讲解`URL`访问和路由的使用,主要包含: - - [URL访问](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/145250#url-) - [参数传入](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/145250#u53C2u6570u4F20u5165) - [隐藏index.php](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/145250#-index-php) - [定义路由](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/145250#u5B9Au4E49u8DEFu7531) - [完整匹配](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/145250#u5B8Cu6574u5339u914D) - [闭包定义](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/145250#u95EDu5305u5B9Au4E49) - [设置URL分隔符](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/145250#-url--1) - [路由参数](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/145250#u8DEFu7531u53C2u6570) - [变量规则](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/145250#u53D8u91CFu89C4u5219) - [路由分组](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/145250#u8DEFu7531u5206u7EC4) - [复杂路由](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/145250#u590Du6742u8DEFu7531) - [生成URL地址](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/145250#-url--2) - [总结](http://ihavenolimitations.xyz/thinkphp/thinkphp5_quickstart/145250#u603Bu7ED3) ## URL访问 `ThinkPHP`采用单一入口模式访问应用,对应用的所有请求都定向到应用的入口文件,系统会从`URL`参数中解析当前请求的模块、控制器和操作,下面是一个标准的`URL`访问格式: ``` <pre class="calibre18"> ``` <span class="hljs-string">http:</span> <span class="hljs-comment">//serverName/index.php/模块/控制器/操作</span> ``` ``` > #### 提示: > > - - - - - - > > 模块在ThinkPHP中的概念其实就是应用目录下面的子目录,而官方的规范是目录名小写,因此模块全部采用小写命名,无论URL是否开启大小写转换,模块名都会强制小写。 应用的`index`模块的`Index`控制器定义如下: ``` <pre class="calibre18"> ``` <span class="hljs-operator"><span class="hljs-number"><?php</span><span class="hljs-keyword">namespace</span> <span class="hljs-title">app</span>\<span class="hljs-title">index</span>\<span class="hljs-title">controller</span>; <span class="hljs-operator"><span class="hljs-keyword">class</span> <span class="hljs-title">Index</span></span>{ <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span><span class="hljs-number">()</span></span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">'index'</span>; } <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hello</span><span class="hljs-number">(<span class="hljs-regexp">$name</span> = <span class="hljs-string">'World'</span>)</span></span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">'Hello,'</span> . <span class="hljs-regexp">$name</span> . <span class="hljs-string">'!'</span>; } }</span> ``` ``` 如果我们直接访问入口文件的话,由于URL中没有模块、控制器和操作,因此系统会访问默认模块(index)下面的默认控制器(Index)的默认操作(index),因此下面的访问是等效的: ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span> ``` ``` 如果要访问控制器的hello方法,则需要使用完整的URL地址 ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/hello/name/thinkphp ``` ``` 访问URL地址后页面输出结果为: ``` <pre class="calibre18"> ``` Hello,thinkphp! ``` ``` 由于`name`参数为可选参数,因此也可以使用 ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/hello ``` ``` 访问URL地址后页面输出结果为: ``` <pre class="calibre18"> ``` Hello,World! ``` ``` 默认情况下,URL地址中的控制器和操作名是不区分大小写的,因此下面的访问其实是等效的: ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/Index/Index http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/INDEX/INDEX ``` ``` 如果你的控制器是驼峰的,例如定义一个HelloWorld控制器(`application/index/controller/HelloWorld.php`): ``` <pre class="calibre18"> ``` <span class="hljs-operator"><span class="hljs-number"><?php</span><span class="hljs-keyword">namespace</span> <span class="hljs-title">app</span>\<span class="hljs-title">index</span>\<span class="hljs-title">controller</span>; <span class="hljs-operator"><span class="hljs-keyword">class</span> <span class="hljs-title">HelloWorld</span></span>{ <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span><span class="hljs-number">(<span class="hljs-regexp">$name</span> = <span class="hljs-string">'World'</span>)</span></span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">'Hello,'</span> . <span class="hljs-regexp">$name</span> . <span class="hljs-string">'!'</span>; } }</span> ``` ``` 正确的URL访问地址(该地址可以使用url方法生成)应该是: ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/hello_world/<span class="hljs-keyword">index</span> ``` ``` 系统会自动定位到`HelloWorld`控制器类去操作。 如果使用 ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/HelloWorld/<span class="hljs-keyword">index</span> ``` ``` 将会报错,并提示`Helloworld`控制器类不存在。 如果希望严格区分大小写访问(或者要支持驼峰法进行控制器访问),可以在应用配置文件中设置: ``` <pre class="calibre18"> ``` <span class="hljs-comment">// 关闭URL自动转换(支持驼峰访问控制器)</span><span class="hljs-string">'url_convert'</span> => <span class="hljs-keyword">false</span>, ``` ``` 关闭URL自动转换之后,必须使用下面的URL地址访问(控制器名称必须严格使用控制器类的名称,不包含控制器后缀): ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/Index/<span class="hljs-keyword">index</span> http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/HelloWorld/<span class="hljs-keyword">index</span> ``` ``` > #### 提示: > > - - - - - - > > 操作方法的访问本身不会受URL自动转换的影响,但会影响默认的模板渲染输出。 如果你的服务器环境不支持`pathinfo`方式的URL访问,可以使用兼容方式,例如: ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php?<span class="hljs-keyword">s</span>=<span class="hljs-regexp">/index/</span>Index/<span class="hljs-keyword">index</span> ``` ``` 其中变量`s`的名称的可以配置的。 > 5.0不再支持普通的URL访问方式,所以下面的访问是无效的,你会发现无论输入什么,访问的都是默认的控制器和操作^\_^ ``` <pre class="calibre18"> ``` <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/index.php?m=index&c=Index&a=hello</span> ``` ``` ## 参数传入 通过操作方法的参数绑定功能,可以实现自动获取URL的参数,仍然以上面的控制器为例,控制器代码如下: ``` <pre class="calibre18"> ``` <span class="hljs-operator"><span class="hljs-number"><?php</span><span class="hljs-keyword">namespace</span> <span class="hljs-title">app</span>\<span class="hljs-title">index</span>\<span class="hljs-title">controller</span>; <span class="hljs-operator"><span class="hljs-keyword">class</span> <span class="hljs-title">Index</span></span>{ <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span><span class="hljs-number">()</span></span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">'index'</span>; } <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hello</span><span class="hljs-number">(<span class="hljs-regexp">$name</span> = <span class="hljs-string">'World'</span>)</span></span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">'Hello,'</span> . <span class="hljs-regexp">$name</span> . <span class="hljs-string">'!'</span>; } }</span> ``` ``` 当我们访问 ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/hello ``` ``` 就是访问`app\index\controller\Index`控制器类的`hello`方法,因为没有传入任何参数,`name`参数就使用默认值`World`。如果传入name参数,则使用: ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/hello/name/thinkphp ``` ``` 页面输出结果为: ``` <pre class="calibre18"> ``` Hello,thinkphp! ``` ``` 现在给hello方法增加第二个参数: ``` <pre class="calibre18"> ``` <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hello</span><span class="hljs-number">(<span class="hljs-regexp">$name</span> = <span class="hljs-string">'World'</span>, <span class="hljs-regexp">$city</span> = <span class="hljs-string">''</span>)</span></span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">'Hello,'</span> . <span class="hljs-regexp">$name</span> . <span class="hljs-string">'! You come from '</span> . <span class="hljs-regexp">$city</span> . <span class="hljs-string">'.'</span>; } ``` ``` 访问地址为 ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/hello/name/thinkphp/city/shanghai ``` ``` 页面输出结果为: ``` <pre class="calibre18"> ``` Hello,thinkphp! You come <span class="hljs-keyword">from</span> shanghai. ``` ``` 可以看到,`hello`方法会自动获取URL地址中的同名参数值作为方法的参数值,而且这个参数的传入顺序不受URL参数顺序的影响,例如下面的URL地址输出的结果和上面是一样的: ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/hello/city/shanghai/name/thinkphp ``` ``` 或者使用 ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/hello?city=shanghai&name=thinkphp ``` ``` 还可以进一步对URL地址做简化,前提就是我们必须明确参数的顺序代表的变量,我们更改下URL参数的获取方式,把应用配置文件中的`url_param_type`参数的值修改如下: ``` <pre class="calibre18"> ``` <span class="hljs-comment">// 按照参数顺序获取</span><span class="hljs-string">'url_param_type'</span> => <span class="hljs-number">1</span>, ``` ``` 现在,URL的参数传值方式就变成了严格按照操作方法的变量定义顺序来传值了,也就是说我们必须使用下面的URL地址访问才能正确传入`name`和`city`参数到`hello`方法: ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/hello/thinkphp/shanghai ``` ``` 页面输出结果为: ``` <pre class="calibre18"> ``` Hello,thinkphp! You come <span class="hljs-keyword">from</span> shanghai. ``` ``` 如果改变参数顺序为 ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/hello/shanghai/thinkphp ``` ``` 页面输出结果为: ``` <pre class="calibre18"> ``` Hello,shanghai! You come <span class="hljs-keyword">from</span> thinkphp. ``` ``` 显然不是我们预期的结果。 同样,我们试图通过 ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/hello/name/thinkphp/city/shanghai ``` ``` 访问也不会得到正确的结果。 但下面的方式仍然可以得到正确的结果: ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>.php/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/hello?name=thinkphp&city=shanghai ``` ``` > #### 注意 > > - - - - - - > > 按顺序绑定参数的话,操作方法的参数只能使用URL pathinfo变量,而不能使用get或者post变量。 ## 隐藏index.php 可以去掉URL地址里面的入口文件`index.php`,但是需要额外配置WEB服务器的重写规则。 以`Apache`为例,需要在入口文件的同级添加`.htaccess`文件(官方默认自带了该文件),内容如下: ``` <pre class="calibre18"> ``` <span class="hljs-regexp"><IfModule mod_rewrite.c></span><span class="hljs-keyword"><span class="hljs-operator">Options</span></span> +FollowSymlinks -Multiviews <span class="hljs-keyword"><span class="hljs-operator">RewriteEngine</span></span> <span class="hljs-number">on</span><span class="hljs-keyword"><span class="hljs-operator">RewriteCond</span></span> <span class="hljs-operator">%{REQUEST_FILENAME}</span> !-d <span class="hljs-keyword"><span class="hljs-operator">RewriteCond</span></span> <span class="hljs-operator">%{REQUEST_FILENAME}</span> !-f <span class="hljs-keyword"><span class="hljs-operator">RewriteRule</span></span> ^(.*)$ index.php/<span class="hljs-number">$1</span><span class="hljs-operator"> [QSA,PT,L]</span><span class="hljs-regexp"></IfModule></span> ``` ``` 如果用的`phpstudy`,规则如下: ``` <pre class="calibre18"> ``` <span class="hljs-regexp"><IfModule mod_rewrite.c></span><span class="hljs-keyword"><span class="hljs-operator">Options</span></span> +FollowSymlinks -Multiviews <span class="hljs-keyword"><span class="hljs-operator">RewriteEngine</span></span> <span class="hljs-number">on</span><span class="hljs-keyword"><span class="hljs-operator">RewriteCond</span></span> <span class="hljs-operator">%{REQUEST_FILENAME}</span> !-d <span class="hljs-keyword"><span class="hljs-operator">RewriteCond</span></span> <span class="hljs-operator">%{REQUEST_FILENAME}</span> !-f <span class="hljs-keyword"><span class="hljs-operator">RewriteRule</span></span> ^(.*)$ index.php<span class="hljs-operator"> [L,E=PATH_INFO:$1] </IfModule></span> ``` ``` 接下来就可以使用下面的URL地址访问了 ``` <pre class="calibre18"> ``` http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span> http:<span class="hljs-regexp">//tp</span>5.com/<span class="hljs-keyword">index</span>/<span class="hljs-keyword">index</span>/hello ``` ``` 如果你使用的`apache`版本使用上面的方式无法正常隐藏`index.php`,可以尝试使用下面的方式配置`.htaccess`文件: ``` <pre class="calibre18"> ``` <span class="hljs-regexp"><IfModule mod_rewrite.c></span><span class="hljs-keyword"><span class="hljs-operator">Options</span></span> +FollowSymlinks -Multiviews <span class="hljs-keyword"><span class="hljs-operator">RewriteEngine</span></span> <span class="hljs-number">on</span><span class="hljs-keyword"><span class="hljs-operator">RewriteCond</span></span> <span class="hljs-operator">%{REQUEST_FILENAME}</span> !-d <span class="hljs-keyword"><span class="hljs-operator">RewriteCond</span></span> <span class="hljs-operator">%{REQUEST_FILENAME}</span> !-f <span class="hljs-keyword"><span class="hljs-operator">RewriteRule</span></span> ^(.*)$ index.php?/<span class="hljs-number">$1</span><span class="hljs-operator"> [QSA,PT,L]</span><span class="hljs-regexp"></IfModule></span> ``` ``` 如果是`Nginx`环境的话,可以在`Nginx.conf`中添加: ``` <pre class="calibre18"> ``` location / { // …..省略部分代码 <span class="hljs-keyword">if</span> (!<span class="hljs-operator">-e</span> <span class="hljs-regexp">$request_filename</span>) { rewrite ^(.*)$ /index.php?s=/<span class="hljs-regexp">$1</span> last; <span class="hljs-number">break</span>; } } ``` ``` > #### 提示: > > - - - - - - > > 后面的示例访问地址,我们都假设配置了隐藏`index.php`。 ## 定义路由 URL地址里面的`index`模块怎么才能省略呢,默认的URL地址显得有点长,下面就来说说如何通过路由简化URL访问。 我们在路由定义文件(`application/route.php`)里面添加一些路由规则,如下: ``` <pre class="calibre18"> ``` <span class="hljs-keyword">return</span> [ <span class="hljs-comment">// 添加路由规则 路由到 index控制器的hello操作方法</span><span class="hljs-string">'hello/:name'</span> => <span class="hljs-string">'index/index/hello'</span>, ]; ``` ``` 该路由规则表示所有`hello`开头的并且带参数的访问都会路由到`index`控制器的`hello`操作方法。 路由之前的URL访问地址为: ``` <pre class="calibre18"> ``` <span class="hljs-string">http:</span>/<span class="hljs-regexp">/tp5.com/index</span><span class="hljs-regexp">/index/hello</span><span class="hljs-regexp">/name/thinkphp</span> ``` ``` 定义路由后就只能访问下面的URL地址 ``` <pre class="calibre18"> ``` <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/hello/thinkphp</span> ``` ``` > #### 注意 > > - - - - - - > > 定义路由规则后,原来的URL地址将会失效,变成非法请求。 ![](https://img.kancloud.cn/09/79/097906eadffa5e3cee8ba09c28759ede_721x309.png) 但这里有一个小问题,如果我们只是访问 ``` <pre class="calibre18"> ``` <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/hello</span> ``` ``` 将发生错误, ![](https://img.kancloud.cn/6e/e0/6ee0cf0e051014518f53c53433048c20_874x540.png) 事实上这是由于路由没有正确匹配到,我们修改路由规则如下: ``` <pre class="calibre18"> ``` <span class="hljs-keyword">return</span> [ <span class="hljs-comment">// 路由参数name为可选</span><span class="hljs-string">'hello/[:name]'</span> => <span class="hljs-string">'index/hello'</span>, ]; ``` ``` 使用`[]`把路由规则中的变量包起来,就表示该变量为可选,接下来就可以正常访问了。 ``` <pre class="calibre18"> ``` <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/hello</span> ``` ``` 当`name`参数没有传入值的时候,`hello`方法的`name`参数有默认值`World`,所以输出的内容为 `Hello,World!` 除了路由配置文件中定义之外,还可以采用动态定义路由规则的方式定义,例如在路由配置文件(`application/route.php`)的开头直接添加下面的方法: ``` <pre class="calibre18"> ``` <span class="hljs-regexp">use</span> <span class="hljs-regexp">think</span>\<span class="hljs-regexp">Route</span>; <span class="hljs-operator"><span class="hljs-title1">Route</span>:<span class="hljs-string">:<span class="hljs-function">rule</span>(<span class="hljs-operator">'hello/:name'</span>, <span class="hljs-operator">'index/hello'</span>)</span></span>; ``` ``` 完成的效果和使用配置方式定义是一样的。 无论是配置方式还是通过Route类的方法定义路由,都统一放到路由配置文件`application/route.php`文件中,具体原因后面会揭晓。 > #### 提示: > > - - - - - - > > 注意路由配置不支持在模块配置文件中设置。 ## 完整匹配 前面定义的路由是只要以hello开头就能进行匹配,如果需要完整匹配,可以使用下面的定义: ``` <pre class="calibre18"> ``` <span class="hljs-keyword">return</span> [ <span class="hljs-comment">// 路由参数name为可选</span><span class="hljs-string">'hello/[:name]$'</span> => <span class="hljs-string">'index/hello'</span>, ]; ``` ``` 当路由规则以`$`结尾的时候就表示当前路由规则需要完整匹配。 当我们访问下面的URL地址的时候: ``` <pre class="calibre18"> ``` <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/hello // 正确匹配</span> <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/hello/thinkphp // 正确匹配</span> <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/hello/thinkphp/val/value // 不会匹配</span> ``` ``` ## 闭包定义 还支持通过定义闭包为某些特殊的场景定义路由规则,例如: ``` <pre class="calibre18"> ``` <span class="hljs-keyword">return</span> [ <span class="hljs-comment">// 定义闭包</span><span class="hljs-string">'hello/[:name]'</span> => <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-number">(<span class="hljs-regexp">$name</span>)</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">'Hello,'</span> . <span class="hljs-regexp">$name</span> . <span class="hljs-string">'!'</span>; }, ]; ``` ``` 或者 ``` <pre class="calibre18"> ``` <span class="hljs-keyword">use</span> <span class="hljs-title">think</span>\<span class="hljs-title">Route</span>; Route::rule(<span class="hljs-string">'hello/:name'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-number">(<span class="hljs-regexp">$name</span>)</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">'Hello,'</span> . <span class="hljs-regexp">$name</span> . <span class="hljs-string">'!'</span>; }); ``` ``` > #### 提示: > > - - - - - - > > 闭包函数的参数就是路由规则中定义的变量。 因此,当访问下面的URL地址: ``` <pre class="calibre18"> ``` <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/hello/thinkphp</span> ``` ``` 会输出 ``` <pre class="calibre18"> ``` Hello,thinkphp! ``` ``` ## 设置URL分隔符 如果需要改变URL地址中的`pathinfo`参数分隔符,只需要在应用配置文件(`application/config.php`)中设置: ``` <pre class="calibre18"> ``` <span class="hljs-comment">// 设置pathinfo分隔符</span><span class="hljs-string">'pathinfo_depr'</span> => <span class="hljs-string">'-'</span>, ``` ``` 路由规则定义无需做任何改变,我们就可以访问下面的地址: ``` <pre class="calibre18"> ``` <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/hello-thinkphp</span> ``` ``` ## 路由参数 我们还可以约束路由规则的请求类型或者URL后缀之类的条件,例如: ``` <pre class="calibre18"> ``` <span class="hljs-keyword">return</span> [ <span class="hljs-comment">// 定义路由的请求类型和后缀</span><span class="hljs-string">'hello/[:name]'</span> => [<span class="hljs-string">'index/hello'</span>, [<span class="hljs-string">'method'</span> => <span class="hljs-string">'get'</span>, <span class="hljs-string">'ext'</span> => <span class="hljs-string">'html'</span>]], ]; ``` ``` 上面定义的路由规则限制了必须是`get`请求,而且后缀必须是`html`的,所以下面的访问地址: ``` <pre class="calibre18"> ``` <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/hello // 无效</span> <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/hello.html // 有效</span> <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/hello/thinkphp // 无效</span> <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/hello/thinkphp.html // 有效</span> ``` ``` > 更多的路由参数请参考完全开发手册的路由参数一节。 ## 变量规则 接下来,我们来尝试一些复杂的路由规则定义满足不同的路由变量。在此之前,首先增加一个控制器类如下: ``` <pre class="calibre18"> ``` <span class="hljs-operator"><span class="hljs-number"><?php</span><span class="hljs-keyword">namespace</span> <span class="hljs-title">app</span>\<span class="hljs-title">index</span>\<span class="hljs-title">controller</span>; <span class="hljs-operator"><span class="hljs-keyword">class</span> <span class="hljs-title">Blog</span></span>{ <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">get</span><span class="hljs-number">(<span class="hljs-regexp">$id</span>)</span></span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">'查看id='</span> . <span class="hljs-regexp">$id</span> . <span class="hljs-string">'的内容'</span>; } <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">read</span><span class="hljs-number">(<span class="hljs-regexp">$name</span>)</span></span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">'查看name='</span> . <span class="hljs-regexp">$name</span> . <span class="hljs-string">'的内容'</span>; } <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">archive</span><span class="hljs-number">(<span class="hljs-regexp">$year</span>, <span class="hljs-regexp">$month</span>)</span></span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">'查看'</span> . <span class="hljs-regexp">$year</span> . <span class="hljs-string">'/'</span> . <span class="hljs-regexp">$month</span> . <span class="hljs-string">'的归档内容'</span>; } }</span> ``` ``` 添加如下路由规则: ``` <pre class="calibre18"> ``` <span class="hljs-keyword">return</span> [ <span class="hljs-string">'blog/:year/:month'</span> => [<span class="hljs-string">'blog/archive'</span>, [<span class="hljs-string">'method'</span> => <span class="hljs-string">'get'</span>], [<span class="hljs-string">'year'</span> => <span class="hljs-string">'\d{4}'</span>, <span class="hljs-string">'month'</span> => <span class="hljs-string">'\d{2}'</span>]], <span class="hljs-string">'blog/:id'</span> => [<span class="hljs-string">'blog/get'</span>, [<span class="hljs-string">'method'</span> => <span class="hljs-string">'get'</span>], [<span class="hljs-string">'id'</span> => <span class="hljs-string">'\d+'</span>]], <span class="hljs-string">'blog/:name'</span> => [<span class="hljs-string">'blog/read'</span>, [<span class="hljs-string">'method'</span> => <span class="hljs-string">'get'</span>], [<span class="hljs-string">'name'</span> => <span class="hljs-string">'\w+'</span>]], ]; ``` ``` 在上面的路由规则中,我们对变量进行的规则约束,变量规则使用正则表达式进行定义。 我们看下几种URL访问的情况 ``` <pre class="calibre18"> ``` <span class="hljs-comment">// 访问id为5的内容</span> <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/blog/5 </span> <span class="hljs-comment">// 访问name为thinkphp的内容</span> <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/blog/thinkphp </span> <span class="hljs-comment">// 访问2015年5月的归档内容</span> <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/blog/2015/05 </span> ``` ``` ## 路由分组 上面的三个路由规则由于都是`blog`打头,所以我们可以做如下的简化: ``` <pre class="calibre18"> ``` <span class="hljs-keyword">return</span> [ <span class="hljs-string">'[blog]'</span> => [ <span class="hljs-string">':year/:month'</span> => [<span class="hljs-string">'blog/archive'</span>, [<span class="hljs-string">'method'</span> => <span class="hljs-string">'get'</span>], [<span class="hljs-string">'year'</span> => <span class="hljs-string">'\d{4}'</span>, <span class="hljs-string">'month'</span> => <span class="hljs-string">'\d{2}'</span>]], <span class="hljs-string">':id'</span> => [<span class="hljs-string">'blog/get'</span>, [<span class="hljs-string">'method'</span> => <span class="hljs-string">'get'</span>], [<span class="hljs-string">'id'</span> => <span class="hljs-string">'\d+'</span>]], <span class="hljs-string">':name'</span> => [<span class="hljs-string">'blog/read'</span>, [<span class="hljs-string">'method'</span> => <span class="hljs-string">'get'</span>], [<span class="hljs-string">'name'</span> => <span class="hljs-string">'\w+'</span>]], ], ]; ``` ``` 对于这种定义方式,我们称之为路由分组,路由分组一定程度上可以提高路由检测的效率。 ## 复杂路由 有时候,我们还需要对URL做一些特殊的定制,例如如果要同时支持下面的访问地址 ``` <pre class="calibre18"> ``` <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/blog/thinkphp </span> <span class="hljs-string">http:</span> <span class="hljs-comment">//tp5.com/blog-2015-05 </span> ``` ``` 我们只要稍微改变路由定义规则即可: ``` <pre class="calibre18"> ``` <span class="hljs-keyword">return</span> [ <span class="hljs-string">'blog/:id'</span> => [<span class="hljs-string">'blog/get'</span>, [<span class="hljs-string">'method'</span> => <span class="hljs-string">'get'</span>], [<span class="hljs-string">'id'</span> => <span class="hljs-string">'\d+'</span>]], <span class="hljs-string">'blog/:name'</span> => [<span class="hljs-string">'blog/read'</span>, [<span class="hljs-string">'method'</span> => <span class="hljs-string">'get'</span>], [<span class="hljs-string">'name'</span> => <span class="hljs-string">'\w+'</span>]], <span class="hljs-string">'blog-<year>-<month>'</span> => [<span class="hljs-string">'blog/archive'</span>, [<span class="hljs-string">'method'</span> => <span class="hljs-string">'get'</span>], [<span class="hljs-string">'year'</span> => <span class="hljs-string">'\d{4}'</span>, <span class="hljs-string">'month'</span> => <span class="hljs-string">'\d{2}'</span>]], ]; ``` ``` 对 `blog-<year>-<month>` 这样的非正常规范,我们需要使用`<变量名>`这样的变量定义方式,而不是 `:变量名`方式。 简单起见,我们还可以把变量规则统一定义,例如: ``` <pre class="calibre18"> ``` <span class="hljs-keyword">return</span> [ <span class="hljs-comment">// 全局变量规则定义</span><span class="hljs-string">'__pattern__'</span> => [ <span class="hljs-string">'name'</span> => <span class="hljs-string">'\w+'</span>, <span class="hljs-string">'id'</span> => <span class="hljs-string">'\d+'</span>, <span class="hljs-string">'year'</span> => <span class="hljs-string">'\d{4}'</span>, <span class="hljs-string">'month'</span> => <span class="hljs-string">'\d{2}'</span>, ], <span class="hljs-comment">// 路由规则定义</span><span class="hljs-string">'blog/:id'</span> => <span class="hljs-string">'blog/get'</span>, <span class="hljs-string">'blog/:name'</span> => <span class="hljs-string">'blog/read'</span>, <span class="hljs-string">'blog-<year>-<month>'</span> => <span class="hljs-string">'blog/archive'</span>, ]; ``` ``` 在`__pattern__`中定义的变量规则我们称之为全局变量规则,在路由规则里面定义的变量规则我们称之为局部变量规则,如果一个变量同时定义了全局规则和局部规则的话,当前的局部规则会覆盖全局规则的,例如: ``` <pre class="calibre18"> ``` <span class="hljs-keyword">return</span> [ <span class="hljs-comment">// 全局变量规则</span><span class="hljs-string">'__pattern__'</span> => [ <span class="hljs-string">'name'</span> => <span class="hljs-string">'\w+'</span>, <span class="hljs-string">'id'</span> => <span class="hljs-string">'\d+'</span>, <span class="hljs-string">'year'</span> => <span class="hljs-string">'\d{4}'</span>, <span class="hljs-string">'month'</span> => <span class="hljs-string">'\d{2}'</span>, ], <span class="hljs-string">'blog/:id'</span> => <span class="hljs-string">'blog/get'</span>, <span class="hljs-comment">// 定义了局部变量规则</span><span class="hljs-string">'blog/:name'</span> => [<span class="hljs-string">'blog/read'</span>, [<span class="hljs-string">'method'</span> => <span class="hljs-string">'get'</span>], [<span class="hljs-string">'name'</span> => <span class="hljs-string">'\w{5,}'</span>]], <span class="hljs-string">'blog-<year>-<month>'</span> => <span class="hljs-string">'blog/archive'</span>, ]; ``` ``` ## 生成URL地址 定义路由规则之后,我们可以通过Url类来方便的生成实际的URL地址(路由地址),针对上面的路由规则,我们可以用下面的方式生成URL地址。 ``` <pre class="calibre18"> ``` <span class="hljs-comment">// 输出 blog/thinkphp</span> Url::build(<span class="hljs-string">'blog/read'</span>, <span class="hljs-string">'name=thinkphp'</span>); Url::build(<span class="hljs-string">'blog/read'</span>, [<span class="hljs-string">'name'</span> => <span class="hljs-string">'thinkphp'</span>]); <span class="hljs-comment">// 输出 blog/5</span> Url::build(<span class="hljs-string">'blog/get'</span>, <span class="hljs-string">'id=5'</span>); Url::build(<span class="hljs-string">'blog/get'</span>, [<span class="hljs-string">'id'</span> => <span class="hljs-number">5</span>]); <span class="hljs-comment">// 输出 blog/2015/05</span> Url::build(<span class="hljs-string">'blog/archive'</span>, <span class="hljs-string">'year=2015&month=05'</span>); Url::build(<span class="hljs-string">'blog/archive'</span>, [<span class="hljs-string">'year'</span> => <span class="hljs-string">'2015'</span>, <span class="hljs-string">'month'</span> => <span class="hljs-string">'05'</span>]); ``` ``` > #### 提示: > > - - - - - - > > build方法的第一个参数使用路由定义中的完整路由地址。 我们还可以使用系统提供的助手函数url来简化 ``` <pre class="calibre18"> ``` url(<span class="hljs-string">'blog/read'</span>, <span class="hljs-string">'name=thinkphp'</span>); <span class="hljs-comment">// 等效于</span><span class="hljs-string">Url:</span>:build(<span class="hljs-string">'blog/read'</span>, <span class="hljs-string">'name=thinkphp'</span>); ``` ``` > 如果我们的路由规则发生调整,生成的URL地址会自动变化。 如果你配置了`url_html_suffix`参数的话,生成的URL地址会带上后缀,例如: ``` <pre class="calibre18"> ``` <span class="hljs-string">'url_html_suffix'</span> => <span class="hljs-string">'html'</span>, ``` ``` 那么生成的URL地址 类似 ``` <pre class="calibre18"> ``` blog/thinkphp.html blog/<span class="hljs-number">2015</span>/<span class="hljs-number">05.</span>html ``` ``` 如果你的URL地址全部采用路由方式定义,也可以直接使用路由规则来定义URL生成,例如: ``` <pre class="calibre18"> ``` url(<span class="hljs-string">'/blog/thinkphp'</span>); <span class="hljs-string">Url:</span>:build(<span class="hljs-string">'/blog/8'</span>); <span class="hljs-string">Url:</span>:build(<span class="hljs-string">'/blog/archive/2015/05'</span>); ``` ``` 生成方法的第一个参数一定要和路由定义的路由地址保持一致,如果你的路由地址比较特殊,例如使用闭包定义的话,则需要手动给路由指定标识,例如: ``` <pre class="calibre18"> ``` <span class="hljs-comment">// 添加hello路由标识</span> Route::rule([<span class="hljs-string">'hello'</span>,<span class="hljs-string">'hello/:name'</span>], <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-number">(<span class="hljs-regexp">$name</span>)</span></span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">'Hello,'</span>.<span class="hljs-regexp">$name</span>; }); <span class="hljs-comment">// 根据路由标识快速生成URL</span> Url::build(<span class="hljs-string">'hello'</span>, <span class="hljs-string">'name=thinkphp'</span>); <span class="hljs-comment">// 或者使用</span> Url::build(<span class="hljs-string">'hello'</span>, [<span class="hljs-string">'name'</span> => <span class="hljs-string">'thinkphp'</span>]); ``` ``` 目前为止,我们掌握的路由功能还只是ThinkPHP`5.0`路由功能的冰山一角,以后我们还会通过更多的专题来讲解路由。 ## 总结 看完本篇内容后,你应该了解如下的知识: - URL访问规范 - 路由定义 - URL地址生成