一、Why Url Rewrite?
简单说2个肤浅的原因: 1、好看,2、有利于SEO
[1]. http://6zou.net/index.php?articleid=1024&mode=view&cate=tech&name=article_title
[2]. http://6zou.net/tech/title.html
这2个url用户更喜欢哪个我想不用我多说,幸运的是url的喜好跟用户一样
最终的第三点: 基于Url Rewrite 来实现 Url Router 进一步优化和简化MVC流程控制
我正是因为这一点需要
二、Apache Url Rewrite Rules
官方文档: http://lamp.linux.gov.cn/Apache/ApacheMenu/mod/mod_rewrite.html
下面说下我的理解
(1)、.htaccess实现url rewrite
首先我们来看一个 apache vhost 的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 | <VirtualHost *:80> ServerName abc.6zou.net ServerAdmin admin@6zou.net DocumentRoot "D:/www/abc.6zou.net/" <Directory "D:/www/abc.6zou.net/"> Options -Indexes FollowSymLinks AllowOverride All Order allow,deny Allow from all </Directory> ErrorLog "D:/www/abc.6zou.net/error.log" CustomLog "D:/www/abc.6zou.net/access.log" combined </VirtualHost> |
通常我们利用 Apache 的 mod_rewrite 进行 url rewrite 时,rewrite rules 写在 .htaccess 文件里
AllowOverride 的值就控制着是否加载对应的 .htaccess 文件内的配置
AllowOverride 指令仅在段有效的
AllowOverride = None 时, 该目录下的 .htaccess 文件中所有配置都被忽略。
AllowOverride = All 时, 该目录下的 .htaccess 文件中所有配置都将生效。
现在不管是虚拟主机提供商 还是 有多个网站的站长 最流行的使用方法是
在配置apache的 vhost 文件时默认 AllowOverride = All,然后在各个网站的根目录中根据各自需求配置各自的 .htaccess
(2)、最简单有效的一组重写规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #开启重写引擎 RewriteEngine on #如果请求的 -f (文件) ! (不存在) 则跳过本条规则 #否则就返回该文件 RewriteCond %{REQUEST_FILENAME} !-f #如果请求的 -d (目录) ! (不存在) 则跳过本条规则 #否则就返回该目录 RewriteCond %{REQUEST_FILENAME} !-d #[L] 声明这是最后一条匹配规则 #[QSA] url rewrite时原uri所在的query string也被传递过去 #例: 1.php?a=0&b=1&c=1 重写为index.php 那么在index.php 里面 $_GET['a'] 是可以取到值的 #^(.*)$ => 所有请求 # 例: 1.php?a=0 重写为 index.php 并携带query string RewriteRule ^(.*)$ index.php [L,QSA] |
三、Url Router的实现
(1)原理
url rewrite 的本质就是一个 钩子接口 满足规则的请求 都被传递给相应的钩子函数
1 2 3 4 5 6 7 8 9 10 11 | |----------| | 用户请求 | 用户请求 http://xxx/login |----------| | |-> UrlRewrite把请求定向给index.php: /login -> index.php | |----------| | index.php|-> 分析: $_SERVER['REQUEST_URI'] -> "/login" |----------| | |-->login模块存在 --> 加载模块 --> 执行并返回给用户 |
(2) PHP实现简单的url router
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | //获取用户请求的uri 并按 / 切割 $uri = explode ( '/' , $_SERVER [ 'REQUEST_URI' ] ); //uri都是 / 开头的 所以 $uri[0] 为空,直接抛弃 array_shift ( $uri ); //把uri的第一段定义为 classname $class = strtolower ( array_shift ( $uri ) ); //把uri的第二段定义为 class的mothod $method = strtolower ( array_shift ( $uri ) ); //把uri的第三段定义为 class的mothod $var = strtolower ( array_shift ( $uri ) ); //当然你还可以接着取第N段,但应做基本的安全过滤 //.... /* * 这里是我自己的规则,我个人认为最简单有效 * 所有类、方法、文件名全小写 * class 对应的文件为 class.php * class::method 对应 class.php 里面的 public function method(){} * 此段代码只适合PHP 5.3+版本 */ //如果这个class对应的文件存在则开始加载,否则返回404错误 if ( file_exists ( $class . '.php' ) ) { require_once $class . '.php' ; //模块的名字既类的名字, 你可以定义为其他的,我这里是为了简化 if ( class_exists ( $class ) ) { //如果class存在则初始化 $clsHandler = new $class (); //如果class的方法是否存在则调用,否则返回404错误 if ( method_exists( $class , $method ) ) { //调用该方法 $clsHandler :: $method (); } else if ( $method != '' ) { header( 'HTTP/1.0 404 Not Found' ); exit ; } } } else { header( 'HTTP/1.0 404 Not Found' ); exit ; } |
(3) 实例
.htaccess
1 2 3 4 | RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [L,QSA] |
index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | $uri = explode ( '/' , $_SERVER [ 'REQUEST_URI' ] ); array_shift ( $uri ); $class = strtolower ( array_shift ( $uri ) ); $method = strtolower ( array_shift ( $uri ) ); $var = strtolower ( array_shift ( $uri ) ); if ( file_exists ( $class . '.php' ) ) { require_once $class . '.php' ; if ( class_exists ( $class ) ) { $clsHandler = new $class (); if ( method_exists( $class , $method ) ) { $clsHandler :: $method ( $var ); } else if ( $method != '' ) { exit ( "<h1>$class.php: $class::$method Not Found</h1>" ); } } else { exit ( "<h1>$class.php: $class Not Found</h1>" ); } } else { switch ( $class ) { case '' : echo "<h1>this is default page!</h1>" ; break ; default : exit ( "<h1>$class.php Not Found</h1>" ); } } |
test.php
1 2 3 4 5 6 7 8 9 10 11 12 | class test { public function test() { echo "<h1>class inited</h1>" ; } public function hello( $name = 'tester' ) { echo strlen ( $name )>0 ? "<h1>hello, $name !</h1>" : "<h1>hello, world !</h1>" ; } } |
测试url:
http://xxx/ 提示 默认页
http://xxx/asdasd 提示 asdasd.php 文件不存在
http://xxx/test 提示 test类 初始化成功
http://xxx/test/hello 调用test类 hello方法 传递参数为空 显示 hello,world
http://xxx/test/hello/jason 调用test类 hello方法 传递参数为jason 显示 hello,jason
没有评论:
发表评论