/lib/Cake/Routing/RouteCollection.php

https://github.com/shama/cakephp · PHP · 259 lines · 129 code · 17 blank · 113 comment · 14 complexity · 799f92cdf89160ee9cc75fc226405770 MD5 · raw file

  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * Redistributions of files must retain the above copyright notice.
  8. *
  9. * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  10. * @link http://cakephp.org CakePHP(tm) Project
  11. * @package Cake.Routing
  12. * @since CakePHP(tm) v 3.0.0
  13. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  14. */
  15. namespace Cake\Routing;
  16. use Cake\Network\Request;
  17. use Cake\Routing\Route\Route;
  18. /**
  19. * RouteCollection is used to operate on a set of routes.
  20. * It stores routes both in a linear list in order of connection, as well
  21. * as storing them in a hash-table indexed by a routes' name.
  22. *
  23. * @package Cake.Routing
  24. */
  25. class RouteCollection implements \Countable {
  26. /**
  27. * A hash table of routes indexed by route names.
  28. * Used for reverse routing.
  29. *
  30. * @var array
  31. */
  32. protected $_routeTable = array();
  33. /**
  34. * A list of routes connected, in the order they were connected.
  35. * Used for parsing incoming urls.
  36. *
  37. * @var array
  38. */
  39. protected $_routes = array();
  40. /**
  41. * The top most request's context. Updated whenever
  42. * requests are pushed/popped off the stack in Router.
  43. *
  44. * @var array
  45. */
  46. protected $_requestContext = array(
  47. '_base' => '',
  48. '_port' => 80,
  49. '_scheme' => 'http',
  50. '_host' => 'localhost',
  51. );
  52. /**
  53. * Add a route to the collection.
  54. *
  55. * Appends the route to the list of routes, and the route hashtable.
  56. * @param Cake\Routing\Route\Route $route The route to add
  57. * @return void
  58. */
  59. public function add(Route $route) {
  60. $name = $route->getName();
  61. if (!isset($this->_routeTable[$name])) {
  62. $this->_routeTable[$name] = array();
  63. }
  64. $this->_routeTable[$name][] = $route;
  65. $this->_routes[] = $route;
  66. }
  67. /**
  68. * Reverse route or match a $url array with the defined routes.
  69. * Returns either the string URL generate by the route, or false on failure.
  70. *
  71. * @param array $url The url to match.
  72. * @return void
  73. */
  74. public function match($url) {
  75. $names = $this->_getNames($url);
  76. unset($url['_name']);
  77. foreach ($names as $name) {
  78. if (isset($this->_routeTable[$name])) {
  79. $output = $this->_matchRoutes($this->_routeTable[$name], $url);
  80. if ($output) {
  81. return $output;
  82. }
  83. }
  84. }
  85. return $this->_matchRoutes($this->_routes, $url);
  86. }
  87. /**
  88. * Matches a set of routes with a given $url and $params
  89. *
  90. * @param array $routes An array of routes to match against.
  91. * @param array $url The url to match.
  92. * @return mixed Either false on failure, or a string on success.
  93. */
  94. protected function _matchRoutes($routes, $url) {
  95. $output = false;
  96. for ($i = 0, $len = count($routes); $i < $len; $i++) {
  97. if ($match = $routes[$i]->match($url, $this->_requestContext)) {
  98. $output = trim($match, '/');
  99. break;
  100. }
  101. }
  102. return $output;
  103. }
  104. /**
  105. * Get the set of names from the $url. Accepts both older style array urls,
  106. * and newer style urls containing '_name'
  107. *
  108. * @param array $url The url to match.
  109. * @return string The name of the url
  110. */
  111. protected function _getNames($url) {
  112. $name = false;
  113. if (isset($url['_name'])) {
  114. $name = $url['_name'];
  115. }
  116. $plugin = false;
  117. if (isset($url['plugin'])) {
  118. $plugin = $url['plugin'];
  119. }
  120. $fallbacks = array(
  121. '%2$s:%3$s',
  122. '%2$s:_action',
  123. '_controller:%3$s',
  124. '_controller:_action'
  125. );
  126. if ($plugin) {
  127. $fallbacks = array(
  128. '%1$s.%2$s:%3$s',
  129. '%1$s.%2$s:_action',
  130. '%1$s._controller:%3$s',
  131. '%1$s._controller:_action',
  132. '_plugin._controller:%3$s',
  133. '_plugin._controller:_action',
  134. '_controller:_action',
  135. );
  136. }
  137. foreach ($fallbacks as $i => $template) {
  138. $fallbacks[$i] = sprintf($template, $plugin, $url['controller'], $url['action']);
  139. }
  140. if ($name) {
  141. array_unshift($fallbacks, $name);
  142. }
  143. return $fallbacks;
  144. }
  145. /**
  146. * Takes the URL string and iterates the routes until one is able to parse the route.
  147. *
  148. * @param string $url Url to parse.
  149. * @return array An array of request parameters parsed from the url.
  150. */
  151. public function parse($url) {
  152. $out = array();
  153. for ($i = 0, $len = count($this); $i < $len; $i++) {
  154. $r = $this->_routes[$i]->parse($url);
  155. if ($r !== false) {
  156. return $r;
  157. }
  158. }
  159. return $out;
  160. }
  161. /**
  162. * Promote a route (by default, the last one added) to the beginning of the list.
  163. * Also promotes the route to the head of its named slice in the named route
  164. * table.
  165. *
  166. * @param integer $which A zero-based array index representing
  167. * the route to move. For example,
  168. * if 3 routes have been added, the last route would be 2.
  169. * @return boolean Returns false if no route exists at the position
  170. * specified by $which.
  171. */
  172. public function promote($which) {
  173. if ($which === null) {
  174. $which = count($this->_routes) - 1;
  175. }
  176. if (!isset($this->_routes[$which])) {
  177. return false;
  178. }
  179. $route =& $this->_routes[$which];
  180. unset($this->_routes[$which]);
  181. array_unshift($this->_routes, $route);
  182. $name = $route->getName();
  183. $routes = $this->_routeTable[$name];
  184. $index = array_search($route, $routes, true);
  185. unset($this->_routeTable[$name][$index]);
  186. array_unshift($this->_routeTable[$name], $route);
  187. return true;
  188. }
  189. /**
  190. * Get route(s) out of the collection.
  191. *
  192. * If a string argument is provided, the first matching
  193. * route for the provided name will be returned.
  194. *
  195. * If an integer argument is provided, the route
  196. * with that index will be returned.
  197. *
  198. * @param mixed $index The index or name of the route you want.
  199. * @return mixed Either the route object or null.
  200. */
  201. public function get($index) {
  202. if (is_string($index)) {
  203. $routes = isset($this->_routeTable[$index]) ? $this->_routeTable[$index] : array(null);
  204. return $routes[0];
  205. }
  206. return isset($this->_routes[$index]) ? $this->_routes[$index] : null;
  207. }
  208. /**
  209. * Part of the countable interface.
  210. *
  211. * @return integer The number of connected routes.
  212. */
  213. public function count() {
  214. return count($this->_routes);
  215. }
  216. /**
  217. * Populate the request context used to generate URL's
  218. * Generally set to the last/most recent request.
  219. *
  220. * @param Cake\Network\Request $request
  221. * @return void
  222. */
  223. public function setContext(Request $request) {
  224. $this->_requestContext = array(
  225. '_base' => $request->base,
  226. '_port' => $request->port(),
  227. '_scheme' => $request->scheme(),
  228. '_host' => $request->host()
  229. );
  230. }
  231. /**
  232. * Sets which extensions routes will use.
  233. *
  234. * @param array $extensions The extensions for routes to use.
  235. * @return void
  236. */
  237. public function setExtensions(array $extensions) {
  238. foreach ($this->_routes as $route) {
  239. $route->setExtensions($extensions);
  240. }
  241. }
  242. }