vendor/symfony-cmf/routing/src/DynamicRouter.php line 255

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of the Symfony CMF package.
  4. *
  5. * (c) 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\Cmf\Component\Routing\Enhancer\RouteEnhancerTrait;
  12. use Symfony\Cmf\Component\Routing\Event\Events;
  13. use Symfony\Cmf\Component\Routing\Event\RouterGenerateEvent;
  14. use Symfony\Cmf\Component\Routing\Event\RouterMatchEvent;
  15. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  16. use Symfony\Component\HttpFoundation\Request;
  17. use Symfony\Component\Routing\Exception\MethodNotAllowedException;
  18. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  19. use Symfony\Component\Routing\Exception\RouteNotFoundException;
  20. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  21. use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
  22. use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
  23. use Symfony\Component\Routing\RequestContext;
  24. use Symfony\Component\Routing\RequestContextAwareInterface;
  25. use Symfony\Component\Routing\Route;
  26. use Symfony\Component\Routing\RouteCollection;
  27. /**
  28. * A flexible router accepting matcher and generator through injection and
  29. * using the RouteEnhancer concept to generate additional data on the routes.
  30. *
  31. * @author Larry Garfield
  32. * @author David Buchmann
  33. */
  34. class DynamicRouter implements RequestMatcherInterface, ChainedRouterInterface
  35. {
  36. use RouteEnhancerTrait;
  37. /**
  38. * @var RequestMatcherInterface|UrlMatcherInterface
  39. */
  40. protected $matcher;
  41. /**
  42. * @var UrlGeneratorInterface
  43. */
  44. protected $generator;
  45. /**
  46. * @var EventDispatcherInterface
  47. */
  48. protected $eventDispatcher;
  49. /**
  50. * The regexp pattern that needs to be matched before a dynamic lookup is
  51. * made.
  52. *
  53. * @var string
  54. */
  55. protected $uriFilterRegexp;
  56. /**
  57. * @var RouteProviderInterface
  58. */
  59. private $provider;
  60. /**
  61. * @var RequestContext
  62. */
  63. protected $context;
  64. /**
  65. * @var RouteCollection
  66. */
  67. private $routeCollection;
  68. /**
  69. * @param RequestMatcherInterface|UrlMatcherInterface $matcher
  70. * @param string $uriFilterRegexp
  71. * @param RouteProviderInterface $provider
  72. *
  73. * @throws \InvalidArgumentException If the matcher is not a request or url matcher
  74. */
  75. public function __construct(
  76. RequestContext $context,
  77. $matcher,
  78. UrlGeneratorInterface $generator,
  79. $uriFilterRegexp = '',
  80. EventDispatcherInterface $eventDispatcher = null,
  81. RouteProviderInterface $provider = null
  82. ) {
  83. $this->context = $context;
  84. if (!$matcher instanceof RequestMatcherInterface && !$matcher instanceof UrlMatcherInterface) {
  85. throw new \InvalidArgumentException(
  86. sprintf('Matcher must implement either %s or %s', RequestMatcherInterface::class, UrlMatcherInterface::class)
  87. );
  88. }
  89. $this->matcher = $matcher;
  90. $this->generator = $generator;
  91. $this->eventDispatcher = $eventDispatcher;
  92. $this->uriFilterRegexp = $uriFilterRegexp;
  93. $this->provider = $provider;
  94. $this->generator->setContext($context);
  95. }
  96. /**
  97. * {@inheritdoc}
  98. */
  99. public function getRouteCollection()
  100. {
  101. if (!$this->routeCollection instanceof RouteCollection) {
  102. $this->routeCollection = $this->provider
  103. ? new LazyRouteCollection($this->provider) : new RouteCollection();
  104. }
  105. return $this->routeCollection;
  106. }
  107. /**
  108. * @return RequestMatcherInterface|UrlMatcherInterface
  109. */
  110. public function getMatcher()
  111. {
  112. /* we may not set the context in DynamicRouter::setContext as this
  113. * would lead to symfony cache warmup problems.
  114. * a request matcher does not need the request context separately as it
  115. * can get it from the request.
  116. */
  117. if ($this->matcher instanceof RequestContextAwareInterface) {
  118. $this->matcher->setContext($this->getContext());
  119. }
  120. return $this->matcher;
  121. }
  122. /**
  123. * @return UrlGeneratorInterface
  124. */
  125. public function getGenerator()
  126. {
  127. $this->generator->setContext($this->getContext());
  128. return $this->generator;
  129. }
  130. /**
  131. * Generates a URL from the given parameters.
  132. *
  133. * If the generator is not able to generate the url, it must throw the
  134. * RouteNotFoundException as documented below.
  135. *
  136. * The CMF routing system used to allow to pass route objects as $name to generate the route.
  137. * Since Symfony 5.0, the UrlGeneratorInterface declares $name as string. We widen the contract
  138. * for BC but deprecate passing non-strings.
  139. * Instead, Pass the RouteObjectInterface::OBJECT_BASED_ROUTE_NAME as route name and the object
  140. * in the parameters with key RouteObjectInterface::ROUTE_OBJECT.
  141. *
  142. * @param string|Route $name The name of the route or the Route instance
  143. *
  144. * @throws RouteNotFoundException if route doesn't exist
  145. */
  146. public function generate($name, $parameters = [], $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH)
  147. {
  148. if (is_object($name)) {
  149. @trigger_error('Passing an object as route name is deprecated since version 2.3. Pass the `RouteObjectInterface::OBJECT_BASED_ROUTE_NAME` as route name and the object in the parameters with key `RouteObjectInterface::ROUTE_OBJECT', E_USER_DEPRECATED);
  150. }
  151. if ($this->eventDispatcher) {
  152. $event = new RouterGenerateEvent($name, $parameters, $referenceType);
  153. $this->eventDispatcher->dispatch($event, Events::PRE_DYNAMIC_GENERATE);
  154. $name = $event->getRoute();
  155. $parameters = $event->getParameters();
  156. $referenceType = $event->getReferenceType();
  157. }
  158. return $this->getGenerator()->generate($name, $parameters, $referenceType);
  159. }
  160. /**
  161. * Delegate to our generator.
  162. *
  163. * {@inheritdoc}
  164. */
  165. public function supports($name)
  166. {
  167. if ($this->generator instanceof VersatileGeneratorInterface) {
  168. return $this->generator->supports($name);
  169. }
  170. return is_string($name);
  171. }
  172. /**
  173. * Tries to match a URL path with a set of routes.
  174. *
  175. * If the matcher can not find information, it must throw one of the
  176. * exceptions documented below.
  177. *
  178. * @param string $pathinfo The path info to be parsed (raw format, i.e. not
  179. * urldecoded)
  180. *
  181. * @return array An array of parameters
  182. *
  183. * @throws ResourceNotFoundException If the resource could not be found
  184. * @throws MethodNotAllowedException If the resource was found but the
  185. * request method is not allowed
  186. *
  187. * @deprecated Use matchRequest exclusively to avoid problems. This method will be removed in version 2.0
  188. *
  189. * @api
  190. */
  191. public function match($pathinfo)
  192. {
  193. @trigger_error(__METHOD__.'() is deprecated since version 1.3 and will be removed in 2.0. Use matchRequest() instead.', E_USER_DEPRECATED);
  194. $request = Request::create($pathinfo);
  195. if ($this->eventDispatcher) {
  196. $event = new RouterMatchEvent();
  197. $this->eventDispatcher->dispatch($event, Events::PRE_DYNAMIC_MATCH);
  198. }
  199. if (!empty($this->uriFilterRegexp) && !preg_match($this->uriFilterRegexp, $pathinfo)) {
  200. throw new ResourceNotFoundException("$pathinfo does not match the '{$this->uriFilterRegexp}' pattern");
  201. }
  202. $matcher = $this->getMatcher();
  203. if (!$matcher instanceof UrlMatcherInterface) {
  204. throw new \InvalidArgumentException('Wrong matcher type, you need to call matchRequest');
  205. }
  206. $defaults = $matcher->match($pathinfo);
  207. return $this->applyRouteEnhancers($defaults, $request);
  208. }
  209. /**
  210. * Tries to match a request with a set of routes and returns the array of
  211. * information for that route.
  212. *
  213. * If the matcher can not find information, it must throw one of the
  214. * exceptions documented below.
  215. *
  216. * @param Request $request The request to match
  217. *
  218. * @return array An array of parameters
  219. *
  220. * @throws ResourceNotFoundException If no matching resource could be found
  221. * @throws MethodNotAllowedException If a matching resource was found but
  222. * the request method is not allowed
  223. */
  224. public function matchRequest(Request $request)
  225. {
  226. if ($this->eventDispatcher) {
  227. $event = new RouterMatchEvent($request);
  228. $this->eventDispatcher->dispatch($event, Events::PRE_DYNAMIC_MATCH_REQUEST);
  229. }
  230. if ($this->uriFilterRegexp
  231. && !preg_match($this->uriFilterRegexp, $request->getPathInfo())
  232. ) {
  233. throw new ResourceNotFoundException("{$request->getPathInfo()} does not match the '{$this->uriFilterRegexp}' pattern");
  234. }
  235. $matcher = $this->getMatcher();
  236. if ($matcher instanceof UrlMatcherInterface) {
  237. $defaults = $matcher->match($request->getPathInfo());
  238. } else {
  239. $defaults = $matcher->matchRequest($request);
  240. }
  241. return $this->applyRouteEnhancers($defaults, $request);
  242. }
  243. /**
  244. * Sets the request context.
  245. *
  246. * @param RequestContext $context The context
  247. *
  248. * @api
  249. */
  250. public function setContext(RequestContext $context)
  251. {
  252. $this->context = $context;
  253. }
  254. /**
  255. * Gets the request context.
  256. *
  257. * @return RequestContext The context
  258. *
  259. * @api
  260. */
  261. public function getContext()
  262. {
  263. return $this->context;
  264. }
  265. /**
  266. * {@inheritdoc}
  267. *
  268. * Forwards to the generator.
  269. */
  270. public function getRouteDebugMessage($name, array $parameters = [])
  271. {
  272. if ($this->generator instanceof VersatileGeneratorInterface) {
  273. return $this->generator->getRouteDebugMessage($name, $parameters);
  274. }
  275. return "Route '$name' not found";
  276. }
  277. }