PageRenderTime 23ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/ZendFramework1Mvc/library/Zend/Controller/Router/Rewrite.php

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