PHP-不用Rewrite如何实现url路由?
在我们平常用到PHP框架的时候例如这样的URL:http://www.abc.com/controller/action 我们都是采用webserver的rewrite来实现URL路由,有没有不用Rewrite来实现URL解析的方法?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
恰好我之前写了个简单的哈,贴出来你自己看去... 完整的框架代码请到我的博客下载
vb2005xu.iteye.com
这也需要 一个简单的重写配置,通常情况下,如果不允许使用重写,可以使用 404 错误重定向到跳转实现
<?php
/**
* 简单的URL 路由解析器
*
* 匹配规则 区分大小写
* 'rule' => '/microblog/{operate}/{action}/*',
*
* @author vb2005xu.iteye.com
*/
class Router {
private $routes = array();
// url 模型参数,用于标识资源对象
const module = 'mod';
const operate = 'op';
const action = 'act';
const default_module = 'default';
const default_operate = 'application';
const default_action = 'index';
// 不支持url重写的情况下读取 qurlid 这个参数后的串作为pathinfo
const qurlid = 'q';
function __construct($routes_file=false){
if (!empty($routes_file)){
try{
$routes = CoreApp::import($routes_file,null,false);
if (!empty($routes)){
foreach ($routes as $name=>$route){
if (isset($route['rule']) && isset($route['default']))
$this->routes[$name] = $route ;
}
}
}catch(ExpectedFileException $ex){
}
}
if (!isset($this->routes['_default_'])){
$this->routes = array_merge(array(
'_default_' => array(
'rule' => '/{operate}/{action}/*',
'default' => array(
'udi' => self::normalizeUDI('application','index')
)
)
),$this->routes);
}
}
function mapping($name,array $route = null){
if ($route){
if (isset($route['rule']) && isset($route['default'])){
$this->routes[$name] = $route ;
}
}
}
// $pathinfo,$rule 末尾均不能带 / 符号
private function match($pathinfo,$rule){
//比对 $pathinfo 和 $route['pattern']
// /books/13/csx <==> /books/{id}/{keyname}
// 定位模式中 第一个 路由变量[{var}] 出现的位置,并比较
$pos = strpos($rule,'{') ;
if ($pos)
return strcmp(substr($rule, 0,$pos-1),substr($pathinfo, 0,$pos-1)) == 0 ;
// 模式中可能 不包含 路由变量[{var}]
return strlen($pathinfo)>strlen($rule) ?
strcmp(substr($pathinfo,0,strlen($rule)),$rule)==0 : false ;
}
// pathinfo 按 规则解析成 url_params 数组
private function pathinfo_to_array($pathinfo,$rule){
$path_parts = explode('/', substr($pathinfo,1));
$rule_parts = explode('/', substr($rule,1));
$result = array();
while(!empty($rule_parts)){
$r = array_shift($rule_parts);
if ($r == '*' || $r == '?') continue;
$p = array_shift($path_parts);
if ($p === NULL) continue ;
if (strpos($r,'{') === false) continue ;
// 当前是路由变量[{var}]
$var = substr($r,1,strlen($r)-2);
$result[strtolower($var)] = $p ;
}
// dump($path_parts);dump($rule_parts);dump($result);die;
// 解析额外参数,路径部分除指定的路由部分外,其它都使用 /key/value
while(!empty($path_parts)){
$k = array_shift($path_parts);$v = array_shift($path_parts);
if (empty($k)) continue ;
$result[strtolower($k)] = $v ;
}
return $result ;
}
// 填充默认参数
private function __fill_default_params(&$url_params,array $default_params){
if (!$default_params) return;
$udi = isset($default_params['udi']) && is_array($default_params['udi']) ? $default_params['udi'] : NULL;
$params = isset($default_params['params']) && $default_params['udi'] ? $default_params['params'] : NULL;
if ($udi){
// 替换路由定义文件中的 资源标识id
$replace_pairs = array('module'=>self::module,'operate'=>self::operate,'action'=>self::action);
foreach ($replace_pairs as $k=>$v){
if (isset($url_params[$k])){
// 存在就进行 数组键 替换
$url_params[$v] = empty($url_params[$k]) ? $udi[$v] : $url_params[$k];
unset($url_params[$k]);
}
else {
$url_params[$v] = $udi[$v] ;
}
}
}
if ($params){
$url_params = array_merge($params,$url_params);
}
}
protected function parser($pathinfo,$route_name=null){
// $routes
if ($route_name){
if (!isset($this->routes[$route_name])) return null ;
$routes = array($route_name=>$this->routes[$route_name]) ;
}else
$routes = $this->routes ;
$pathinfo = trim($pathinfo);
// /books/show => /books/show/ 不同
// 移除末尾的 / 字符
if (preg_match('/\/$/',$pathinfo))
$pathinfo = preg_replace('/\/$/','',$pathinfo);
foreach (array_reverse($routes) as $name =>$route){
// 移除末尾的 / 字符
$route['rule'] = trim($route['rule']);
if (preg_match('/\/$/',$route['rule']))
$route['rule'] = preg_replace('/\/$/','',$route['rule']);
if ($this->match( $pathinfo,$route['rule'] )){
$url_params = $this->pathinfo_to_array($pathinfo,$route['rule']);
// 合并 $url_params , $route['default'] 成 $_GET数组
$this->__fill_default_params($url_params,$route['default']);
return $url_params ;
}
}
return null ;
}
// 过滤 HTTP GET 请求数组
function filter(& $request){
// 匹配规则,定位到udi
$pathinfo = self::pathinfo();
if (!$pathinfo){ //script_entrypoint未挂参数 或者 不是pathinfo格式
$abs_uri = str_ireplace(SCRIPT_ENTRYPOINT,'',$_SERVER['REQUEST_URI']);
if (!empty($abs_uri)){ // 不是pathinfo格式,不处理$request数组
return ;
}
// script_entrypoint未挂参数时取缺省路由的缺省设置
$url_params = $this->parser($pathinfo,'_default_');
}
if (!isset($url_params)) // 是pathinfo格式
$url_params = $this->parser($pathinfo);
// 改变原来的请求数组
if (!empty($url_params))
$request = array_merge($request,$url_params);
}
/**
* 返回 pathinfo 字符串
* @return string
*/
static function pathinfo(){
static $once = false;
static $pathinfo = null;
if ($once) return $pathinfo;
$once = true;
$pathinfo = !empty($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] :
(empty($_SERVER['ORIG_PATH_INFO']) ? '' : $_SERVER['ORIG_PATH_INFO']);
if (!empty($pathinfo)) return $pathinfo;
// No PATH_INFO?... What about QUERY_STRING?
$pathinfo = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
if (empty($pathinfo)){
$pathinfo = null;
return $pathinfo;
}
if (!isset($_GET[self::qurlid]) || empty($_GET[self::qurlid])) {
$pathinfo = null;
return $pathinfo;
}
$pathinfo = $_GET[self::qurlid];
return $pathinfo;
}
static function normalizeUDI($operate=self::default_operate,$action=self::default_action,$module=self::default_module){
$udi = array(
self::module => empty($module) ? self::default_module : $module,
self::operate => empty($operate) ? self::default_operate : $operate,
self::action => empty($action) ? self::default_action : $action,
);
$udi = array_change_key_case($udi, CASE_LOWER);
return $udi;
}
static function formatUDI(array $udi=null){
$udi = $udi ? array_change_key_case($udi, CASE_LOWER) : array();
if (!isset($udi[self::module]))
$udi[self::module] = self::default_module;
if (!isset($udi[self::operate]))
$udi[self::operate] = self::default_operate;
if (!isset($udi[self::action]))
$udi[self::action] = self::default_action;
return $udi;
}
static function toUDIString(array $udi)
{
$udi = self::formatUDI($udi);
return "{$udi[self::operate]}/{$udi[self::action]}@{$udi[self::module]}";
}
}
有的网站使用的框架是使用这样 http://www.abc.com/index.php/user/login 的形式来实现的,算是另外一种方式吧
Apache 里 可以设置隐藏 default
http://www.abc.com/index.php/user/login 这是采用的pathinfo格式,MVC的路由需要依赖于rewrite,就是说,你要实现http://www.abc.com/user/login.html这样的URL,并且MVC能解析,那么就需要nginxApache等web server的rewrite转发
以前自己写框架,几个核心的文件,把几个类类文件包含进来,执行$app->run()
new出来一个类的实例,执行里面的方法,就可以不用nginx路由重写功能了。
这个类文件就相当于controller,里面的一个个函数就是action.
或者用反射机制也可以实现。