File indexing completed on 2024-12-22 05:36:34
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Controller 0017 * @subpackage Router 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @version $Id$ 0020 * @license http://framework.zend.com/license/new-bsd New BSD License 0021 */ 0022 0023 /** Zend_Controller_Router_Abstract */ 0024 // require_once 'Zend/Controller/Router/Abstract.php'; 0025 0026 /** Zend_Controller_Router_Route */ 0027 // require_once 'Zend/Controller/Router/Route.php'; 0028 0029 /** 0030 * Ruby routing based Router. 0031 * 0032 * @package Zend_Controller 0033 * @subpackage Router 0034 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0035 * @license http://framework.zend.com/license/new-bsd New BSD License 0036 * @see http://manuals.rubyonrails.com/read/chapter/65 0037 */ 0038 class Zend_Controller_Router_Rewrite extends Zend_Controller_Router_Abstract 0039 { 0040 0041 /** 0042 * Whether or not to use default routes 0043 * 0044 * @var boolean 0045 */ 0046 protected $_useDefaultRoutes = true; 0047 0048 /** 0049 * Array of routes to match against 0050 * 0051 * @var array 0052 */ 0053 protected $_routes = array(); 0054 0055 /** 0056 * Currently matched route 0057 * 0058 * @var string 0059 */ 0060 protected $_currentRoute = null; 0061 0062 /** 0063 * Global parameters given to all routes 0064 * 0065 * @var array 0066 */ 0067 protected $_globalParams = array(); 0068 0069 /** 0070 * Separator to use with chain names 0071 * 0072 * @var string 0073 */ 0074 protected $_chainNameSeparator = '-'; 0075 0076 /** 0077 * Determines if request parameters should be used as global parameters 0078 * inside this router. 0079 * 0080 * @var boolean 0081 */ 0082 protected $_useCurrentParamsAsGlobal = false; 0083 0084 /** 0085 * Add default routes which are used to mimic basic router behaviour 0086 * 0087 * @return Zend_Controller_Router_Rewrite 0088 */ 0089 public function addDefaultRoutes() 0090 { 0091 if (!$this->hasRoute('default')) { 0092 $dispatcher = $this->getFrontController()->getDispatcher(); 0093 $request = $this->getFrontController()->getRequest(); 0094 0095 // require_once 'Zend/Controller/Router/Route/Module.php'; 0096 $compat = new Zend_Controller_Router_Route_Module(array(), $dispatcher, $request); 0097 0098 $this->_routes = array('default' => $compat) + $this->_routes; 0099 } 0100 0101 return $this; 0102 } 0103 0104 /** 0105 * Add route to the route chain 0106 * 0107 * If route contains method setRequest(), it is initialized with a request object 0108 * 0109 * @param string $name Name of the route 0110 * @param Zend_Controller_Router_Route_Interface $route Instance of the route 0111 * @return Zend_Controller_Router_Rewrite 0112 */ 0113 public function addRoute($name, Zend_Controller_Router_Route_Interface $route) 0114 { 0115 if (method_exists($route, 'setRequest')) { 0116 $route->setRequest($this->getFrontController()->getRequest()); 0117 } 0118 0119 $this->_routes[$name] = $route; 0120 0121 return $this; 0122 } 0123 0124 /** 0125 * Add routes to the route chain 0126 * 0127 * @param array $routes Array of routes with names as keys and routes as values 0128 * @return Zend_Controller_Router_Rewrite 0129 */ 0130 public function addRoutes($routes) 0131 { 0132 foreach ($routes as $name => $route) { 0133 $this->addRoute($name, $route); 0134 } 0135 0136 return $this; 0137 } 0138 0139 /** 0140 * Create routes out of Zend_Config configuration 0141 * 0142 * Example INI: 0143 * routes.archive.route = "archive/:year/*" 0144 * routes.archive.defaults.controller = archive 0145 * routes.archive.defaults.action = show 0146 * routes.archive.defaults.year = 2000 0147 * routes.archive.reqs.year = "\d+" 0148 * 0149 * routes.news.type = "Zend_Controller_Router_Route_Static" 0150 * routes.news.route = "news" 0151 * routes.news.defaults.controller = "news" 0152 * routes.news.defaults.action = "list" 0153 * 0154 * And finally after you have created a Zend_Config with above ini: 0155 * $router = new Zend_Controller_Router_Rewrite(); 0156 * $router->addConfig($config, 'routes'); 0157 * 0158 * @param Zend_Config $config Configuration object 0159 * @param string $section Name of the config section containing route's definitions 0160 * @throws Zend_Controller_Router_Exception 0161 * @return Zend_Controller_Router_Rewrite 0162 */ 0163 public function addConfig(Zend_Config $config, $section = null) 0164 { 0165 if ($section !== null) { 0166 if ($config->{$section} === null) { 0167 // require_once 'Zend/Controller/Router/Exception.php'; 0168 throw new Zend_Controller_Router_Exception("No route configuration in section '{$section}'"); 0169 } 0170 0171 $config = $config->{$section}; 0172 } 0173 0174 foreach ($config as $name => $info) { 0175 $route = $this->_getRouteFromConfig($info); 0176 0177 if ($route instanceof Zend_Controller_Router_Route_Chain) { 0178 if (!isset($info->chain)) { 0179 // require_once 'Zend/Controller/Router/Exception.php'; 0180 throw new Zend_Controller_Router_Exception("No chain defined"); 0181 } 0182 0183 if ($info->chain instanceof Zend_Config) { 0184 $childRouteNames = $info->chain; 0185 } else { 0186 $childRouteNames = explode(',', $info->chain); 0187 } 0188 0189 foreach ($childRouteNames as $childRouteName) { 0190 $childRoute = $this->getRoute(trim($childRouteName)); 0191 $route->chain($childRoute); 0192 } 0193 0194 $this->addRoute($name, $route); 0195 } elseif (isset($info->chains) && $info->chains instanceof Zend_Config) { 0196 $this->_addChainRoutesFromConfig($name, $route, $info->chains); 0197 } else { 0198 $this->addRoute($name, $route); 0199 } 0200 } 0201 0202 return $this; 0203 } 0204 0205 /** 0206 * Get a route frm a config instance 0207 * 0208 * @param Zend_Config $info 0209 * @return Zend_Controller_Router_Route_Interface 0210 */ 0211 protected function _getRouteFromConfig(Zend_Config $info) 0212 { 0213 $class = (isset($info->type)) ? $info->type : 'Zend_Controller_Router_Route'; 0214 if (!class_exists($class)) { 0215 // require_once 'Zend/Loader.php'; 0216 Zend_Loader::loadClass($class); 0217 } 0218 0219 $route = call_user_func( 0220 array( 0221 $class, 0222 'getInstance' 0223 ), $info 0224 ); 0225 0226 if (isset($info->abstract) && $info->abstract && method_exists($route, 'isAbstract')) { 0227 $route->isAbstract(true); 0228 } 0229 0230 return $route; 0231 } 0232 0233 /** 0234 * Add chain routes from a config route 0235 * 0236 * @param string $name 0237 * @param Zend_Controller_Router_Route_Interface $route 0238 * @param Zend_Config $childRoutesInfo 0239 * @return void 0240 */ 0241 protected function _addChainRoutesFromConfig( 0242 $name, 0243 Zend_Controller_Router_Route_Interface $route, 0244 Zend_Config $childRoutesInfo 0245 ) 0246 { 0247 foreach ($childRoutesInfo as $childRouteName => $childRouteInfo) { 0248 if (is_string($childRouteInfo)) { 0249 $childRouteName = $childRouteInfo; 0250 $childRoute = $this->getRoute($childRouteName); 0251 } else { 0252 $childRoute = $this->_getRouteFromConfig($childRouteInfo); 0253 } 0254 0255 if ($route instanceof Zend_Controller_Router_Route_Chain) { 0256 $chainRoute = clone $route; 0257 $chainRoute->chain($childRoute); 0258 } else { 0259 $chainRoute = $route->chain($childRoute); 0260 } 0261 0262 $chainName = $name . $this->_chainNameSeparator . $childRouteName; 0263 0264 if (isset($childRouteInfo->chains)) { 0265 $this->_addChainRoutesFromConfig($chainName, $chainRoute, $childRouteInfo->chains); 0266 } else { 0267 $this->addRoute($chainName, $chainRoute); 0268 } 0269 } 0270 } 0271 0272 /** 0273 * Remove a route from the route chain 0274 * 0275 * @param string $name Name of the route 0276 * @throws Zend_Controller_Router_Exception 0277 * @return Zend_Controller_Router_Rewrite 0278 */ 0279 public function removeRoute($name) 0280 { 0281 if (!isset($this->_routes[$name])) { 0282 // require_once 'Zend/Controller/Router/Exception.php'; 0283 throw new Zend_Controller_Router_Exception("Route $name is not defined"); 0284 } 0285 0286 unset($this->_routes[$name]); 0287 0288 return $this; 0289 } 0290 0291 /** 0292 * Remove all standard default routes 0293 * 0294 * @return Zend_Controller_Router_Rewrite 0295 */ 0296 public function removeDefaultRoutes() 0297 { 0298 $this->_useDefaultRoutes = false; 0299 0300 return $this; 0301 } 0302 0303 /** 0304 * Check if named route exists 0305 * 0306 * @param string $name Name of the route 0307 * @return boolean 0308 */ 0309 public function hasRoute($name) 0310 { 0311 return isset($this->_routes[$name]); 0312 } 0313 0314 /** 0315 * Retrieve a named route 0316 * 0317 * @param string $name Name of the route 0318 * @throws Zend_Controller_Router_Exception 0319 * @return Zend_Controller_Router_Route_Interface Route object 0320 */ 0321 public function getRoute($name) 0322 { 0323 if (!isset($this->_routes[$name])) { 0324 // require_once 'Zend/Controller/Router/Exception.php'; 0325 throw new Zend_Controller_Router_Exception("Route $name is not defined"); 0326 } 0327 0328 return $this->_routes[$name]; 0329 } 0330 0331 /** 0332 * Retrieve a currently matched route 0333 * 0334 * @throws Zend_Controller_Router_Exception 0335 * @return Zend_Controller_Router_Route_Interface Route object 0336 */ 0337 public function getCurrentRoute() 0338 { 0339 if (!isset($this->_currentRoute)) { 0340 // require_once 'Zend/Controller/Router/Exception.php'; 0341 throw new Zend_Controller_Router_Exception("Current route is not defined"); 0342 } 0343 0344 return $this->getRoute($this->_currentRoute); 0345 } 0346 0347 /** 0348 * Retrieve a name of currently matched route 0349 * 0350 * @throws Zend_Controller_Router_Exception 0351 * @return string Route name 0352 */ 0353 public function getCurrentRouteName() 0354 { 0355 if (!isset($this->_currentRoute)) { 0356 // require_once 'Zend/Controller/Router/Exception.php'; 0357 throw new Zend_Controller_Router_Exception("Current route is not defined"); 0358 } 0359 0360 return $this->_currentRoute; 0361 } 0362 0363 /** 0364 * Retrieve an array of routes added to the route chain 0365 * 0366 * @return array All of the defined routes 0367 */ 0368 public function getRoutes() 0369 { 0370 return $this->_routes; 0371 } 0372 0373 /** 0374 * Find a matching route to the current PATH_INFO and inject 0375 * returning values to the Request object. 0376 * 0377 * @param Zend_Controller_Request_Abstract $request 0378 * @throws Zend_Controller_Router_Exception 0379 * @return Zend_Controller_Request_Abstract Request object 0380 */ 0381 public function route(Zend_Controller_Request_Abstract $request) 0382 { 0383 if (!$request instanceof Zend_Controller_Request_Http) { 0384 // require_once 'Zend/Controller/Router/Exception.php'; 0385 throw new Zend_Controller_Router_Exception( 0386 'Zend_Controller_Router_Rewrite requires a Zend_Controller_Request_Http-based request object' 0387 ); 0388 } 0389 0390 if ($this->_useDefaultRoutes) { 0391 $this->addDefaultRoutes(); 0392 } 0393 0394 // Find the matching route 0395 $routeMatched = false; 0396 0397 foreach (array_reverse($this->_routes, true) as $name => $route) { 0398 // TODO: Should be an interface method. Hack for 1.0 BC 0399 if (method_exists($route, 'isAbstract') && $route->isAbstract()) { 0400 continue; 0401 } 0402 0403 // TODO: Should be an interface method. Hack for 1.0 BC 0404 if (!method_exists($route, 'getVersion') || $route->getVersion() == 1) { 0405 $match = $request->getPathInfo(); 0406 } else { 0407 $match = $request; 0408 } 0409 0410 if ($params = $route->match($match)) { 0411 $this->_setRequestParams($request, $params); 0412 $this->_currentRoute = $name; 0413 $routeMatched = true; 0414 break; 0415 } 0416 } 0417 0418 if (!$routeMatched) { 0419 // require_once 'Zend/Controller/Router/Exception.php'; 0420 throw new Zend_Controller_Router_Exception('No route matched the request', 404); 0421 } 0422 0423 if ($this->_useCurrentParamsAsGlobal) { 0424 $params = $request->getParams(); 0425 foreach ($params as $param => $value) { 0426 $this->setGlobalParam($param, $value); 0427 } 0428 } 0429 0430 return $request; 0431 } 0432 0433 /** 0434 * Sets parameters for request object 0435 * 0436 * Module name, controller name and action name 0437 * 0438 * @param Zend_Controller_Request_Abstract $request 0439 * @param array $params 0440 */ 0441 protected function _setRequestParams($request, $params) 0442 { 0443 foreach ($params as $param => $value) { 0444 0445 $request->setParam($param, $value); 0446 0447 if ($param === $request->getModuleKey()) { 0448 $request->setModuleName($value); 0449 } 0450 if ($param === $request->getControllerKey()) { 0451 $request->setControllerName($value); 0452 } 0453 if ($param === $request->getActionKey()) { 0454 $request->setActionName($value); 0455 } 0456 } 0457 } 0458 0459 /** 0460 * Generates a URL path that can be used in URL creation, redirection, etc. 0461 * 0462 * @param array $userParams Options passed by a user used to override parameters 0463 * @param mixed $name The name of a Route to use 0464 * @param bool $reset Whether to reset to the route defaults ignoring URL params 0465 * @param bool $encode Tells to encode URL parts on output 0466 * @throws Zend_Controller_Router_Exception 0467 * @return string Resulting absolute URL path 0468 */ 0469 public function assemble($userParams, $name = null, $reset = false, $encode = true) 0470 { 0471 if (!is_array($userParams)) { 0472 // require_once 'Zend/Controller/Router/Exception.php'; 0473 throw new Zend_Controller_Router_Exception('userParams must be an array'); 0474 } 0475 0476 if ($name == null) { 0477 try { 0478 $name = $this->getCurrentRouteName(); 0479 } catch (Zend_Controller_Router_Exception $e) { 0480 $name = 'default'; 0481 } 0482 } 0483 0484 // Use UNION (+) in order to preserve numeric keys 0485 $params = $userParams + $this->_globalParams; 0486 0487 $route = $this->getRoute($name); 0488 $url = $route->assemble($params, $reset, $encode); 0489 0490 if (!preg_match('|^[a-z]+://|', $url)) { 0491 $url = rtrim($this->getFrontController()->getBaseUrl(), self::URI_DELIMITER) . self::URI_DELIMITER . $url; 0492 } 0493 0494 return $url; 0495 } 0496 0497 /** 0498 * Set a global parameter 0499 * 0500 * @param string $name 0501 * @param mixed $value 0502 * @return Zend_Controller_Router_Rewrite 0503 */ 0504 public function setGlobalParam($name, $value) 0505 { 0506 $this->_globalParams[$name] = $value; 0507 0508 return $this; 0509 } 0510 0511 /** 0512 * Set the separator to use with chain names 0513 * 0514 * @param string $separator The separator to use 0515 * @return Zend_Controller_Router_Rewrite 0516 */ 0517 public function setChainNameSeparator($separator) 0518 { 0519 $this->_chainNameSeparator = $separator; 0520 0521 return $this; 0522 } 0523 0524 /** 0525 * Get the separator to use for chain names 0526 * 0527 * @return string 0528 */ 0529 public function getChainNameSeparator() 0530 { 0531 return $this->_chainNameSeparator; 0532 } 0533 0534 /** 0535 * Determines/returns whether to use the request parameters as global parameters. 0536 * 0537 * @param boolean|null $use 0538 * Null/unset when you want to retrieve the current state. 0539 * True when request parameters should be global, false otherwise 0540 * @return boolean|Zend_Controller_Router_Rewrite 0541 * Returns a boolean if first param isn't set, returns an 0542 * instance of Zend_Controller_Router_Rewrite otherwise. 0543 * 0544 */ 0545 public function useRequestParametersAsGlobal($use = null) 0546 { 0547 if ($use === null) { 0548 return $this->_useCurrentParamsAsGlobal; 0549 } 0550 0551 $this->_useCurrentParamsAsGlobal = (bool)$use; 0552 0553 return $this; 0554 } 0555 }