php url 调度

1, 支持非rewrite即:
http://localhost/index.php/blog/view/5456-asdf.html
也可以被正确解析。。

-----------------------------------
2,增加:绝对地址生成 只要 
rurl('myFirstRouter', array('id' => '33', 'name' => 'thename'), true);
最后多加一个true,默认为false即相对地址。
生成绝对地址如:网站根目录/fleaphp/test/blog/view/33-thename.html

修改自ZendFramework的Router_Regexp类。
花了点时间整理的,水平有限,希望有高人能完善一下。

定义一个路由跟定义DSN一样的方法:
  1. return array(
  2. 'routers' =>array(
  3.   'myFirstRouter' => array(
  4.    'blog/view(?:/(\d+)-(.+))\.html',
  5.    array(
  6.     'id'   => '1',
  7.     'controller'  => 'default',
  8.     'action'  => 'index'
  9.    ),
  10.    array(
  11.     1 => 'id',
  12.     2 => 'name'
  13.    ),
  14.    'blog/view/%d-%s.html'
  15.   ),
  16.   'mySecondRouter' => array(
  17.    'blog(?:/(\d+)-(.+))\.html',
  18.    array(
  19.     'id'   => '1',
  20.     'controller'  => 'default',
  21.     'action'  => 'index'
  22.    ),
  23.    array(
  24.     1 => 'id',
  25.     2 => 'name'
  26.    ),
  27.    'blog/%d-%s.html'
  28.   ),
  29.   'myThirdRouter' => array(
  30.    '([a-z0-9]+)/([a-z0-9]+)',
  31.    array(),
  32.    array(
  33.     1 => 'controller',
  34.     2 => 'action'
  35.    ),
  36.    'blog/%d-%s.html'
  37.   )  
  38. )
  39. );
复制代码
是一个二维数组,每一个值为一条路由规则。
其中第一项是正则表达式,第二项为:参数默认值(这里可以设置controller,action,及其它参数的默认值。)
第三项为:参数的对应关系,与第一项的正则表达里面匹配元素对应。
第四项用于生成链接时候使用的格式。如果没看明白,可以看ZF的Router一节。

先发改的My_Dispatcher_Regexp类的代码:
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to [email protected] so we can send you a copy immediately.
  14. *
  15. * @package    Zend_Controller
  16. * @subpackage Router
  17. * @copyright  Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @version    $Id$
  19. * @license    http://framework.zend.com/license/new-bsd     New BSD License
  20. */
  21. // {{{ includes
  22. FLEA::loadClass('FLEA_Dispatcher_Simple');
  23. // }}}
  24. /**
  25. * My_Dispatcher_Regexp
  26. *
  27. * @package My_Dispatcher
  28. * @author tg8866
  29. * @version 0.01
  30. */
  31. class My_Dispatcher_Regexp extends FLEA_Dispatcher_Simple
  32. {
  33.     /**
  34.      * 保存路由信息的数组(二级数组)
  35.      *
  36.      * @var array
  37.      */
  38.     var $_routers;
  39.     
  40.     /**
  41.      * 当前浏览页面的路由信息
  42.      *
  43.      * @var array (一维数组)
  44.      */
  45.     var $_curRouter;
  46.     
  47.     /**
  48.      * 保存baseurl信息
  49.      *
  50.      * @var string
  51.      */
  52.     var $_baseUrl;
  53.     
  54.     /**
  55.      * 保存requestURI信息
  56.      *
  57.      * @var string
  58.      */
  59.     var $_requestUri;
  60.     
  61.     /**
  62.      * 可用的路径信息 不包括 子目录信息 
  63.      * 比如:http://localhost/index.php 是主页
  64.      * 则 http://localhost/blog/view/123-abc.html
  65.      * 的路径信息为: /blog/view/123-abc.html
  66.      * @var unknown_type
  67.      */
  68.     var $_pathInfo;
  69.     /**
  70.      * 构造函数
  71.      *
  72.      * @param array $request
  73.      *
  74.      * @return My_Dispatcher_Regexp
  75.      */
  76.     function My_Dispatcher_Regexp(& $request)
  77.     {
  78.         parent::FLEA_Dispatcher_Simple($request);
  79.         
  80.         $this->loadRouters();
  81.         if (! is_array($this->_routers)) return false;
  82.         if (!$this->_pathInfo) $this->getPathInfo();
  83.         
  84.         foreach (array_reverse($this->_routers) as $router) {
  85.          if (! is_array($router)) continue;
  86.          if ($router[0] == '' || !is_string($router[0])) continue;
  87.          $regexp = '#^' . $router[0]. '$#i';
  88.    if (! isset($router[1])) $router[1] = array();
  89.          if (! isset($router[2])) $router[2] = array();
  90.          if ($args = $this->match($regexp, $this->_pathInfo, $router[1], $router[2])) {
  91.           $this->_curRouter = $router;
  92.           $data['controller'] = $args['controller'];
  93.           $data['action'] = $args['action'];
  94.           $_GET = array_merge($_GET, $args);
  95.           break;
  96.          }
  97.         }
  98.         $this->_request = $data;
  99.     }
  100.     /**
  101.      * 载入路由数据信息
  102.      *
  103.      */
  104.     function loadRouters() {
  105.      static $routerLoaded;
  106.      if ($routerLoaded) return;
  107.      $routerLoaded = false;
  108.      $routerConfig = FLEA::getAppInf('routerConfig');
  109.         FLEA::loadAppInf($routerConfig);
  110.         $this->_routers = FLEA::getAppInf('routers');
  111.         $routerLoaded = true;
  112.     }
  113.     /**
  114.      * 根据服务器环境不同,取得RequestUri信息
  115.      *
  116.      * @return unknown
  117.      */
  118.     function getRequestUri() {
  119.      if ($this->_requestUri) return $this->_requestUri;
  120.         if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { // check this first so IIS will catch
  121.             $requestUri = $_SERVER['HTTP_X_REWRITE_URL'];
  122.         } elseif (isset($_SERVER['REQUEST_URI'])) {
  123.             $requestUri = $_SERVER['REQUEST_URI'];
  124.         } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0, PHP as CGI
  125.             $requestUri = $_SERVER['ORIG_PATH_INFO'];
  126.             if (!empty($_SERVER['QUERY_STRING'])) {
  127.                 $requestUri .= '?' . $_SERVER['QUERY_STRING'];
  128.             }
  129.         } else {
  130.          $requestUri = null;
  131.         }
  132.         $this->_requestUri = $requestUri;
  133.         return $requestUri;
  134.     }
  135.     function getBaseUrl() {
  136.      if ($this->_baseUrl) return $this->_baseUrl;
  137.         $filename = basename($_SERVER['SCRIPT_FILENAME']);
  138.         if (basename($_SERVER['SCRIPT_NAME']) === $filename) {
  139.             $baseUrl = $_SERVER['SCRIPT_NAME'];
  140.         } elseif (basename($_SERVER['PHP_SELF']) === $filename) {
  141.             $baseUrl = $_SERVER['PHP_SELF'];
  142.         } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $filename) {
  143.             $baseUrl = $_SERVER['ORIG_SCRIPT_NAME']; // 1and1 shared hosting compatibility
  144.         } else {
  145.             // Backtrack up the script_filename to find the portion matching
  146.             // php_self
  147.             $path    = $_SERVER['PHP_SELF'];
  148.             $segs    = explode('/', trim($_SERVER['SCRIPT_FILENAME'], '/'));
  149.             $segs    = array_reverse($segs);
  150.             $index   = 0;
  151.             $last    = count($segs);
  152.             $baseUrl = '';
  153.             do {
  154.                 $seg     = $segs[$index];
  155.                 $baseUrl = '/' . $seg . $baseUrl;
  156.                 ++$index;
  157.             } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos));
  158.         }
  159.         // Does the baseUrl have anything in common with the request_uri?
  160.         $requestUri = $this->getRequestUri();
  161.         if (0 === strpos($requestUri, $baseUrl)) {
  162.             // full $baseUrl matches
  163.             $this->_baseUrl = $baseUrl;
  164.             return $this->_baseUrl;
  165.         }
  166.         if (0 === strpos($requestUri, dirname($baseUrl))) {
  167.             // directory portion of $baseUrl matches
  168.             $baseUrl = rtrim(dirname($baseUrl), '/');
  169.             $this->_baseUrl = $baseUrl;
  170.             return $this->_baseUrl;
  171.         }
  172.         if (!strpos($requestUri, basename($baseUrl))) {
  173.             // no match whatsoever; set it blank
  174.             $this->_baseUrl = '';
  175.             return $this->_baseUrl;
  176.         }
  177.         // If using mod_rewrite or ISAPI_Rewrite strip the script filename
  178.         // out of baseUrl. $pos !== 0 makes sure it is not matching a value
  179.         // from PATH_INFO or QUERY_STRING
  180.         if ((strlen($requestUri) >= strlen($baseUrl))
  181.             && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0)))
  182.         {
  183.             $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
  184.         }
  185.        $baseUrl = rtrim($baseUrl, '/'); 
  186.        $this->_baseUrl = $baseUrl; 
  187.        return $this->_baseUrl;    
  188.     }
  189.     function getPathInfo () {
  190.         $baseUrl = $this->getBaseUrl();
  191.         if (null === ($requestUri = $this->getRequestUri())) {
  192.             return null;
  193.         }
  194.         // Remove the query string from REQUEST_URI
  195.         if ($pos = strpos($requestUri, '?')) {
  196.             $requestUri = substr($requestUri, 0, $pos);
  197.         }
  198.         if ((null !== $baseUrl)
  199.             && (false === ($pathInfo = substr($requestUri, strlen($baseUrl)))))
  200.         {
  201.             // If substr() returns false then PATH_INFO is set to an empty string
  202.             $pathInfo = '';
  203.         } elseif (null === $baseUrl) {
  204.             $pathInfo = $requestUri;
  205.         }
  206.         $this->_pathInfo = $pathInfo;
  207.         return $pathInfo;
  208.     }
  209.     
  210.     /**
  211.      * Matches a user submitted path with a previously defined route.
  212.      * Assigns and returns an array of defaults on a successful match.
  213.      *
  214.      * @param string Path used to match against this routing map
  215.      * @return array|false An array of assigned values or a false on a mismatch
  216.      */
  217.     function match($regex, $path, $defaults, $map)
  218.     {
  219.         $path = trim(urldecode($path), '/');
  220.         $res = preg_match($regex, $path, $values);
  221.         if ($res === 0) return false;
  222.         foreach ($values as $i => $value) {
  223.             if (!is_int($i) || $i === 0) {
  224.                 unset($values[$i]);
  225.             }
  226.         }
  227.         $values = $this->_getMappedValues($map, $values);
  228.         $defaults = $this->_getMappedValues($map, $defaults, false, true);
  229.         $return = $values + $defaults;
  230.         return $return;
  231.     }
  232.     /**
  233.      * Maps numerically indexed array values to it's associative mapped counterpart.
  234.      * Or vice versa. Uses user provided map array which consists of index => name
  235.      * parameter mapping. If map is not found, it returns original array.
  236.      *
  237.      * Method strips destination type of keys form source array. Ie. if source array is
  238.      * indexed numerically then every associative key will be stripped. Vice versa if reversed
  239.      * is set to true.
  240.      *
  241.      * @param array Indexed or associative array of values to map
  242.      * @param boolean False means translation of index to association. True means reverse.
  243.      * @param boolean Should wrong type of keys be preserved or stripped.
  244.      * @return array An array of mapped values
  245.      */
  246.     function _getMappedValues($map, $values, $reversed = false, $preserve = false)
  247.     {
  248.         if (count($map) == 0) {
  249.             return $values;
  250.         }
  251.         $return = array();
  252.         foreach ($values as $key => $value) {
  253.             if (is_int($key) && !$reversed) {
  254.                 if (array_key_exists($key, $map)) {
  255.                     $index = $map[$key];
  256.                 } elseif (false === ($index = array_search($key, $map))) {
  257.                     $index = $key;
  258.                 }
  259.                 $return[$index] = $values[$key];
  260.             } elseif ($reversed) {
  261.                 $index = (!is_int($key)) ? array_search($key, $map, true) : $key;
  262.                 if (false !== $index) {
  263.                     $return[$index] = $values[$key];
  264.                 }
  265.             } elseif ($preserve) {
  266.                 $return[$key] = $value;
  267.             }
  268.         }
  269.         return $return;
  270.     }
  271.     /**
  272.      * Assembles a URL path defined by this route
  273.      *
  274.      * @param array An array of name (or index) and value pairs used as parameters
  275.      * @return string Route path with user submitted parameters
  276.      */
  277.     function assemble($defaults, $map = array(), $reverse, $data = array())
  278.     {
  279.         if ($reverse === null) {
  280.             return '构建网址失败!路由参数错误!';   
  281.         }     
  282.         $data = $this->_getMappedValues($map, $data, true, false);
  283.         $data += $this->_getMappedValues($map, $defaults, true, false);
  284.         //$data += $this->_values;
  285.         ksort($data);
  286.         $return = @vsprintf($reverse, $data);
  287.         if ($return === false) {
  288.             return '构建网址失败!';  
  289.         }
  290.         return $return;
  291.     }
  292.     /**
  293.      * 使用路由构建网址
  294.      */
  295.     function url($routerName, $urlOptions, $absolute) {
  296.      $this->loadRouters();
  297.      if (isset($this->_routers[$routerName])) $curRouter = $this->_routers[$routerName];
  298.      elseif (isset($this->_curRouter)) $curRouter = $this->_curRouter;
  299.   if (is_array($curRouter) && count($curRouter) == 4 && is_string($curRouter[3])) {
  300.    $defaults = $curRouter[1];
  301.    $map = $curRouter[2];
  302.    $reverse = $curRouter[3];
  303.   } else {
  304.    return '构建网址失败!路由参数错误!';
  305.   }
  306.      if (is_array($map) && is_string($reverse))
  307.       if (!$absolute) return $this->assemble($defaults, $map, $reverse, $urlOptions);
  308.       else {
  309.        if (!$this->_baseUrl) $this->getBaseUrl();
  310.        return $this->_baseUrl . '/' .$this->assemble($defaults, $map, $reverse, $urlOptions);
  311.       }
  312.     }
  313. }
复制代码
这里要说一个比较好的自定义类的命名规则及文件放置位置。
在FLEA下面建一个My的目录里面放自已的类。比如My_Dispatcher_Regexp放在:
My/Dispatcher/Regexp.php

同时为方便写一个生成网址助手:
My_Helper_Router
My/Helper/Router.php
代码如下:
  1. <?php
  2. /**
  3. * 路由网址助手
  4. */
  5. function rurl($routerName, $urlOptions, $absolute = false) {
  6. $routerHelper =& FLEA::getSingleton('My_Dispatcher_Regexp');
  7. echo $routerHelper->url($routerName, $urlOptions, $absolute);
  8. }
复制代码
使用方法:
/* 修改默认的Dispatcher为自定义的Dispatcher类*/
FLEA::setAppInf('dispatcher','My_Dispatcher_Regexp');

/* 设置路由配置信息的文件位置*/
FLEA::setAppInf('routerConfig', './APP/config/router.php');

其它代码跟任何一个普通的例子一样。

controller里面代码如下:
  1. <?php
  2. class Controller_Default extends FLEA_Controller_Action
  3. {
  4. function actionIndex()
  5. {
  6. FLEA::loadFile('My_Helper_Router');
  7. include('APP/View/PostIndex.php');
  8. }
  9. }
复制代码
我们在view中用下面代码:
  1. <?php
  2. dump($_GET);
  3. rurl('myFirstRouter', array('id' => '33', 'name' => 'thename'));
复制代码
就可以看到$_GET得到正确的参数,
rurl也生成我们期望的网址:
blog/view/33-thename.html

绝对网址生成方法如下:
  1. rurl('myFirstRouter', array('id' => '33', 'name' => 'thename'),true);
  2. //将生成如下的网址:
  3. /other/fleaphp/test/blog/view/33-thename.html
  4. 如果没有使用apache的mod_rewrite功能生成的网址如下:
  5. /fleaphp/test/index.php/blog/view/33-thename.html

你可能感兴趣的:(PHP)