• HOME
  • DOCS
  • WTF
  • TECH
  • LIFE
  • PAGES
    • ARCHIVE
    • TAGS
    • ABOUT
    • FRIENDS
    • RSS
  • TOOLS
    • GEO
    • RANDOM()
    • GOO.GL
    • CSS HEART
Aj's Blog

记录时间溜走的瞬间和折腾过的那些事

PHP MVC开发日志(二)Apache Url Rewrite + PHP 实现自己的 Url Router
2012-08-02 @ TECH ApachephpUrl RewriteUrl Router3

一、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 的配置

<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)、最简单有效的一组重写规则

#开启重写引擎
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 的本质就是一个 钩子接口 满足规则的请求 都被传递给相应的钩子函数

|----------|
| 用户请求  | 用户请求 http://xxx/login
|----------|
   |
   |-> UrlRewrite把请求定向给index.php: /login -> index.php
   |
|----------|
| index.php|-> 分析: $_SERVER['REQUEST_URI'] -> "/login"
|----------|
   |
   |-->login模块存在 --> 加载模块 --> 执行并返回给用户

(2) PHP实现简单的url router

			//获取用户请求的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

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [L,QSA]

index.php

		$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

		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

下一篇:   PHP MVC开发日志(三)MS Access数据库转 Mysql
上一篇:   PHP MVC开发日志(一) 概述
  • 韩十七 says:
    August 8, 2016 at 17:07

    谢谢~您的.htaccess重写规则帮助了我。在此表示感谢^_^

    Reply
  • Garrison says:
    October 8, 2013 at 20:51

    有bug。test.php 文件不能和 index.php放在同目录。
    .htaccess 的规则
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    已经说明在index.php 下能找到 指定的文件或目录,重写功能将不会生效!

    所以test.php 不可以放在index.php同目录,相应地,index.php的 file_exists , require_once也要修改

    Reply
    • Aj says:
      October 29, 2013 at 06:27

      这个问题你可以自己define一个全局变量分别hold住app modle view的路径
      本文提到的只是一个简单的mvc demo

      Reply
  • Cancel reply