ThinkPHP3.1.3使U方法支持路由详解

ThinkPHP是国产的PHP框架,在很多项目中使用过,是非常优秀的框架。

ThinkPHP在3.1.2以前的版本中,U方法都是支持路由的,这给项目部署带来了极大的方便,在模板中使用U生成url,如需要变更,只需更改一行路由规则即可。 但在3.1.3版本中,不知道官方出于何种考虑,去掉了U方法中的根据路由规则生成URL,这么方便的功能木有了,怎么办?发挥动手精神,自己把这个添加进去吧。 3.1.3版本的U方法代码:

function U($url='',$vars='',$suffix=true,$redirect=false,$domain=false) {
// 解析URL
$info   =  parse_url($url);
$url    =  !empty($info['path'])?$info['path']:ACTION_NAME;
if(isset($info['fragment'])) { // 解析锚点
    $anchor =   $info['fragment'];
    if(false !== strpos($anchor,'?')) { // 解析参数
        list($anchor,$info['query']) = explode('?',$anchor,2);
    }        
    if(false !== strpos($anchor,'@')) { // 解析域名
        list($anchor,$host)    =   explode('@',$anchor, 2);
    }
}elseif(false !== strpos($url,'@')) { // 解析域名
    list($url,$host)    =   explode('@',$info['path'], 2);
}
// 解析子域名
if(isset($host)) {
    $domain = $host.(strpos($host,'.')?'':strstr($_SERVER['HTTP_HOST'],'.'));
}elseif($domain===true){
    $domain = $_SERVER['HTTP_HOST'];
    if(C('APP_SUB_DOMAIN_DEPLOY') ) { // 开启子域名部署
        $domain = $domain=='localhost'?'localhost':'www'.strstr($_SERVER['HTTP_HOST'],'.');
        // '子域名'=>array('项目[/分组]');
        foreach (C('APP_SUB_DOMAIN_RULES') as $key => $rule) {
            if(false === strpos($key,'*') && 0=== strpos($url,$rule[0])) {
                $domain = $key.strstr($domain,'.'); // 生成对应子域名
                $url    =  substr_replace($url,'',0,strlen($rule[0]));
                break;
            }
        }
    }
}

// 解析参数
if(is_string($vars)) { // aaa=1&bbb=2 转换成数组
    parse_str($vars,$vars);
}elseif(!is_array($vars)){
    $vars = array();
}
if(isset($info['query'])) { // 解析地址里面参数 合并到vars
    parse_str($info['query'],$params);
    $vars = array_merge($params,$vars);
}

// URL组装
$depr = C('URL_PATHINFO_DEPR');
if($url) {
    if(0=== strpos($url,'/')) {// 定义路由
        $route      =   true;
        $url        =   substr($url,1);
        if('/' != $depr) {
            $url    =   str_replace('/',$depr,$url);
        }
    }else{
        if('/' != $depr) { // 安全替换
            $url    =   str_replace('/',$depr,$url);
        }
        // 解析分组、模块和操作
        $url        =   trim($url,$depr);
        $path       =   explode($depr,$url);
        $var        =   array();
        $var[C('VAR_ACTION')]       =   !empty($path)?array_pop($path):ACTION_NAME;
        $var[C('VAR_MODULE')]       =   !empty($path)?array_pop($path):MODULE_NAME;
        if($maps = C('URL_ACTION_MAP')) {
            if(isset($maps[strtolower($var[C('VAR_MODULE')])])) {
                $maps    =   $maps[strtolower($var[C('VAR_MODULE')])];
                if($action = array_search(strtolower($var[C('VAR_ACTION')]),$maps)){
                    $var[C('VAR_ACTION')] = $action;
                }
            }
        }
        if($maps = C('URL_MODULE_MAP')) {
            if($module = array_search(strtolower($var[C('VAR_MODULE')]),$maps)){
                $var[C('VAR_MODULE')] = $module;
            }
        }            
        if(C('URL_CASE_INSENSITIVE')) {
            $var[C('VAR_MODULE')]   =   parse_name($var[C('VAR_MODULE')]);
        }
        if(!C('APP_SUB_DOMAIN_DEPLOY') && C('APP_GROUP_LIST')) {
            if(!empty($path)) {
                $group                  =   array_pop($path);
                $var[C('VAR_GROUP')]    =   $group;
            }else{
                if(GROUP_NAME != C('DEFAULT_GROUP')) {
                    $var[C('VAR_GROUP')]=   GROUP_NAME;
                }
            }
            if(C('URL_CASE_INSENSITIVE') && isset($var[C('VAR_GROUP')])) {
                $var[C('VAR_GROUP')]    =  strtolower($var[C('VAR_GROUP')]);
            }
        }
    }
}
//从这里开始构造url  ---------MoLiBei 注
if(C('URL_MODEL') == 0) { // 普通模式URL转换
    $url        =   __APP__.'?'.http_build_query(array_reverse($var));
    if(!empty($vars)) {
        $vars   =   urldecode(http_build_query($vars));
        $url   .=   '&'.$vars;
    }
}else{ // PATHINFO模式或者兼容URL模式
    if(isset($route)) {
        $url    =   __APP__.'/'.rtrim($url,$depr);
    }else{
        $url    =   __APP__.'/'.implode($depr,array_reverse($var));
    }
    if(!empty($vars)) { // 添加参数
        foreach ($vars as $var => $val){
            if('' !== trim($val))   $url .= $depr . $var . $depr . urlencode($val);
        }                
    }
    if($suffix) {
        $suffix   =  $suffix===true?C('URL_HTML_SUFFIX'):$suffix;
        if($pos = strpos($suffix, '|')){
            $suffix = substr($suffix, 0, $pos);
        }
        if($suffix && '/' != substr($url,-1)){
            $url  .=  '.'.ltrim($suffix,'.');
        }
    }
}
if(isset($anchor)){
    $url  .= '#'.$anchor;
}
if($domain) {
    $url   =  (is_ssl()?'https://':'http://').$domain.$url;
}
if($redirect) // 直接跳转URL
    redirect($url);
else
    return $url;
} 

这样,任何路由规则都不起作用了,区别如下:

//3.1.3以前
//路由规则
'/^article/(\d+)$/' => 'article/index?id=1'
//会生成URL如下
/article/1.html
//3.1.3
/article/index/id/1.html

从代码中可以看到,TP官方是直接去掉了根据路由规则构造URL的代码。现在开始移植3.1.2的路由构造代码,修改后的U方法如下:

/**
 * MoLiBei 修改版
 * URL组装 支持不同URL模式
 * @param string $url URL表达式,格式:'[分组/模块/操作#锚点@域名]?参数1=值1&参数2=值2...'
 * @param string|array $vars 传入的参数,支持数组和字符串
 * @param string $suffix 伪静态后缀,默认为true表示获取配置值
 * @param boolean $redirect 是否跳转,如果设置为true则表示跳转到该URL地址
 * @param boolean $domain 是否显示域名
 * @return string
 */
function U($url='',$vars='',$suffix=true,$redirect=false,$domain=false) {
    // 解析URL
    $info   =  parse_url($url);
    $url    =  !empty($info['path'])?$info['path']:ACTION_NAME;
    if(isset($info['fragment'])) { // 解析锚点
        $anchor =   $info['fragment'];
        if(false !== strpos($anchor,'?')) { // 解析参数
            list($anchor,$info['query']) = explode('?',$anchor,2);
        }        
        if(false !== strpos($anchor,'@')) { // 解析域名
            list($anchor,$host)    =   explode('@',$anchor, 2);
        }
    }elseif(false !== strpos($url,'@')) { // 解析域名
        list($url,$host)    =   explode('@',$info['path'], 2);
    }
    // 解析子域名
    if(isset($host)) {
        $domain = $host.(strpos($host,'.')?'':strstr($_SERVER['HTTP_HOST'],'.'));
    }elseif($domain===true){
        $domain = $_SERVER['HTTP_HOST'];
        if(C('APP_SUB_DOMAIN_DEPLOY') ) { // 开启子域名部署
            $domain = $domain=='localhost'?'localhost':'www'.strstr($_SERVER['HTTP_HOST'],'.');
            // '子域名'=>array('项目[/分组]');
            foreach (C('APP_SUB_DOMAIN_RULES') as $key => $rule) {
                if(false === strpos($key,'*') && 0=== strpos($url,$rule[0])) {
                    $domain = $key.strstr($domain,'.'); // 生成对应子域名
                    $url    =  substr_replace($url,'',0,strlen($rule[0]));
                    break;
                }
            }
        }
    }

    // 解析参数
    if(is_string($vars)) { // aaa=1&bbb=2 转换成数组
        parse_str($vars,$vars);
    }elseif(!is_array($vars)){
        $vars = array();
    }
    if(isset($info['query'])) { // 解析地址里面参数 合并到vars
        parse_str($info['query'],$params);
        $vars = array_merge($params,$vars);
    }

    // URL组装
    $depr = C('URL_PATHINFO_DEPR');
    if($url) {
        if(0=== strpos($url,'/')) {// 定义路由
            $route      =   true;
            $url        =   substr($url,1);
            if('/' != $depr) {
                $url    =   str_replace('/',$depr,$url);
            }
        }else{
            if('/' != $depr) { // 安全替换
                $url    =   str_replace('/',$depr,$url);
            }
            // 解析分组、模块和操作
            $url        =   trim($url,$depr);
            $path       =   explode($depr,$url);
            $var        =   array();
            $var[C('VAR_ACTION')]       =   !empty($path)?array_pop($path):ACTION_NAME;
            $var[C('VAR_MODULE')]       =   !empty($path)?array_pop($path):MODULE_NAME;
            if($maps = C('URL_ACTION_MAP')) {
                if(isset($maps[strtolower($var[C('VAR_MODULE')])])) {
                    $maps    =   $maps[strtolower($var[C('VAR_MODULE')])];
                    if($action = array_search(strtolower($var[C('VAR_ACTION')]),$maps)){
                        $var[C('VAR_ACTION')] = $action;
                    }
                }
            }
            if($maps = C('URL_MODULE_MAP')) {
                if($module = array_search(strtolower($var[C('VAR_MODULE')]),$maps)){
                    $var[C('VAR_MODULE')] = $module;
                }
            }            
            if(C('URL_CASE_INSENSITIVE')) {
                $var[C('VAR_MODULE')]   =   parse_name($var[C('VAR_MODULE')]);
            }
            if(!C('APP_SUB_DOMAIN_DEPLOY') && C('APP_GROUP_LIST')) {
                if(!empty($path)) {
                    $group                  =   array_pop($path);
                    $var[C('VAR_GROUP')]    =   $group;
                }else{
                    if(GROUP_NAME != C('DEFAULT_GROUP')) {
                        $var[C('VAR_GROUP')]=   GROUP_NAME;
                    }
                }
                if(C('URL_CASE_INSENSITIVE') && isset($var[C('VAR_GROUP')])) {
                    $var[C('VAR_GROUP')]    =  strtolower($var[C('VAR_GROUP')]);
                }
            }
        }
    }

    if(C('URL_MODEL') == 0) { // 普通模式URL转换
        $url        =   __APP__.'?'.http_build_query(array_reverse($var));
        if(!empty($vars)) {
            $vars   =   urldecode(http_build_query($vars));
            $url   .=   '&'.$vars;
        }
    }else{ // PATHINFO模式或者兼容URL模式
        $routes = C('URL_ROUTE_RULES');
        if(C('URL_ROUTER_ON') && !empty($routes)){ //如果开启路由而且路由规则不为空
            if (false === strpos('/', $url)) {
                $url = implode($depr, array_reverse($var));
            }
            $_url = array_merge(parseUrl($url), $vars);
            foreach ($routes as $rule => $r_route) {
                $r_route = str_replace('/', $depr, $r_route);
                $_route = parseUrl(str_replace(':', '~', $r_route));
                if (url_compare($_url, $_route))
                    break;
            }
            if (url_compare($_url, $_route)) {
                foreach ($_route as $key => $val) {
                    if ($key == 'm' || $key == 'a')
                        continue;
                    if (preg_match("/~/", $val)) {
                        $rule = preg_replace("/(\(\\\[\w]\+\))/", $_url[$key], $rule, 1);
                    }
                }
                //去掉最后的"/"
                $url = __APP__ . '/' . ltrim(rtrim(trim($rule, '/'), '$'), '^');
                $url = str_replace('\\', '', $url);
                }else{
                //当没有符合的路由规则时 使用兼容模式----- MoLiBei
                $rule_status = true;
            }
        }else{
                //使用兼容模式
               $rule_status = true;
        }
        if($rule_status){
            if(isset($route)) {
                $url    =   __APP__.'/'.rtrim($url,$depr);
          }else{
            $url    =   __APP__.'/'.implode($depr,array_reverse($var));
        }
        if(!empty($vars)) { // 添加参数
            foreach ($vars as $var => $val){
                if('' !== trim($val))   $url .= $depr . $var . $depr . urlencode($val);
            }                
        }
    }
    if($suffix) {
        $suffix   =  $suffix===true?C('URL_HTML_SUFFIX'):$suffix;
        if($pos = strpos($suffix, '|')){
            $suffix = substr($suffix, 0, $pos);
        }
        if($suffix && '/' != substr($url,-1)){
            $url  .=  '.'.ltrim($suffix,'.');
        }
    }
}
if(isset($anchor)){
    $url  .= '#'.$anchor;
}
if($domain) {
    $url   =  (is_ssl()?'https://':'http://').$domain.$url;
}
if($redirect) // 直接跳转URL
    redirect($url);
else
    return $url;
}

function url_compare($url, $route) {
    ksort($url);
    $url_keys = array_keys($url);
    ksort($route);
    $route_keys = array_keys($route);
    if ($url_keys != $route_keys) {
    return false;
}
$flag = true;
foreach ($route as $_key => $_val) {
    if (false === strpos($_val, '~')) {
        if ($url[$_key] != $_val) {
            $flag = false;
            break;
        }
    }
}
return $flag;
}

function parseUrl($url) {
    $depr = C('URL_PATHINFO_DEPR');
    $var = array();
    if (false !== strpos($url, '?')) { // [分组/模块/操作?]参数1=值1&参数2=值2...
        $info = parse_url($url);
        $path = explode($depr, $info['path']);
        parse_str($info['query'], $var);
    } elseif (strpos($url, $depr)) { // [分组/模块/操作]
        $path = explode($depr, $url);
    } else { // 参数1=值1&参数2=值2...
        parse_str($url, $var);
    }
    if (isset($path)) {
        $var[C('VAR_ACTION')] = array_pop($path);
        if (!empty($path)) {
            $var[C('VAR_MODULE')] = array_pop($path);
        }
        if (!empty($path)) {
            $var[C('VAR_GROUP')] = array_pop($path);
        }
    }
    return $var;
}

到这里,就修改完成了,只支持正则路由,规则路由还在调试中。

标签: php, thinkphp, 路由

添加新评论