/vendor/cakephp/cakephp/src/Routing/RouteCollection.php

https://gitlab.com/vannh/portal_training · PHP · 305 lines · 172 code · 27 blank · 106 comment · 23 complexity · fbc68fbbffd91074b464ff51db048e0c MD5 · raw file

  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Routing;
  16. use Cake\Routing\Exception\MissingRouteException;
  17. use Cake\Routing\Route\Route;
  18. /**
  19. * Contains a collection of routes.
  20. *
  21. * Provides an interface for adding/removing routes
  22. * and parsing/generating URLs with the routes it contains.
  23. *
  24. * @internal
  25. */
  26. class RouteCollection
  27. {
  28. /**
  29. * The routes connected to this collection.
  30. *
  31. * @var array
  32. */
  33. protected $_routeTable = [];
  34. /**
  35. * The routes connected to this collection.
  36. *
  37. * @var array
  38. */
  39. protected $_routes = [];
  40. /**
  41. * The hash map of named routes that are in this collection.
  42. *
  43. * @var array
  44. */
  45. protected $_named = [];
  46. /**
  47. * Routes indexed by path prefix.
  48. *
  49. * @var array
  50. */
  51. protected $_paths = [];
  52. /**
  53. * Route extensions
  54. *
  55. * @var array
  56. */
  57. protected $_extensions = [];
  58. /**
  59. * Add a route to the collection.
  60. *
  61. * @param \Cake\Routing\Route\Route $route The route object to add.
  62. * @param array $options Additional options for the route. Primarily for the
  63. * `_name` option, which enables named routes.
  64. * @return void
  65. */
  66. public function add(Route $route, array $options = [])
  67. {
  68. $this->_routes[] = $route;
  69. // Explicit names
  70. if (isset($options['_name'])) {
  71. $this->_named[$options['_name']] = $route;
  72. }
  73. // Generated names.
  74. $name = $route->getName();
  75. if (!isset($this->_routeTable[$name])) {
  76. $this->_routeTable[$name] = [];
  77. }
  78. $this->_routeTable[$name][] = $route;
  79. // Index path prefixes (for parsing)
  80. $path = $route->staticPath();
  81. if (empty($this->_paths[$path])) {
  82. $this->_paths[$path] = [];
  83. krsort($this->_paths);
  84. }
  85. $this->_paths[$path][] = $route;
  86. $extensions = $route->extensions();
  87. if ($extensions) {
  88. $this->extensions($extensions);
  89. }
  90. }
  91. /**
  92. * Takes the URL string and iterates the routes until one is able to parse the route.
  93. *
  94. * @param string $url URL to parse.
  95. * @return array An array of request parameters parsed from the URL.
  96. * @throws \Cake\Routing\Exception\MissingRouteException When a URL has no matching route.
  97. */
  98. public function parse($url)
  99. {
  100. foreach (array_keys($this->_paths) as $path) {
  101. if (strpos($url, $path) !== 0) {
  102. continue;
  103. }
  104. $queryParameters = null;
  105. if (strpos($url, '?') !== false) {
  106. list($url, $queryParameters) = explode('?', $url, 2);
  107. parse_str($queryParameters, $queryParameters);
  108. }
  109. foreach ($this->_paths[$path] as $route) {
  110. $r = $route->parse($url);
  111. if ($r === false) {
  112. continue;
  113. }
  114. if ($queryParameters) {
  115. $r['?'] = $queryParameters;
  116. }
  117. return $r;
  118. }
  119. }
  120. throw new MissingRouteException(['url' => $url]);
  121. }
  122. /**
  123. * Get the set of names from the $url. Accepts both older style array urls,
  124. * and newer style urls containing '_name'
  125. *
  126. * @param array $url The url to match.
  127. * @return array The set of names of the url
  128. */
  129. protected function _getNames($url)
  130. {
  131. $plugin = false;
  132. if (isset($url['plugin']) && $url['plugin'] !== false) {
  133. $plugin = strtolower($url['plugin']);
  134. }
  135. $prefix = false;
  136. if (isset($url['prefix']) && $url['prefix'] !== false) {
  137. $prefix = strtolower($url['prefix']);
  138. }
  139. $controller = strtolower($url['controller']);
  140. $action = strtolower($url['action']);
  141. $names = [
  142. "${controller}:${action}",
  143. "${controller}:_action",
  144. "_controller:${action}",
  145. "_controller:_action"
  146. ];
  147. // No prefix, no plugin
  148. if ($prefix === false && $plugin === false) {
  149. return $names;
  150. }
  151. // Only a plugin
  152. if ($prefix === false) {
  153. return [
  154. "${plugin}.${controller}:${action}",
  155. "${plugin}.${controller}:_action",
  156. "${plugin}._controller:${action}",
  157. "${plugin}._controller:_action",
  158. "_plugin.${controller}:${action}",
  159. "_plugin.${controller}:_action",
  160. "_plugin._controller:${action}",
  161. "_plugin._controller:_action",
  162. ];
  163. }
  164. // Only a prefix
  165. if ($plugin === false) {
  166. return [
  167. "${prefix}:${controller}:${action}",
  168. "${prefix}:${controller}:_action",
  169. "${prefix}:_controller:${action}",
  170. "${prefix}:_controller:_action",
  171. "_prefix:${controller}:${action}",
  172. "_prefix:${controller}:_action",
  173. "_prefix:_controller:${action}",
  174. "_prefix:_controller:_action"
  175. ];
  176. }
  177. // Prefix and plugin has the most options
  178. // as there are 4 factors.
  179. return [
  180. "${prefix}:${plugin}.${controller}:${action}",
  181. "${prefix}:${plugin}.${controller}:_action",
  182. "${prefix}:${plugin}._controller:${action}",
  183. "${prefix}:${plugin}._controller:_action",
  184. "${prefix}:_plugin.${controller}:${action}",
  185. "${prefix}:_plugin.${controller}:_action",
  186. "${prefix}:_plugin._controller:${action}",
  187. "${prefix}:_plugin._controller:_action",
  188. "_prefix:${plugin}.${controller}:${action}",
  189. "_prefix:${plugin}.${controller}:_action",
  190. "_prefix:${plugin}._controller:${action}",
  191. "_prefix:${plugin}._controller:_action",
  192. "_prefix:_plugin.${controller}:${action}",
  193. "_prefix:_plugin.${controller}:_action",
  194. "_prefix:_plugin._controller:${action}",
  195. "_prefix:_plugin._controller:_action",
  196. ];
  197. }
  198. /**
  199. * Reverse route or match a $url array with the defined routes.
  200. * Returns either the string URL generate by the route, or false on failure.
  201. *
  202. * @param array $url The url to match.
  203. * @param array $context The request context to use. Contains _base, _port,
  204. * _host, _scheme and params keys.
  205. * @return string|false Either a string on match, or false on failure.
  206. * @throws \Cake\Routing\Exception\MissingRouteException when a route cannot be matched.
  207. */
  208. public function match($url, $context)
  209. {
  210. // Named routes support optimization.
  211. if (isset($url['_name'])) {
  212. $name = $url['_name'];
  213. unset($url['_name']);
  214. $out = false;
  215. if (isset($this->_named[$name])) {
  216. $route = $this->_named[$name];
  217. $out = $route->match($url + $route->defaults, $context);
  218. }
  219. if ($out) {
  220. return $out;
  221. }
  222. throw new MissingRouteException(['url' => $name, 'context' => $context]);
  223. }
  224. foreach ($this->_getNames($url) as $name) {
  225. if (empty($this->_routeTable[$name])) {
  226. continue;
  227. }
  228. foreach ($this->_routeTable[$name] as $route) {
  229. $match = $route->match($url, $context);
  230. if ($match) {
  231. return strlen($match) > 1 ? trim($match, '/') : $match;
  232. }
  233. }
  234. }
  235. throw new MissingRouteException(['url' => var_export($url, true), 'context' => $context]);
  236. }
  237. /**
  238. * Get all the connected routes as a flat list.
  239. *
  240. * @return array
  241. */
  242. public function routes()
  243. {
  244. return $this->_routes;
  245. }
  246. /**
  247. * Get the connected named routes.
  248. *
  249. * @return array
  250. */
  251. public function named()
  252. {
  253. return $this->_named;
  254. }
  255. /**
  256. * Get/set the extensions that the route collection could handle.
  257. *
  258. * @param null|string|array $extensions Either the list of extensions to set,
  259. * or null to get.
  260. * @param bool $merge Whether to merge with or override existing extensions.
  261. * Defaults to `true`.
  262. * @return array The valid extensions.
  263. */
  264. public function extensions($extensions = null, $merge = true)
  265. {
  266. if ($extensions === null) {
  267. return $this->_extensions;
  268. }
  269. $extensions = (array)$extensions;
  270. if ($merge) {
  271. $extensions = array_unique(array_merge(
  272. $this->_extensions,
  273. $extensions
  274. ));
  275. }
  276. return $this->_extensions = $extensions;
  277. }
  278. }