PageRenderTime 1926ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/symfony-cmf/routing/ChainRouter.php

https://gitlab.com/geeta7/drupal
PHP | 309 lines | 173 code | 37 blank | 99 comment | 24 complexity | f83de2fd884962bdb1ca568ecbb6bb19 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony CMF package.
  4. *
  5. * (c) 2011-2014 Symfony CMF
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Cmf\Component\Routing;
  11. use Symfony\Component\Routing\RouterInterface;
  12. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  13. use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
  14. use Symfony\Component\Routing\RequestContext;
  15. use Symfony\Component\Routing\RequestContextAwareInterface;
  16. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  17. use Symfony\Component\Routing\Exception\RouteNotFoundException;
  18. use Symfony\Component\Routing\Exception\MethodNotAllowedException;
  19. use Symfony\Component\Routing\RouteCollection;
  20. use Symfony\Component\HttpFoundation\Request;
  21. use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
  22. use Psr\Log\LoggerInterface;
  23. /**
  24. * The ChainRouter allows to combine several routers to try in a defined order.
  25. *
  26. * @author Henrik Bjornskov <henrik@bjrnskov.dk>
  27. * @author Magnus Nordlander <magnus@e-butik.se>
  28. */
  29. class ChainRouter implements ChainRouterInterface, WarmableInterface
  30. {
  31. /**
  32. * @var RequestContext
  33. */
  34. private $context;
  35. /**
  36. * Array of arrays of routers grouped by priority
  37. * @var array
  38. */
  39. private $routers = array();
  40. /**
  41. * @var RouterInterface[] Array of routers, sorted by priority
  42. */
  43. private $sortedRouters;
  44. /**
  45. * @var RouteCollection
  46. */
  47. private $routeCollection;
  48. /**
  49. * @var null|LoggerInterface
  50. */
  51. protected $logger;
  52. /**
  53. * @param LoggerInterface $logger
  54. */
  55. public function __construct(LoggerInterface $logger = null)
  56. {
  57. $this->logger = $logger;
  58. }
  59. /**
  60. * @return RequestContext
  61. */
  62. public function getContext()
  63. {
  64. return $this->context;
  65. }
  66. /**
  67. * {@inheritdoc}
  68. */
  69. public function add($router, $priority = 0)
  70. {
  71. if (!$router instanceof RouterInterface
  72. && !($router instanceof RequestMatcherInterface && $router instanceof UrlGeneratorInterface)
  73. ) {
  74. throw new \InvalidArgumentException(sprintf('%s is not a valid router.', get_class($router)));
  75. }
  76. if (empty($this->routers[$priority])) {
  77. $this->routers[$priority] = array();
  78. }
  79. $this->routers[$priority][] = $router;
  80. $this->sortedRouters = array();
  81. }
  82. /**
  83. * {@inheritdoc}
  84. */
  85. public function all()
  86. {
  87. if (empty($this->sortedRouters)) {
  88. $this->sortedRouters = $this->sortRouters();
  89. // setContext() is done here instead of in add() to avoid fatal errors when clearing and warming up caches
  90. // See https://github.com/symfony-cmf/Routing/pull/18
  91. $context = $this->getContext();
  92. if (null !== $context) {
  93. foreach ($this->sortedRouters as $router) {
  94. if ($router instanceof RequestContextAwareInterface) {
  95. $router->setContext($context);
  96. }
  97. }
  98. }
  99. }
  100. return $this->sortedRouters;
  101. }
  102. /**
  103. * Sort routers by priority.
  104. * The highest priority number is the highest priority (reverse sorting)
  105. *
  106. * @return RouterInterface[]
  107. */
  108. protected function sortRouters()
  109. {
  110. $sortedRouters = array();
  111. krsort($this->routers);
  112. foreach ($this->routers as $routers) {
  113. $sortedRouters = array_merge($sortedRouters, $routers);
  114. }
  115. return $sortedRouters;
  116. }
  117. /**
  118. * {@inheritdoc}
  119. *
  120. * Loops through all routes and tries to match the passed url.
  121. *
  122. * Note: You should use matchRequest if you can.
  123. */
  124. public function match($url)
  125. {
  126. return $this->doMatch($url);
  127. }
  128. /**
  129. * {@inheritdoc}
  130. *
  131. * Loops through all routes and tries to match the passed request.
  132. */
  133. public function matchRequest(Request $request)
  134. {
  135. return $this->doMatch($request->getPathInfo(), $request);
  136. }
  137. /**
  138. * Loops through all routers and tries to match the passed request or url.
  139. *
  140. * At least the url must be provided, if a request is additionally provided
  141. * the request takes precedence.
  142. *
  143. * @param string $url
  144. * @param Request $request
  145. *
  146. * @return array An array of parameters
  147. *
  148. * @throws ResourceNotFoundException If no router matched.
  149. */
  150. private function doMatch($url, Request $request = null)
  151. {
  152. $methodNotAllowed = null;
  153. $requestForMatching = $request;
  154. foreach ($this->all() as $router) {
  155. try {
  156. // the request/url match logic is the same as in Symfony/Component/HttpKernel/EventListener/RouterListener.php
  157. // matching requests is more powerful than matching URLs only, so try that first
  158. if ($router instanceof RequestMatcherInterface) {
  159. if (empty($requestForMatching)) {
  160. $requestForMatching = Request::create($url);
  161. }
  162. return $router->matchRequest($requestForMatching);
  163. }
  164. // every router implements the match method
  165. return $router->match($url);
  166. } catch (ResourceNotFoundException $e) {
  167. if ($this->logger) {
  168. $this->logger->debug('Router '.get_class($router).' was not able to match, message "'.$e->getMessage().'"');
  169. }
  170. // Needs special care
  171. } catch (MethodNotAllowedException $e) {
  172. if ($this->logger) {
  173. $this->logger->debug('Router '.get_class($router).' throws MethodNotAllowedException with message "'.$e->getMessage().'"');
  174. }
  175. $methodNotAllowed = $e;
  176. }
  177. }
  178. $info = $request
  179. ? "this request\n$request"
  180. : "url '$url'";
  181. throw $methodNotAllowed ?: new ResourceNotFoundException("None of the routers in the chain matched $info");
  182. }
  183. /**
  184. * {@inheritdoc}
  185. *
  186. * Loops through all registered routers and returns a router if one is found.
  187. * It will always return the first route generated.
  188. */
  189. public function generate($name, $parameters = array(), $absolute = false)
  190. {
  191. $debug = array();
  192. foreach ($this->all() as $router) {
  193. // if $router does not announce it is capable of handling
  194. // non-string routes and $name is not a string, continue
  195. if ($name && !is_string($name) && !$router instanceof VersatileGeneratorInterface) {
  196. continue;
  197. }
  198. // If $router is versatile and doesn't support this route name, continue
  199. if ($router instanceof VersatileGeneratorInterface && !$router->supports($name)) {
  200. continue;
  201. }
  202. try {
  203. return $router->generate($name, $parameters, $absolute);
  204. } catch (RouteNotFoundException $e) {
  205. $hint = $this->getErrorMessage($name, $router, $parameters);
  206. $debug[] = $hint;
  207. if ($this->logger) {
  208. $this->logger->debug('Router '.get_class($router)." was unable to generate route. Reason: '$hint': ".$e->getMessage());
  209. }
  210. }
  211. }
  212. if ($debug) {
  213. $debug = array_unique($debug);
  214. $info = implode(', ', $debug);
  215. } else {
  216. $info = $this->getErrorMessage($name);
  217. }
  218. throw new RouteNotFoundException(sprintf('None of the chained routers were able to generate route: %s', $info));
  219. }
  220. private function getErrorMessage($name, $router = null, $parameters = null)
  221. {
  222. if ($router instanceof VersatileGeneratorInterface) {
  223. $displayName = $router->getRouteDebugMessage($name, $parameters);
  224. } elseif (is_object($name)) {
  225. $displayName = method_exists($name, '__toString')
  226. ? (string) $name
  227. : get_class($name)
  228. ;
  229. } else {
  230. $displayName = (string) $name;
  231. }
  232. return "Route '$displayName' not found";
  233. }
  234. /**
  235. * {@inheritdoc}
  236. */
  237. public function setContext(RequestContext $context)
  238. {
  239. foreach ($this->all() as $router) {
  240. if ($router instanceof RequestContextAwareInterface) {
  241. $router->setContext($context);
  242. }
  243. }
  244. $this->context = $context;
  245. }
  246. /**
  247. * {@inheritdoc}
  248. *
  249. * check for each contained router if it can warmup
  250. */
  251. public function warmUp($cacheDir)
  252. {
  253. foreach ($this->all() as $router) {
  254. if ($router instanceof WarmableInterface) {
  255. $router->warmUp($cacheDir);
  256. }
  257. }
  258. }
  259. /**
  260. * {@inheritdoc}
  261. */
  262. public function getRouteCollection()
  263. {
  264. if (!$this->routeCollection instanceof RouteCollection) {
  265. $this->routeCollection = new ChainRouteCollection();
  266. foreach ($this->all() as $router) {
  267. $this->routeCollection->addCollection($router->getRouteCollection());
  268. }
  269. }
  270. return $this->routeCollection;
  271. }
  272. }