曾经和一朋友聊框架的时候,总结道:
框架其实就是MVC结构、URL路由、数据库驱动、模板引擎的结合。
想来想去也确实没什么东西。由此可见URL在框架里的地位,但是说它重要,它又不是那么重要,为什么呢?
第一,它没有多少,第二,它的作用也就是美化URL。仅此而已。
TP里提供了四种URL模式,在之前接触的框架定义文件defines.php里有它们的说明:
1 | //支持的URL模式 |
- 普通模式
1 | http://localhost/appName/index.php?m=moduleName&a=actionName&id=1 |
TP里默认m是model,a是action,g是group,在框架配置文件convention.php里定义:
1 | 'VAR_GROUP' => 'g', // 默认分组获取变量 |
在这里可以修改成自己喜欢的
- PATHINFO模式(TP默认)
1 | http://localhost/appName/index.php/moduleName/actionName/id/1/ |
- REWRITE模式
1 | http://localhost/appName/moduleName/actionName/id/1/ |
与pathinfo模式相比隐藏了入口文件index.php,用web服务器(apache、nginx等)来配置。
+ 兼容模式
1 | http://localhost/appName/?s=/module/action/id/1/ |
服务器不能很好地支持PATHINFO模式而又不想用普通模式可以试试这个。
用现在流行的话来说:
用普通模式的是普通青年,用PATHINFO和REWRITE的是文艺青年,用兼容模式的是2B青年。呵呵。开个玩笑。
接下来看看TP是如何实现来着的吧。
Dispatcher一共有四个方法:
dispatch URL路由处理
getPathInfo 获取URL的pathInfo
parsePathInfo 解析pathInfo
routerCheck 检查路由是否合法
要想对URL进行处理,首先要获取它并解析:
getPathInfo:
1 | if(!empty($_GET[C('VAR_PATHINFO')])) { |
这一段多分支结构,只是为了获取path信息:
检查$_GET里是否包含VAR_PATHINFO(兼容模式的获取变量,默认是s),保存到$path变量里,释放$_GET[s],否则进入条件2;(这一条是为兼容模式准备的)
检查$_SERVER[‘PATH_INFO’],从里面去掉$_SERVER[‘SCRIPT_NAME’]。
$_SERVER[‘PATH_INFO’]获取的是.php后缀后面的内容,$_SERVER[‘SCRIPT_NAME’]获取的是域名之后的第一个斜杠(/)到.php(包含.php)之间的内容。
(这一条是针对PATHINFO和REWRITE模式的)
否则进入下一个条件分支;
- 判断$_SERVER[‘ORIG_PATH_INFO’]
网上说$_SERVER[‘ORIG_PATH_INFO’]相当于$_SERVER[‘PATH_INFO’]的原始信息,$_SERVER[‘PATH_INFO’]在localhost下显示,而$_SERVER[‘ORIG_PATH_INFO’]在服务器上会显示,这个到目前还没有得到证实。
$_SERVER[‘REDIRECT_PATH_INFO’]是用header()进行跳转后的PATH_INFO。
$_SERVER[“REDIRECT_Url”]
1 | if(C('URL_HTML_SUFFIX') && !empty($path)) { |
URL_HTML_SUFFIX是URL伪静态的设置,在获取到path后,会在后面加上设置的后缀,默认是空。
最后把path写到$_SERVER[‘PATH_INFO’]中。
parsePathInfo
1 | $pathInfo = array(); |
先定义一个数组用来存解析后的URL信息。
1 |
|
刚才处理的$_SERVER[‘PATH_INFO’]在这里开始发光发热。把它左右的斜杠(/)给删除掉后,进行分隔(按URL_PATHINFO_DEPR设置的URL路径分隔符),存入$path; 读取项目的分组列表:$groupApp = C(‘APP_GROUP_LIST’); 如果$path[0]在项目的分组列表里,就把它写到$pathInfo里:
1 | $pathInfo[C('VAR_GROUP')] = in_array(strtolower($paths[0]),$arr)? array_shift($paths) : ''; |
上面已经列举了,框架默认的VAR_GROUP是g,翻译过来就是:
1 | $pathInfo[g] = in_array(strtolower($paths[0]),$arr)? array_shift($paths) : ''; |
接着读取模块和动作:
1 | $pathInfo[C('VAR_MODULE')] = array_shift($paths); |
接下来把剩余的遍历以写入$pathInfo:如果剩下的都是成对的,就以key(i)=>value(i+1)的形式写入。这里有一个特殊情况就是$path里只剩一个值了(i=0有值,i=1没有),那么就把这个值赋给VAR_ACTION对应的value。如:
1 | http://domain.com/index.php/GroupName/ModuleName/ActionName/123 |
解析后的:
1 | $pathInfo['g'] = GroupName; |
这个有什么用?
在dispath方法里调用它把$pathInfo的信息合并到$_GET中:
1 | $_GET = array_merge(self :: parsePathInfo(),$_GET); |
然后在App的getGroup、getModule和getAction中调用,看一下getModule里的内容:
1 | $var = C('VAR_MODULE'); |
从$_GET和$_POST里判断是否有值,没有的话取默认值。然后在App::exec中实例化。(这就是框架里通过URL来执行控制器里方法的秘密)。
回到Dispatcher,该看看dispatch方法了:
dispatch
1 | $urlMode = C('URL_MODEL'); |
取出当前项目的URL_MODEL,根据MODEL来定义项目的地址PHP_FILE。兼容模式的PHP_FIEL是当前项目+C(‘VAR_PATHINFO’)+等号。接下来又是对$urlMode的判断:
1 | if($urlMode) { |
前面已经知道$urlMode只有四个值:0-3,那么在这时,只有$urlMode为0,即普通模式,会走到else分支后的部分:除了路由检查什么也不用干,当然,路由检查还要在开启的状态下。 而对于其它的模式,就要做些处理了。
1 | self::getPathInfo(); |
对当前的URL进行处理,然后判断$_GET是否为空,以及$_GET[C(‘VAR_ROUTER’)])是否设置。
1 | $_GET = array_merge (self :: parsePathInfo(),$_GET); |
取出处理后的PATHINFO信息,与$_GET合并;
读取一些配置:分组、模块、动作的默认变量,URL分隔符,PATHINFO模式。
1 | if (!C('APP_GROUP_LIST')) { |
读取分组名的列表,如果为空,就把分组变量设为空;
判断解析后的url info里是否有模块名和动作名,如果没有,取默认的。
下面开始重新组装URL:
1 | $_URL = '/'; if($_pathModel==2) { $_URL .= $_GET[$_varGroup].($_GET[$_varGroup]?$_depr:'').$_GET[$_varModule].$_depr.$_GET[$_varAction].$_depr; unset($_GET[$_varGroup],$_GET[$_varModule],$_GET[$_varAction]); } |
如果URL_PATHINFO_MODEL是2(PATHINFO、REWRITE),判断Group、Module及Action,并把它们用设置的分隔符给连接起来。最后,从$_GET中删除。
1 | foreach ($_GET as $_VAR => $_VAL) { |
把剩余的$_GET里的信息连接起来。
1 | if($_depr==',') $_URL = substr($_URL, 0, -1).'/'; |
在Url模式的PATHINFO和REWRITE两种模式里,存在着另一种默认的URL分隔符,即英文半角逗号(,):
智能模式 设置URL_PATHINFO_MODEL参数为2 (系统默认的模式)自动识别模块和操作,例如 http://serverName/appName/module/action/id/1/ 或者 http://serverName/appName/module,action,id,1/ 在智能模式下面,第一个参数会被解析成模块名称(或者路由名称,下面会有描述), 第二个参数会被解析成操作(在第一个参数不是路由名称的前提下)
跳转到组装好的URL,URL重写完成。
如果不需要重写:
1 | if(C('URL_ROUTER_ON')) self::routerCheck(); |
判断是否检查路由规则,合并$_GET、$REQUEST。