虽然有人抱怨使用Apache的mod_rewrite在访问量大的时候经常会出现CPU的占用率为 100%的情况,但是对于一些中小型网站来说,这样做毕竟会美化URL,有益于搜索引擎的收录。
因此CMSPAD 1.1的最后一个特性就是支持URL路由(或URL Rewrite?),以下称URLR。那么,CMSPAD是如何支持URLR的呢?下面我们来分析一下URL路径的一些特性。
一个URL由协议、主机、端口、访问路径组成,前三者忽略,说说访问路径吧,它是一个由“/”分隔的一个字符串,用来定位文件位置。正常的一个网站的URL模式为:
http://www.yourcompany.com/index.php
其中“/index.php”就是其访问路径(或叫URI?不知道,自己查查吧)。而好多网站为了隐藏整站程序的实现技术,一般都隐藏了文件后缀,例如:
http://www.yourcompany.com/hello/world/2007/12/20
通过某些URLR技术来进行路径重写到指定脚本文件中。Servlet路径模式匹配和RoR都是这样干的。
因此,如何使用CMSPAD把路径给优化成很不错的感觉呢?其实很简单。
1. 建立一个Dispatcher文件,是PHP的。
<?php
require_once('kernel/global.php'); //包含CMSPAD核心文件
import('Dispatcher'); //导入Dispatcher包
$dispatcher = new Dispatcher('default'); //创建Dispatcher实例,这里使用默认的URLR解析器
$dispatcher->dispatch(isset($_SERVER['PATH_INFO'])?$_SERVER['PATH_INFO']:''); //使用PATH_INFO作为参数进行URLR转换。
?>
当然,如果你不想使用PATH_INFO作为路径转换,可以使用其他的数据,例如GET参数。将文件保存在网站目录下,例如 /index.php。
2. 增加 .htaccess 文件:就是写Apache的URL Rewrite规则,这里就不提了。
3. 新建一个Portlet,如果不知道什么叫Portlet,那就看看偶本博客中以前的文章。
class DefaultPortal extends Portlet{
public function pageDefaultPortal($params){
return 'This is home page: '.$params[0];
}
public function pageHelloWorld($params){
return 'This is hello world page: '.print_r($params);
}
}
看到了没有,所有的URLR方法都是以“page”开头的,并且有个类型为单项数组的参数($params),并返回输出的内容,将代码保存到Portlet主目录下的DefaultPortal.php文件中。
4. 本节就三个字:搞定鸟。
在测试之前,大家需要了解一下CMSPAD URLR的一些路径处理规则:
1. default: 默认的路径处理方法,格式为 /[PortletName]/[PageName]/[Param1[/Param2[...]]]
2. statics: 伪静态页的路径处理方法,格式为:/[PortletName]/[PageName]/[Param1[,Param2[...]]].html
在这里如果没有PageName,那么CMSPAD将自动匹配到与PortletName同名的方法,例如使用默认模式的路径 /SimplePortlet/100/200/300 将被转换为 /SimplePortlet/SimplePortlet/100/200/300。
如果连PortletName也没有,没事儿,CMSPAD可以通过配置信息变量 $_CONFIG['DISPATCH']['PORTAL'] 来作为默认的Portlet使用;如果连 $_CONFIG['DISPATCH']['PORTAL'] 变量都没有定义,也没事儿,系统会自动使用 DefaultPortal 作为Portlet名字来使用;如果连 DefaultPortal 也没定义,那没办法了,系统会返回404错误。
下面就开始测试了:
把Apache服务器打开,假设以上程序在web根目录下,在浏览器中敲入下列地址 http://localhost/index.php/DefaultPortal/100000,你将会看到以下内容:
This is home page: 100000
,而敲入 http://localhost/index.php/DefaultPortal/helloWorld/100/200,则会看到:
This is hello world page: Array([0] => 100, [1] => 200)
而如果使用statics模式,则路径分别为:http://localhost/index.php/DefaultPortal/100000.html 和 http://localhost/index.php/DefaultPortal/helloWorld/100,200.html
这就有个问题了,如果可以任意转换URLR处理器,那么在模板中使用路径时怎么样动态转换路径呢?答案当然是有的啦,哈哈
在模板中,使用hyperlink函数即可实现,例如:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>URL Router Portal Test</title>
</head>
<body>
<!-- 完整的使用方法 1-->
<a href="<{hyperlink portlet="DefaultPortal" page="DefaultPortal" arg1="100" arg2="200"}>">Portal Link 1</a><br/>
<!-- 完整的使用方法 2-->
<a href="<{hyperlink portlet="DefaultPortal" page="helloworld" arg1="100" arg2="200"}>">Portal Link 2</a><br/>
<!-- 最简使用方法,使用当前Portlet的名字,如果没有在Portlet中使用则使用默认的DefaultPortal,方法名与Portlet名字相同 -->
<a href="<{hyperlink arg1="100" arg2="200"}>">Portal Link 3</a><br/>
<!-- 简便使用方法 1,方法名与Portlet名相同 -->
<a href="<{hyperlink portlet="DefaultPortal" arg1="100" arg2="200"}>">Portal Link 4</a><br/>
<!-- 简便使用方法 2,使用当前Portlet的名字,如果没有在Portlet中使用则使用默认的DefaultPortal-->
<a href="<{hyperlink page="helloworld" arg1="100" arg2="200"}>">Portal Link 5</a><br/>
</body>
</html>
这样,当在后台转换URLR路径处理了方法时,则模板中自动应用了新的路径。
使用默认URLR处理器时输出为:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>URL Router Portal Test</title>
</head>
<body>
<a href="/index.php/DefaultPortal/100/200">Portal Link 1</a><br/>
<a href="/index.php/DefaultPortal/helloworld/100/200">Portal Link 2</a><br/>
<a href="/index.php/DefaultPortal/100/200">Portal Link 3</a><br/>
<a href="/index.php/DefaultPortal/100/200">Portal Link 4</a><br/>
<a href="/index.php/DefaultPortal/helloworld/100/200">Portal Link 5</a><br/>
</body>
</html>
而使用statics处理器时输出为:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>URL Router Portal Test</title>
</head>
<body>
<a href="/index.php/DefaultPortal/100,200.html">Portal Link 1</a><br/>
<a href="/index.php/DefaultPortal/helloworld/100,200.html">Portal Link 2</a><br/>
<a href="/index.php/DefaultPortal/100,200.html">Portal Link 3</a><br/>
<a href="/index.php/DefaultPortal/100,200.html">Portal Link 4</a><br/>
<a href="/index.php/DefaultPortal/helloworld/100,200.html">Portal Link 5</a><br/>
</body>
</html>
下面来看看如何开发URLR路径处理器吧:
创建一个名字为 cmspad_router_<文件名字>的类,实现 URLRouter接口,并实现接口里的两个函数:
class cmspad_router_myrule implements URLRouter{
public function route($pathinfo){
// 处理并输出路径所对应的内容。
}
public function hyperlink($portlet = null, $method = null, $parameters = array()){
// 生成并返回该规则的URL路径。
}
}
将以上代码保存到 inc/urlrouters/myrule.php 文件中。然后在创建Dispatcher文件的代码中将
$dispatcher = new Dispatcher('default');
改成
$dispatcher = new Dispatcher('myrule');
这样,你写的规则就应用上去了。
下面给出当前默认实现(default)的源代码:
<?php
cmspad_import('util.PortletUtil');
class cmspad_router_default implements URLRouter{
private $portal;
public function __construct($portal = 'DefaultPortal'){
$this->portal = $portal;
}
public function route($pathinfo){
while(strpos($pathinfo,'//') !== false){
$pathinfo = str_replace('//','/',$pathinfo);
}
$pathinfo = trim($pathinfo,' /');
if(empty($pathinfo)){
$pathinfo = $this->portal . '/' . $this->portal;
}
$arr = explode('/',$pathinfo);
if(count($arr) > 0){
$portlet = $arr[0];
$portletObj = PortletUtil::newObject($portlet);
if($portletObj === null || $portletObj === false){
$portletObj = PortletUtil::newObject($portlet = $this->portal);
}else{
array_shift($arr);
}
}
if($portletObj === null || $portletObj === false){
return false;
}
if(count($arr) > 0){
$portletArr = PortletUtil::newMethod($portletObj,'page' . ucfirst($arr[0]));
if($portletArr === null || $portletArr === false){
$portletArr = PortletUtil::newMethod($portletObj,'page' . ucfirst($portlet));
}else{
array_shift($arr);
}
$portletArg = $arr;
}else{
$portletArr = PortletUtil::newMethod($portletObj,'page' . ucfirst($portlet));
$portletArg = array();
}
if($portletArr === null || $portletArr === false){
return false;
}
$contents = call_user_func($portletArr,$portletArg);
if($contents){
echo $contents;
}
return true;
}
public function hyperlink($portlet = null,$method = null,$params = null){
global $_CONFIG;
if(isset($_CONFIG['DISPATCH']['SCRIPT'])){
$url = $_CONFIG['DISPATCH']['SCRIPT'];
}else{
$url = $_SERVER['SCRIPT_NAME'];
}
if(!$portlet){
$portlet = $this->portal;
}
if($portlet){
$url .= ($portlet == $this->portal?'':'/' . $portlet);
if($method){
$url .= ($method == $portlet?'':'/' . $method);
}
if($params){
$url .= '/' . implode('/',$params);
}
}
return $url;
}
}
?>
此代码保存在 inc/urlrouters/default.php 中。