2013年1月10日星期四

【转】PHP MVC开发日志(二)Apache Url Rewrite + PHP 实现自己的 Url Router

一、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

摘自:http://www.6zou.net/tech/url-rewrite-php-url-router.html

没有评论: