当前位置:首页 > CMS教程 > 其它CMS > 列表

Zend Framework教程之路由功能Zend_Controller_Router详解

发布:smiling 来源: PHP粉丝网  添加日期:2021-07-13 16:01:39 浏览: 评论:0 

本文实例讲述了Zend Framework教程之路由功能Zend_Controller_Router用法,分享给大家供大家参考,具体如下:

Zend Framework的路由提供了两个主要功能路由和创建路由。

Zend_Controller_Router的Route类和相应Route目录下的类定义常见的路由操作。

接口Zend_Controller_Router_Interface,类Zend_Controller_Router_Abstract和Zend_Controller_Router_Rewrite完成了基本的路由,创建路由,删除路由的功能。

  1. └── Router 
  2.     ├── Abstract.php 
  3.     ├── Exception.php 
  4.     ├── Interface.php 
  5.     ├── Rewrite.php 
  6.     ├── Route 
  7.     │   ├── Abstract.php 
  8.     │   ├── Chain.php 
  9.     │   ├── Hostname.php 
  10.     │   ├── Interface.php 
  11.     │   ├── Module.php 
  12.     │   ├── Regex.php 
  13.     │   └── Static.php 
  14.     └── Route.php 

Zend_Controller_Router路由功能的实现

Zend_Controller_Router_Interface

  1. <?php 
  2. interface Zend_Controller_Router_Interface 
  3. { 
  4.   /** 
  5.    * Processes a request and sets its controller and action. If 
  6.    * no route was possible, an exception is thrown. 
  7.    * 
  8.    * @param Zend_Controller_Request_Abstract 
  9.    * @throws Zend_Controller_Router_Exception 
  10.    * @return Zend_Controller_Request_Abstract|boolean 
  11.    */ 
  12.   public function route(Zend_Controller_Request_Abstract $dispatcher); 
  13.   /** 
  14.    * Generates a URL path that can be used in URL creation, redirection, etc. 
  15.    * 
  16.    * May be passed user params to override ones from URI, Request or even defaults. 
  17.    * If passed parameter has a value of null, it's URL variable will be reset to 
  18.    * default. 
  19.    * 
  20.    * If null is passed as a route name assemble will use the current Route or 'default' 
  21.    * if current is not yet set. 
  22.    * 
  23.    * Reset is used to signal that all parameters should be reset to it's defaults. 
  24.    * Ignoring all URL specified values. User specified params still get precedence. 
  25.    * 
  26.    * Encode tells to url encode resulting path parts. 
  27.    * 
  28.    * @param array $userParams Options passed by a user used to override parameters 
  29.    * @param mixed $name The name of a Route to use 
  30.    * @param bool $reset Whether to reset to the route defaults ignoring URL params 
  31.    * @param bool $encode Tells to encode URL parts on output 
  32.    * @throws Zend_Controller_Router_Exception 
  33.    * @return string Resulting URL path 
  34.    */ 
  35.   public function assemble($userParams, $name = null, $reset = false, $encode = true); 
  36.   /** 
  37.    * Retrieve Front Controller 
  38.    * 
  39.    * @return Zend_Controller_Front 
  40.    */ 
  41.   public function getFrontController(); 
  42.   /** 
  43.    * Set Front Controller 
  44.    * 
  45.    * @param Zend_Controller_Front $controller 
  46.    * @return Zend_Controller_Router_Interface 
  47.    */ 
  48.   public function setFrontController(Zend_Controller_Front $controller); 
  49.   /** 
  50.    * Add or modify a parameter with which to instantiate any helper objects 
  51.    * 
  52.    * @param string $name 
  53.    * @param mixed $param 
  54.    * @return Zend_Controller_Router_Interface 
  55.    */ 
  56.   public function setParam($name, $value); 
  57.   /** 
  58.    * Set an array of a parameters to pass to helper object constructors 
  59.    * 
  60.    * @param array $params 
  61.    * @return Zend_Controller_Router_Interface 
  62.    */ 
  63.   public function setParams(array $params); 
  64.   /** 
  65.    * Retrieve a single parameter from the controller parameter stack 
  66.    * 
  67.    * @param string $name 
  68.    * @return mixed 
  69.    */ 
  70.   public function getParam($name); 
  71.   /** 
  72.    * Retrieve the parameters to pass to helper object constructors 
  73.    * 
  74.    * @return array 
  75.    */ 
  76.   public function getParams(); 
  77.   /** 
  78.    * Clear the controller parameter stack 
  79.    * 
  80.    * By default, clears all parameters. If a parameter name is given, clears 
  81.    * only that parameter; if an array of parameter names is provided, clears 
  82.    * each. 
  83.    * 
  84.    * @param null|string|array single key or array of keys for params to clear 
  85.    * @return Zend_Controller_Router_Interface 
  86.    */ 
  87.   public function clearParams($name = null); 
  88. } 

Zend_Controller_Router_Abstract

  1. <?php 
  2. /** Zend_Controller_Router_Interface */ 
  3. require_once 'Zend/Controller/Router/Interface.php'; 
  4. abstract class Zend_Controller_Router_Abstract implements Zend_Controller_Router_Interface 
  5. { 
  6.   /** 
  7.    * URI delimiter 
  8.    */ 
  9.   const URI_DELIMITER = '/'; 
  10.   /** 
  11.    * Front controller instance 
  12.    * @var Zend_Controller_Front 
  13.    */ 
  14.   protected $_frontController; 
  15.   /** 
  16.    * Array of invocation parameters to use when instantiating action 
  17.    * controllers 
  18.    * @var array 
  19.    */ 
  20.   protected $_invokeParams = array(); 
  21.   /** 
  22.    * Constructor 
  23.    * 
  24.    * @param array $params 
  25.    * @return void 
  26.    */ 
  27.   public function __construct(array $params = array()) 
  28.   { 
  29.     $this->setParams($params); 
  30.   } 
  31.   /** 
  32.    * Add or modify a parameter to use when instantiating an action controller 
  33.    * 
  34.    * @param string $name 
  35.    * @param mixed $value 
  36.    * @return Zend_Controller_Router 
  37.    */ 
  38.   public function setParam($name, $value) 
  39.   { 
  40.     $name = (string) $name; 
  41.     $this->_invokeParams[$name] = $value; 
  42.     return $this; 
  43.   } 
  44.   /** 
  45.    * Set parameters to pass to action controller constructors 
  46.    * 
  47.    * @param array $params 
  48.    * @return Zend_Controller_Router 
  49.    */ 
  50.   public function setParams(array $params) 
  51.   { 
  52.     $this->_invokeParams = array_merge($this->_invokeParams, $params); 
  53.     return $this; 
  54.   } 
  55.   /** 
  56.    * Retrieve a single parameter from the controller parameter stack 
  57.    * 
  58.    * @param string $name 
  59.    * @return mixed 
  60.    */ 
  61.   public function getParam($name) 
  62.   { 
  63.     if(isset($this->_invokeParams[$name])) { 
  64.       return $this->_invokeParams[$name]; 
  65.     } 
  66.     return null; 
  67.   } 
  68.   /** 
  69.    * Retrieve action controller instantiation parameters 
  70.    * 
  71.    * @return array 
  72.    */ 
  73.   public function getParams() 
  74.   { 
  75.     return $this->_invokeParams; 
  76.   } 
  77.   /** 
  78.    * Clear the controller parameter stack 
  79.    * 
  80.    * By default, clears all parameters. If a parameter name is given, clears 
  81.    * only that parameter; if an array of parameter names is provided, clears 
  82.    * each. 
  83.    * 
  84.    * @param null|string|array single key or array of keys for params to clear 
  85.    * @return Zend_Controller_Router 
  86.    */ 
  87.   public function clearParams($name = null) 
  88.   { 
  89.     if (null === $name) { 
  90.       $this->_invokeParams = array(); 
  91.     } elseif (is_string($name) && isset($this->_invokeParams[$name])) { 
  92.       unset($this->_invokeParams[$name]); 
  93.     } elseif (is_array($name)) { 
  94.       foreach ($name as $key) { 
  95.         if (is_string($key) && isset($this->_invokeParams[$key])) { 
  96.           unset($this->_invokeParams[$key]); 
  97.         } 
  98.       } 
  99.     } 
  100.     return $this; 
  101.   } 
  102.   /** 
  103.    * Retrieve Front Controller 
  104.    * 
  105.    * @return Zend_Controller_Front 
  106.    */ 
  107.   public function getFrontController() 
  108.   { 
  109.     // Used cache version if found 
  110.     if (null !== $this->_frontController) { 
  111.       return $this->_frontController; 
  112.     } 
  113.     require_once 'Zend/Controller/Front.php'; 
  114.     $this->_frontController = Zend_Controller_Front::getInstance(); 
  115.     return $this->_frontController; 
  116.   } 
  117.   /** 
  118.    * Set Front Controller 
  119.    * 
  120.    * @param Zend_Controller_Front $controller 
  121.    * @return Zend_Controller_Router_Interface 
  122.    */ 
  123.   public function setFrontController(Zend_Controller_Front $controller) 
  124.   { 
  125.     $this->_frontController = $controller; 
  126.     return $this; 
  127.   } 
  128. } 

Zend_Controller_Router_Rewrite

  1. <?php 
  2. /** Zend_Controller_Router_Abstract */ 
  3. require_once 'Zend/Controller/Router/Abstract.php'; 
  4. /** Zend_Controller_Router_Route */ 
  5. require_once 'Zend/Controller/Router/Route.php'; 
  6. class Zend_Controller_Router_Rewrite extends Zend_Controller_Router_Abstract 
  7. { 
  8.   /** 
  9.    * Whether or not to use default routes 
  10.    * 
  11.    * @var boolean 
  12.    */ 
  13.   protected $_useDefaultRoutes = true; 
  14.   /** 
  15.    * Array of routes to match against 
  16.    * 
  17.    * @var array 
  18.    */ 
  19.   protected $_routes = array(); 
  20.   /** 
  21.    * Currently matched route 
  22.    * 
  23.    * @var Zend_Controller_Router_Route_Interface 
  24.    */ 
  25.   protected $_currentRoute = null; 
  26.   /** 
  27.    * Global parameters given to all routes 
  28.    * 
  29.    * @var array 
  30.    */ 
  31.   protected $_globalParams = array(); 
  32.   /** 
  33.    * Separator to use with chain names 
  34.    * 
  35.    * @var string 
  36.    */ 
  37.   protected $_chainNameSeparator = '-'; 
  38.   /** 
  39.    * Determines if request parameters should be used as global parameters 
  40.    * inside this router. 
  41.    * 
  42.    * @var boolean 
  43.    */ 
  44.   protected $_useCurrentParamsAsGlobal = false; 
  45.   /** 
  46.    * Add default routes which are used to mimic basic router behaviour 
  47.    * 
  48.    * @return Zend_Controller_Router_Rewrite 
  49.    */ 
  50.   public function addDefaultRoutes() 
  51.   { 
  52.     if (!$this->hasRoute('default')) { 
  53.       $dispatcher = $this->getFrontController()->getDispatcher(); 
  54.       $request = $this->getFrontController()->getRequest(); 
  55.       require_once 'Zend/Controller/Router/Route/Module.php'; 
  56.       $compat = new Zend_Controller_Router_Route_Module(array(), $dispatcher, $request); 
  57.       $this->_routes = array('default' => $compat) + $this->_routes; 
  58.     } 
  59.     return $this; 
  60.   } 
  61.   /** 
  62.    * Add route to the route chain 
  63.    * 
  64.    * If route contains method setRequest(), it is initialized with a request object 
  65.    * 
  66.    * @param string                 $name    Name of the route 
  67.    * @param Zend_Controller_Router_Route_Interface $route   Instance of the route 
  68.    * @return Zend_Controller_Router_Rewrite 
  69.    */ 
  70.   public function addRoute($name, Zend_Controller_Router_Route_Interface $route) 
  71.   { 
  72.     if (method_exists($route, 'setRequest')) { 
  73.       $route->setRequest($this->getFrontController()->getRequest()); 
  74.     } 
  75.     $this->_routes[$name] = $route; 
  76.     return $this; 
  77.   } 
  78.   /** 
  79.    * Add routes to the route chain 
  80.    * 
  81.    * @param array $routes Array of routes with names as keys and routes as values 
  82.    * @return Zend_Controller_Router_Rewrite 
  83.    */ 
  84.   public function addRoutes($routes) { 
  85.     foreach ($routes as $name => $route) { 
  86.       $this->addRoute($name, $route); 
  87.     } 
  88.     return $this; 
  89.   } 
  90.   /** 
  91.    * Create routes out of Zend_Config configuration 
  92.    * 
  93.    * Example INI: 
  94.    * routes.archive.route = "archive/:year/*" 
  95.    * routes.archive.defaults.controller = archive 
  96.    * routes.archive.defaults.action = show 
  97.    * routes.archive.defaults.year = 2000 
  98.    * routes.archive.reqs.year = "\d+" 
  99.    * 
  100.    * routes.news.type = "Zend_Controller_Router_Route_Static" 
  101.    * routes.news.route = "news" 
  102.    * routes.news.defaults.controller = "news" 
  103.    * routes.news.defaults.action = "list" 
  104.    * 
  105.    * And finally after you have created a Zend_Config with above ini: 
  106.    * $router = new Zend_Controller_Router_Rewrite(); 
  107.    * $router->addConfig($config, 'routes'); 
  108.    * 
  109.    * @param Zend_Config $config Configuration object 
  110.    * @param string   $section Name of the config section containing route's definitions 
  111.    * @throws Zend_Controller_Router_Exception 
  112.    * @return Zend_Controller_Router_Rewrite 
  113.    */ 
  114.   public function addConfig(Zend_Config $config, $section = null) 
  115.   { 
  116.     if ($section !== null) { 
  117.       if ($config->{$section} === null) { 
  118.         require_once 'Zend/Controller/Router/Exception.php'; 
  119.         throw new Zend_Controller_Router_Exception("No route configuration in section '{$section}'"); 
  120.       } 
  121.       $config = $config->{$section}; 
  122.     } 
  123.     foreach ($config as $name => $info) { 
  124.       $route = $this->_getRouteFromConfig($info); 
  125.       if ($route instanceof Zend_Controller_Router_Route_Chain) { 
  126.         if (!isset($info->chain)) { 
  127.           require_once 'Zend/Controller/Router/Exception.php'; 
  128.           throw new Zend_Controller_Router_Exception("No chain defined"); 
  129.         } 
  130.         if ($info->chain instanceof Zend_Config) { 
  131.           $childRouteNames = $info->chain; 
  132.         } else { 
  133.           $childRouteNames = explode(',', $info->chain); 
  134.         } 
  135.         foreach ($childRouteNames as $childRouteName) { 
  136.           $childRoute = $this->getRoute(trim($childRouteName)); 
  137.           $route->chain($childRoute); 
  138.         } 
  139.         $this->addRoute($name, $route); 
  140.       } elseif (isset($info->chains) && $info->chains instanceof Zend_Config) { 
  141.         $this->_addChainRoutesFromConfig($name, $route, $info->chains); 
  142.       } else { 
  143.         $this->addRoute($name, $route); 
  144.       } 
  145.     } 
  146.     return $this; 
  147.   } 
  148.   /** 
  149.    * Get a route frm a config instance 
  150.    * 
  151.    * @param Zend_Config $info 
  152.    * @return Zend_Controller_Router_Route_Interface 
  153.    */ 
  154.   protected function _getRouteFromConfig(Zend_Config $info) 
  155.   { 
  156.     $class = (isset($info->type)) ? $info->type : 'Zend_Controller_Router_Route'; 
  157.     if (!class_exists($class)) { 
  158.       require_once 'Zend/Loader.php'; 
  159.       Zend_Loader::loadClass($class); 
  160.     } 
  161.     $route = call_user_func(array($class, 'getInstance'), $info); 
  162.     if (isset($info->abstract) && $info->abstract && method_exists($route, 'isAbstract')) { 
  163.       $route->isAbstract(true); 
  164.     } 
  165.     return $route; 
  166.   } 
  167.   /** 
  168.    * Add chain routes from a config route 
  169.    * 
  170.    * @param string                 $name 
  171.    * @param Zend_Controller_Router_Route_Interface $route 
  172.    * @param Zend_Config              $childRoutesInfo 
  173.    * @return void 
  174.    */ 
  175.   protected function _addChainRoutesFromConfig($name, 
  176.                          Zend_Controller_Router_Route_Interface $route, 
  177.                          Zend_Config $childRoutesInfo) 
  178.   { 
  179.     foreach ($childRoutesInfo as $childRouteName => $childRouteInfo) { 
  180.       if (is_string($childRouteInfo)) { 
  181.         $childRouteName = $childRouteInfo; 
  182.         $childRoute   = $this->getRoute($childRouteName); 
  183.       } else { 
  184.         $childRoute = $this->_getRouteFromConfig($childRouteInfo); 
  185.       } 
  186.       if ($route instanceof Zend_Controller_Router_Route_Chain) { 
  187.         $chainRoute = clone $route; 
  188.         $chainRoute->chain($childRoute); 
  189.       } else { 
  190.         $chainRoute = $route->chain($childRoute); 
  191.       } 
  192.       $chainName = $name . $this->_chainNameSeparator . $childRouteName; 
  193.       if (isset($childRouteInfo->chains)) { 
  194.         $this->_addChainRoutesFromConfig($chainName, $chainRoute, $childRouteInfo->chains); 
  195.       } else { 
  196.         $this->addRoute($chainName, $chainRoute); 
  197.       } 
  198.     } 
  199.   } 
  200.   /** 
  201.    * Remove a route from the route chain 
  202.    * 
  203.    * @param string $name Name of the route 
  204.    * @throws Zend_Controller_Router_Exception 
  205.    * @return Zend_Controller_Router_Rewrite 
  206.    */ 
  207.   public function removeRoute($name) 
  208.   { 
  209.     if (!isset($this->_routes[$name])) { 
  210.       require_once 'Zend/Controller/Router/Exception.php'; 
  211.       throw new Zend_Controller_Router_Exception("Route $name is not defined"); 
  212.     } 
  213.     unset($this->_routes[$name]); 
  214.     return $this; 
  215.   } 
  216.   /** 
  217.    * Remove all standard default routes 
  218.    * 
  219.    * @param Zend_Controller_Router_Route_Interface Route 
  220.    * @return Zend_Controller_Router_Rewrite 
  221.    */ 
  222.   public function removeDefaultRoutes() 
  223.   { 
  224.     $this->_useDefaultRoutes = false; 
  225.     return $this; 
  226.   } 
  227.   /** 
  228.    * Check if named route exists 
  229.    * 
  230.    * @param string $name Name of the route 
  231.    * @return boolean 
  232.    */ 
  233.   public function hasRoute($name) 
  234.   { 
  235.     return isset($this->_routes[$name]); 
  236.   } 
  237.   /** 
  238.    * Retrieve a named route 
  239.    * 
  240.    * @param string $name Name of the route 
  241.    * @throws Zend_Controller_Router_Exception 
  242.    * @return Zend_Controller_Router_Route_Interface Route object 
  243.    */ 
  244.   public function getRoute($name) 
  245.   { 
  246.     if (!isset($this->_routes[$name])) { 
  247.       require_once 'Zend/Controller/Router/Exception.php'; 
  248.       throw new Zend_Controller_Router_Exception("Route $name is not defined"); 
  249.     } 
  250.     return $this->_routes[$name]; 
  251.   } 
  252.   /** 
  253.    * Retrieve a currently matched route 
  254.    * 
  255.    * @throws Zend_Controller_Router_Exception 
  256.    * @return Zend_Controller_Router_Route_Interface Route object 
  257.    */ 
  258.   public function getCurrentRoute() 
  259.   { 
  260.     if (!isset($this->_currentRoute)) { 
  261.       require_once 'Zend/Controller/Router/Exception.php'; 
  262.       throw new Zend_Controller_Router_Exception("Current route is not defined"); 
  263.     } 
  264.     return $this->getRoute($this->_currentRoute); 
  265.   } 
  266.   /** 
  267.    * Retrieve a name of currently matched route 
  268.    * 
  269.    * @throws Zend_Controller_Router_Exception 
  270.    * @return Zend_Controller_Router_Route_Interface Route object 
  271.    */ 
  272.   public function getCurrentRouteName() 
  273.   { 
  274.     if (!isset($this->_currentRoute)) { 
  275.       require_once 'Zend/Controller/Router/Exception.php'; 
  276.       throw new Zend_Controller_Router_Exception("Current route is not defined"); 
  277.     } 
  278.     return $this->_currentRoute; 
  279.   } 
  280.   /** 
  281.    * Retrieve an array of routes added to the route chain 
  282.    * 
  283.    * @return array All of the defined routes 
  284.    */ 
  285.   public function getRoutes() 
  286.   { 
  287.     return $this->_routes; 
  288.   } 
  289.   /** 
  290.    * Find a matching route to the current PATH_INFO and inject 
  291.    * returning values to the Request object. 
  292.    * 
  293.    * @throws Zend_Controller_Router_Exception 
  294.    * @return Zend_Controller_Request_Abstract Request object 
  295.    */ 
  296.   public function route(Zend_Controller_Request_Abstract $request) 
  297.   { 
  298.     if (!$request instanceof Zend_Controller_Request_Http) { 
  299.       require_once 'Zend/Controller/Router/Exception.php'; 
  300.       throw new Zend_Controller_Router_Exception('Zend_Controller_Router_Rewrite requires a Zend_Controller_Request_Http-based request object'); 
  301.     } 
  302.     if ($this->_useDefaultRoutes) { 
  303.       $this->addDefaultRoutes(); 
  304.     } 
  305.     // Find the matching route 
  306.     $routeMatched = false; 
  307.     foreach (array_reverse($this->_routes, true) as $name => $route) { 
  308.       // TODO: Should be an interface method. Hack for 1.0 BC 
  309.       if (method_exists($route, 'isAbstract') && $route->isAbstract()) { 
  310.         continue; 
  311.       } 
  312.       // TODO: Should be an interface method. Hack for 1.0 BC 
  313.       if (!method_exists($route, 'getVersion') || $route->getVersion() == 1) { 
  314.         $match = $request->getPathInfo(); 
  315.       } else { 
  316.         $match = $request; 
  317.       } 
  318.       if ($params = $route->match($match)) { 
  319.         $this->_setRequestParams($request, $params); 
  320.         $this->_currentRoute = $name; 
  321.         $routeMatched    = true; 
  322.         break; 
  323.       } 
  324.     } 
  325.      if (!$routeMatched) { 
  326.        require_once 'Zend/Controller/Router/Exception.php'; 
  327.        throw new Zend_Controller_Router_Exception('No route matched the request', 404); 
  328.      } 
  329.     if($this->_useCurrentParamsAsGlobal) { 
  330.       $params = $request->getParams(); 
  331.       foreach($params as $param => $value) { 
  332.         $this->setGlobalParam($param, $value); 
  333.       } 
  334.     } 
  335.     return $request; 
  336.   } 
  337.   protected function _setRequestParams($request, $params) 
  338.   { 
  339.     foreach ($params as $param => $value) { 
  340.       $request->setParam($param, $value); 
  341.       if ($param === $request->getModuleKey()) { 
  342.         $request->setModuleName($value); 
  343.       } 
  344.       if ($param === $request->getControllerKey()) { 
  345.         $request->setControllerName($value); 
  346.       } 
  347.       if ($param === $request->getActionKey()) { 
  348.         $request->setActionName($value); 
  349.       } 
  350.     } 
  351.   } 
  352.   /** 
  353.    * Generates a URL path that can be used in URL creation, redirection, etc. 
  354.    * 
  355.    * @param array $userParams Options passed by a user used to override parameters 
  356.    * @param mixed $name The name of a Route to use 
  357.    * @param bool $reset Whether to reset to the route defaults ignoring URL params 
  358.    * @param bool $encode Tells to encode URL parts on output 
  359.    * @throws Zend_Controller_Router_Exception 
  360.    * @return string Resulting absolute URL path 
  361.    */ 
  362.   public function assemble($userParams, $name = null, $reset = false, $encode = true) 
  363.   { 
  364.     if (!is_array($userParams)) { 
  365.       require_once 'Zend/Controller/Router/Exception.php'; 
  366.       throw new Zend_Controller_Router_Exception('userParams must be an array'); 
  367.     } 
  368.     if ($name == null) { 
  369.       try { 
  370.         $name = $this->getCurrentRouteName(); 
  371.       } catch (Zend_Controller_Router_Exception $e) { 
  372.         $name = 'default'; 
  373.       } 
  374.     } 
  375.     // Use UNION (+) in order to preserve numeric keys 
  376.     $params = $userParams + $this->_globalParams; 
  377.     $route = $this->getRoute($name); 
  378.     $url  = $route->assemble($params, $reset, $encode); 
  379.     if (!preg_match('|^[a-z]+://|', $url)) { 
  380.       $url = rtrim($this->getFrontController()->getBaseUrl(), self::URI_DELIMITER) . self::URI_DELIMITER . $url; 
  381.     } 
  382.     return $url; 
  383.   } 
  384.   /** 
  385.    * Set a global parameter 
  386.    * 
  387.    * @param string $name 
  388.    * @param mixed $value 
  389.    * @return Zend_Controller_Router_Rewrite 
  390.    */ 
  391.   public function setGlobalParam($name, $value) 
  392.   { 
  393.     $this->_globalParams[$name] = $value; 
  394.     return $this; 
  395.   } 
  396.   /** 
  397.    * Set the separator to use with chain names 
  398.    * 
  399.    * @param string $separator The separator to use 
  400.    * @return Zend_Controller_Router_Rewrite 
  401.    */ 
  402.   public function setChainNameSeparator($separator) { 
  403.     $this->_chainNameSeparator = $separator; 
  404.     return $this; 
  405.   } 
  406.   /** 
  407.    * Get the separator to use for chain names 
  408.    * 
  409.    * @return string 
  410.    */ 
  411.   public function getChainNameSeparator() { 
  412.     return $this->_chainNameSeparator; 
  413.   } 
  414.   /** 
  415.    * Determines/returns whether to use the request parameters as global parameters. 
  416.    * 
  417.    * @param boolean|null $use 
  418.    *      Null/unset when you want to retrieve the current state. 
  419.    *      True when request parameters should be global, false otherwise 
  420.    * @return boolean|Zend_Controller_Router_Rewrite 
  421.    *       Returns a boolean if first param isn't set, returns an 
  422.    *       instance of Zend_Controller_Router_Rewrite otherwise. 
  423.    * 
  424.    */ 
  425.   public function useRequestParametersAsGlobal($use = null) { 
  426.     if($use === null) { 
  427.       return $this->_useCurrentParamsAsGlobal; 
  428.     } 
  429.     $this->_useCurrentParamsAsGlobal = (bool) $use; 
  430.     return $this; 
  431.   } 
  432. } 

添加路由的操作方法

  1. public function addRoute($name, Zend_Controller_Router_Route_Interface $route) 
  2.  
  3. public function addRoutes($routes) 
  4.  
  5. $router = $ctrl->getRouter(); // returns a rewrite router by default 
  6. $router->addRoute('user', 
  7.          new Zend_Controller_Router_Route('user/:username')); 

addRoute的第一个参数是路由名。第二个参数是路由自己,路由名最普通的用法是通过Zend_View_Url助手的方法:

"<?= $this->url(array('username' => 'martel'), 'user') ?>">Martel</a>

它将导致在 href: user/martel.

路由是一个简单的过程,这个过程通过所有提供的路由和匹配它的当前请求的URI定义来迭代。当一个正匹配被发现,变量值从路由实例返回并注入到Zend_Controller_Request对象以备将来在派遣器和用户创建的控制器中使用。如果是负匹配,在链中的下个路由被检查。

Note: 倒序匹配

用倒序来匹配路由确保最通用的路由被首先定义。

Note: 返回的值

从路由返回的值来自于URL参数或用于定义的缺省值。这些变量以后可通过Zend_Controller_Request::getParam() 或 Zend_Controller_Action::_getParam() 方法来访问。

有三个特殊的变量可用于你的路由-'module'、 'controller' 和 'action'。这些特殊的变量被Zend_Controller_Dispatcher用来找出控制器和动作然后派遣过去。

Note: 特殊变量

如果你选择通过 setControllerKey 和 setActionKey方法的方式来改变缺省值,这些特殊变量的名字可能会不同。

缺省路由

Zend_Controller_Router_Rewrite 和缺省路由一起预先配置,它将以controller/action的形式匹配URIs。另外,模块名可以被指定作为第一个路径参数,允许这种module/controller/action形式的URIs。最后,它也将缺省地匹配任何另外的追加到URI的参数-controller/action/var1/value1/var2/value2。

一些路由如何匹配的例子:

  1. // Assuming the following: 
  2. $ctrl->setControllerDirectory( 
  3.   array( 
  4.     'default' => '/path/to/default/controllers', 
  5.     'news'  => '/path/to/news/controllers', 
  6.     'blog'  => '/path/to/blog/controllers' 
  7.   ) 
  8. ); 
  9. Module only: 
  10. http://example/news 
  11.   module == news 
  12. Invalid module maps to controller name: 
  13. http://example/foo 
  14.   controller == foo 
  15. Module + controller: 
  16. http://example/blog/archive 
  17.   module   == blog 
  18.   controller == archive 
  19. Module + controller + action: 
  20. http://example/blog/archive/list 
  21.   module   == blog 
  22.   controller == archive 
  23.   action   == list 
  24. Module + controller + action + params: 
  25. http://example/blog/archive/list/sort/alpha/date/desc 
  26.   module   == blog 
  27.   controller == archive 
  28.   action   == list 
  29.   sort    == alpha 
  30.   date    == desc 

缺省路由是存储在RewriteRouter名(index)为'default'的简单的Zend_Controller_Router_Route_Module对象。它被创建多多少少象下面这样:

  1. $compat = new Zend_Controller_Router_Route_Module(array(), 
  2.                          $dispatcher, 
  3.                          $request); 
  4. $this->addRoute('default', $compat); 

如果你不想这个特别的缺省路由在你的路由计划中,你可以重写你自己的‘缺省'路由(例如,把它存储在'default'名下)或用removeDefaultRoutes()完全清除它:

// Remove any default routes

$router->removeDefaultRoutes();

为了增加路由的灵活性,方便自定义新的路由类型,Zend_Controller_Router定义了Zend_Controller_Router_Route_Interface接口和类Zend_Controller_Router_Route_Abstract,实现相应的类方法即可定义路由类型,为开发提供了便利。

Zend_Controller_Router的路由类型

Zend_Controller_Router默认提供了以下路由类型,分别为:

Zend_Controller_Router_Route

Zend_Controller_Router_Route_Static

Zend_Controller_Router_Route_Regex

Zend_Controller_Router_Route_Hostname

Zend_Controller_Router_Route_Module

Zend_Controller_Router_Route_Chain

Zend_Controller_Router_Route

Zend_Controller_Router_Route是标准的框架路由。它结合了灵活路由定义的易用性。每个路由包含了基本的URL映射(静态的和动态的部分(变量))并且可以被缺省地初始化,也可以根据不同的要求初始化。

让我们想象一下我们假设的应用程序将需要一些广域内容作者的信息页面。我们想能够把浏览器指向http://domain.com/author/martel去看一个叫"martel"的信息。有这样功能的路由看起来是这样的:

  1. $route = new Zend_Controller_Router_Route( 
  2.   'author/:username', 
  3.   array( 
  4.     'controller' => 'profile', 
  5.     'action'   => 'userinfo' 
  6.   ) 
  7. ); 
  8. $router->addRoute('user', $route); 

在Zend_Controller_Router_Route里构造函数的第一个参数是路由的定义,它将匹配一个URL,路由定义包含静态的和动态部分,它们由正斜杠('/')符分开,静态部分只是简单的字符:author。动态部分,被叫做变量,用预设的冒号来标记变量名::username。

Note: 字符的的用法

当前的实现允许你使用任何字符(正斜杠除外)作为变量标识符,但强烈建议只使用PHP使用的变量标识符。将来的实现也许会改变这个行为,它可能会导致在你的代码里有隐藏的bugs。

当你把浏览器指向http://domain.com/author/martel这个例子的路由应该被匹配,它所有的变量将被注入到Zend_Controller_Request对象并在ProfileController可访问。由这个例子返回的变量可能会被表示为如下键和值配对的数组:

  1. $values = array( 
  2.   'username'  => 'martel', 
  3.   'controller' => 'profile', 
  4.   'action'   => 'userinfo' 
  5. ); 

稍后,基于这些值,Zend_Controller_Dispatcher_Standard应该调用ProfileController类(在缺省模块中)中的userinfoAction()方法,你将依靠Zend_Controller_Action::_getParam()或者Zend_Controller_Request::getParam()方法能够访问所有的变量:

  1. public function userinfoAction() 
  2. { 
  3.   $request = $this->getRequest(); 
  4.   $username = $request->getParam('username'); 
  5.   $username = $this->_getParam('username'); 
  6. } 

路由定义可以包一个额外的特别字符-通配符-表示为'*'号,它被用来取得参数,和缺省模块路由类似(在URI中定义的var=>value),下面的路由多多少少地模仿了模块路由的行为:

  1. $route = new Zend_Controller_Router_Route( 
  2.   ':module/:controller/:action/*', 
  3.   array('module' => 'default') 
  4. ); 
  5. $router->addRoute('default', $route); 

变量缺省

在路由中每个变量可以有一个缺省值,这就是Zend_Controller_Router_Route中构造函数使用的第二个变量,这个参数是一个数组,在数组中键表示变量名,值就是期望的缺省值:

  1. $route = new Zend_Controller_Router_Route( 
  2.   'archive/:year', 
  3.   array('year' => 2006) 
  4. ); 
  5. $router->addRoute('archive', $route); 

上述路由将匹配象http://domain.com/archive/2005和http://example.com/archive的URLs,对于后者变量year将有一个初始的缺省值为2006。

这个例子将导致注入一个year变量给请求对象,应为没有路由信息出现(没有控制器和动作参数被定义),应用程序将被派遣给缺省的控制器和动作方法(它们都在Zend_Controller_Dispatcher_Abstract被定义),为使它更可用,你必须提供一个有效的控制器和动作作为路由的缺省值:

  1. $route = new Zend_Controller_Router_Route( 
  2.   'archive/:year', 
  3.   array( 
  4.     'year'    => 2006, 
  5.     'controller' => 'archive', 
  6.     'action'   => 'show' 
  7.   ) 
  8. ); 
  9. $router->addRoute('archive', $route); 

这个路由将导致派遣给ArchiveController类的showAction()方法。

变量请求

当变量请求被设定,第三个参数可以加给Zend_Controller_Router_Route的构造函数,这些被定义为正则表达式的一部分:

  1. $route = new Zend_Controller_Router_Route( 
  2.   'archive/:year', 
  3.   array( 
  4.     'year'    => 2006, 
  5.     'controller' => 'archive', 
  6.     'action'   => 'show' 
  7.   ), 
  8.   array('year' => '\d+') 
  9. ); 
  10. $router->addRoute('archive', $route); 

用上述定义的路由,路由器仅当year变量包含数字数据将匹配它,例如http://domain.com/archive/2345。象http://example.com/archive/test这样的URL将不被匹配并且控制将被传递给在链中的下一个路由。

主机名路由

你也可以使用主机名做路由匹配,对简单的匹配使用静态主机名选项:

  1. $route = new Zend_Controller_Router_Route( 
  2.   array( 
  3.     'host' => 'blog.mysite.com', 
  4.     'path' => 'archive' 
  5.   ), 
  6.   array( 
  7.     'module'   => 'blog', 
  8.     'controller' => 'archive', 
  9.     'action'   => 'index' 
  10.   ) 
  11. ); 
  12. $router->addRoute('archive', $route); 

如果你想匹配参数在主机名里,使用 regex 选项。在下面例子中,子域为动作控制器被用作用户名参数。 当组装路由时,你可以给出用户名为参数,就像你用其它路径参数一样:

  1. $route = new Zend_Controller_Router_Route( 
  2.   array( 
  3.     'host' => array( 
  4.       'regex'  => '([a-z]+).mysite.com', 
  5.       'reverse' => '%s.mysite.com' 
  6.       'params' => array( 
  7.         1 => 'username' 
  8.       ) 
  9.     ), 
  10.     'path' => '' 
  11.   ), 
  12.   array( 
  13.     'module'   => 'users', 
  14.     'controller' => 'profile', 
  15.     'action'   => 'index' 
  16.   ) 
  17. ); 
  18. $router->addRoute('profile', $route); 
  19. Zend_Controller_Router_Route_Static 

设置固定不变的路由:

  1. $route = new Zend_Controller_Router_Route_Static( 
  2.   'login', 
  3.   array('controller' => 'auth', 'action' => 'login') 
  4. ); 
  5. $router->addRoute('login', $route); 

上面的路由将匹配http://domain.com/login的URL,并分派到 AuthController::loginAction().

Zend_Controller_Router_Route_Regex

除了缺省的和静态的路由类型外,正则表达式路由类型也可用。这个路由比其它路由更强更灵活,只是稍微有点复杂。同时,它应该比标准路由快。

象标准路由一样,这个路由必须用路由定义和一些缺省条件来初始化。让我们创建一个archive路由作为例子,和先前定义的类似,这次只是用了Regex:

  1. $route = new Zend_Controller_Router_Route_Regex( 
  2.   'archive/(\d+)', 
  3.   array( 
  4.     'controller' => 'archive', 
  5.     'action'   => 'show' 
  6.   ) 
  7. ); 
  8. $router->addRoute('archive', $route); 

每个定义的regex子模式将被注入到请求对象里,同上述的例子,再成功匹配http://domain.com/archive/2006之后,结果值的数组看起来象这样:

  1. $values = array( 
  2.   1      => '2006', 
  3.   'controller' => 'archive', 
  4.   'action'   => 'show' 
  5. ); 

Note: 在匹配之前,开头和结尾的斜杠从路由器里的URL中去除掉了,结果,匹配http://domain.com/foo/bar/,需要foo/bar这样的regex,而不是/foo/bar。

Note: 行开头和行结尾符号(分别为'^' 和 '$')被自动预先追加到所有表达式,这样,你不需要在你的正则表达式里用它们,你应该匹配整个字符串。

Note: 这个路由类使用#符作为分隔符。这意味着你将需要避免哈希符('#')但不是正斜杠('/')在你的路由定义里。因为'#'符(名称为锚)很少被传给webserver,你将几乎不需要在你的regex里使用它。

你可以用通常的办法获得已定义的子模式的内容:

  1. public function showAction() 
  2. { 
  3.   $request = $this->getRequest(); 
  4.   $year  = $request->getParam(1); // $year = '2006'; 
  5. } 

Note: 注意这个键是整数(1) 而不是字符串('1')。

因为'year'的缺省没有设置,这个路由将和它的标准路由副本不是非常精确地相同。即使我们为'year'声明一个缺省并使子模式可选,也不清楚是否会在拖尾斜杠(trailing slash)上还将有问题。方案是使整个'year'部分和斜杠一起可选但只抓取数字部分:(这段比较绕口,请校对者仔细看看,谢谢 Jason Qi)

  1. $route = new Zend_Controller_Router_Route_Regex( 
  2.   'archive(?:/(\d+))?', 
  3.   array( 
  4.     1      => '2006', 
  5.     'controller' => 'archive', 
  6.     'action'   => 'show' 
  7.   ) 
  8. ); 
  9. $router->addRoute('archive', $route); 

让我们看看你可能注意到的问题,给参数使用基于整数的键不是容易管理的办法,今后可能会有问题,这就是为什么有第三个参数,这是个联合数组表示一个regex子模式到参数名键的映射,我们来看看一个简单的例子:

  1. $route = new Zend_Controller_Router_Route_Regex( 
  2.   'archive/(\d+)', 
  3.   array( 
  4.     'controller' => 'archive', 
  5.     'action' => 'show' 
  6.   ), 
  7.   array( 
  8.     1 => 'year' 
  9.   ) 
  10. ); 
  11. $router->addRoute('archive', $route); 

这将导致下面的值被注入到请求:

  1. $values = array( 
  2.   'year'    => '2006', 
  3.   'controller' => 'archive', 
  4.   'action'   => 'show' 
  5. ); 

这个映射被任何目录来定义使它能工作于任何环境,键可以包含变量名或子模式索引:

  1. $route = new Zend_Controller_Router_Route_Regex( 
  2.   'archive/(\d+)', 
  3.   array( ... ), 
  4.   array(1 => 'year') 
  5. ); 
  6. // OR 
  7. $route = new Zend_Controller_Router_Route_Regex( 
  8.   'archive/(\d+)', 
  9.   array( ... ), 
  10.   array('year' => 1) 
  11. ); 

Note: 子模式键必须用整数表示。

注意在请求值中的数字索引不见了,代替的是一个名字变量,当然如果你愿意可以把数字和名字变量混合使用:

  1. $route = new Zend_Controller_Router_Route_Regex( 
  2.   'archive/(\d+)/page/(\d+)', 
  3.   array( ... ), 
  4.   array('year' => 1) 
  5. ); 

这将导致在请求中有混合的值可用,例如:URLhttp://domain.com/archive/2006/page/10将在下列结果中:

  1. $values = array( 
  2.   'year'    => '2006', 
  3.   2      => 10, 
  4.   'controller' => 'archive', 
  5.   'action'   => 'show' 
  6. ); 

因为regex模型不容易颠倒,如果你想用URL助手或这个类中的 assemble方法,你需要准备一个颠倒的URL,这个颠倒的路径用可由sprintf()解析的字符串来表示并定义为第四个构造参数:

  1. $route = new Zend_Controller_Router_Route_Regex( 
  2.   'archive/(\d+)', 
  3.   array( ... ), 
  4.   array('year' => 1), 
  5.   'archive/%s' 
  6. ); 

所有这些都已经可能由标准路由对象完成,那么使用Regex路由的好处在哪里?首先,它允许你不受限制地描述任何类型的URL。想象一下你有一个博客并希望创建象http://domain.com/blog/archive/01-Using_the_Regex_Router.html这样的URLs,还有把解析它路径元素中的最后部分,01-Using_the_Regex_Router.html,到一个文章的ID和文章的标题/描述;这不可能由标准路由完成。用Regex路由,你可以做象下面的方案:

  1. $route = new Zend_Controller_Router_Route_Regex( 
  2.   'blog/archive/(\d+)-(.+)\.html', 
  3.   array( 
  4.     'controller' => 'blog', 
  5.     'action'   => 'view' 
  6.   ), 
  7.   array( 
  8.     1 => 'id', 
  9.     2 => 'description' 
  10.   ), 
  11.   'blog/archive/%d-%s.html' 
  12. ); 
  13. $router->addRoute('blogArchive', $route); 

正如你所看到的,这个在标准路由上添加了巨大的灵活性。

通过配置文件定义路由规则

例如:

  1. [production] 
  2. routes.archive.route = "archive/:year/*" 
  3. routes.archive.defaults.controller = archive 
  4. routes.archive.defaults.action = show 
  5. routes.archive.defaults.year = 2000 
  6. routes.archive.reqs.year = "\d+" 
  7. routes.news.type = "Zend_Controller_Router_Route_Static" 
  8. routes.news.route = "news" 
  9. routes.news.defaults.controller = "news" 
  10. routes.news.defaults.action = "list" 
  11. routes.archive.type = "Zend_Controller_Router_Route_Regex" 
  12. routes.archive.route = "archive/(\d+)" 
  13. routes.archive.defaults.controller = "archive" 
  14. routes.archive.defaults.action = "show" 
  15. routes.archive.map.1 = "year" 
  16. ; OR: routes.archive.map.year = 1 

上述的INI文件可以被读进Zend_Config对象:

$config = new Zend_Config_Ini('/path/to/config.ini', 'production');

$router = new Zend_Controller_Router_Rewrite();

$router->addConfig($config, 'routes');

在上面的例子中,我们告诉路由器去使用INI文件'routes'一节给它的路由。每个在这个节下的顶级键将用来定义路由名;上述例子定义了路由'archive'和'news'。每个路由接着要求,至少,一个'route'条目和一个或更多'defaults'条目;可选地,一个或更多'reqs'('required'的简写)可能要求提供。总之,这些相对应的三个参数提供给Zend_Controller_Router_Route_Interface对象。一个选项键,'type',可用来指定路由类的类型给特殊的路由;缺省地,它使用Zend_Controller_Router_Route。在上述例子中,'news'路由被定义来使用Zend_Controller_Router_Route_Static。

自定义路由类型

标准的rewrite路由器应当最大限度提供你所需的功能;大多时候,为了通过已知的路由提供新的或修改的功能,你将只需要创建一个新的路由类型

那就是说,你可能想要用不同的路由范例,接口Zend_Controller_Router_Interface提供了需要最少的信息来创建路由器,并包含一个单个的方法。

  1. interface Zend_Controller_Router_Interface 
  2. { 
  3.  /** 
  4.   * @param Zend_Controller_Request_Abstract $request 
  5.   * @throws Zend_Controller_Router_Exception 
  6.   * @return Zend_Controller_Request_Abstract 
  7.   */ 
  8.  public function route(Zend_Controller_Request_Abstract $request); 
  9. } 

路由只发生一次:当请求第一次接收到系统,路由器的意图是基于请求的环境决定控制器、动作和可选的参数,并把它们发给请求,请求对象接着传递给派遣器,如果不可能映射一个路由到一个派遣令牌,路由器对请求对象就什么也不做。

Tags: Zend_Controller_Router

分享到: