annotate vendor/symfony-cmf/routing/DynamicRouter.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 4c8ae668cc8c
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 /*
Chris@0 4 * This file is part of the Symfony CMF package.
Chris@0 5 *
Chris@0 6 * (c) 2011-2015 Symfony CMF
Chris@0 7 *
Chris@0 8 * For the full copyright and license information, please view the LICENSE
Chris@0 9 * file that was distributed with this source code.
Chris@0 10 */
Chris@0 11
Chris@0 12 namespace Symfony\Cmf\Component\Routing;
Chris@0 13
Chris@0 14 use Symfony\Component\HttpFoundation\Request;
Chris@0 15 use Symfony\Component\Routing\RequestContext;
Chris@0 16 use Symfony\Component\Routing\Route;
Chris@0 17 use Symfony\Component\Routing\RouteCollection;
Chris@0 18 use Symfony\Component\Routing\RouterInterface;
Chris@0 19 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
Chris@0 20 use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
Chris@0 21 use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
Chris@0 22 use Symfony\Component\Routing\RequestContextAwareInterface;
Chris@0 23 use Symfony\Component\Routing\Exception\RouteNotFoundException;
Chris@0 24 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
Chris@0 25 use Symfony\Component\Routing\Exception\MethodNotAllowedException;
Chris@0 26 use Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface;
Chris@0 27 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
Chris@0 28 use Symfony\Cmf\Component\Routing\Event\Events;
Chris@0 29 use Symfony\Cmf\Component\Routing\Event\RouterMatchEvent;
Chris@0 30 use Symfony\Cmf\Component\Routing\Event\RouterGenerateEvent;
Chris@0 31
Chris@0 32 /**
Chris@0 33 * A flexible router accepting matcher and generator through injection and
Chris@0 34 * using the RouteEnhancer concept to generate additional data on the routes.
Chris@0 35 *
Chris@0 36 * @author Larry Garfield
Chris@0 37 * @author David Buchmann
Chris@0 38 */
Chris@0 39 class DynamicRouter implements RouterInterface, RequestMatcherInterface, ChainedRouterInterface
Chris@0 40 {
Chris@0 41 /**
Chris@0 42 * @var RequestMatcherInterface|UrlMatcherInterface
Chris@0 43 */
Chris@0 44 protected $matcher;
Chris@0 45
Chris@0 46 /**
Chris@0 47 * @var UrlGeneratorInterface
Chris@0 48 */
Chris@0 49 protected $generator;
Chris@0 50
Chris@0 51 /**
Chris@0 52 * @var EventDispatcherInterface
Chris@0 53 */
Chris@0 54 protected $eventDispatcher;
Chris@0 55
Chris@0 56 /**
Chris@0 57 * @var RouteEnhancerInterface[]
Chris@0 58 */
Chris@0 59 protected $enhancers = array();
Chris@0 60
Chris@0 61 /**
Chris@0 62 * Cached sorted list of enhancers.
Chris@0 63 *
Chris@0 64 * @var RouteEnhancerInterface[]
Chris@0 65 */
Chris@0 66 protected $sortedEnhancers = array();
Chris@0 67
Chris@0 68 /**
Chris@0 69 * The regexp pattern that needs to be matched before a dynamic lookup is
Chris@0 70 * made.
Chris@0 71 *
Chris@0 72 * @var string
Chris@0 73 */
Chris@0 74 protected $uriFilterRegexp;
Chris@0 75
Chris@0 76 /**
Chris@0 77 * @var RequestContext
Chris@0 78 */
Chris@0 79 protected $context;
Chris@0 80
Chris@0 81 /**
Chris@0 82 * @var RouteCollection
Chris@0 83 */
Chris@0 84 private $routeCollection;
Chris@0 85
Chris@0 86 /**
Chris@0 87 * @param RequestContext $context
Chris@0 88 * @param RequestMatcherInterface|UrlMatcherInterface $matcher
Chris@0 89 * @param UrlGeneratorInterface $generator
Chris@0 90 * @param string $uriFilterRegexp
Chris@0 91 * @param EventDispatcherInterface|null $eventDispatcher
Chris@0 92 * @param RouteProviderInterface $provider
Chris@0 93 */
Chris@0 94 public function __construct(RequestContext $context,
Chris@0 95 $matcher,
Chris@0 96 UrlGeneratorInterface $generator,
Chris@0 97 $uriFilterRegexp = '',
Chris@0 98 EventDispatcherInterface $eventDispatcher = null,
Chris@0 99 RouteProviderInterface $provider = null
Chris@0 100 ) {
Chris@0 101 $this->context = $context;
Chris@0 102 if (!$matcher instanceof RequestMatcherInterface && !$matcher instanceof UrlMatcherInterface) {
Chris@0 103 throw new \InvalidArgumentException('Matcher must implement either Symfony\Component\Routing\Matcher\RequestMatcherInterface or Symfony\Component\Routing\Matcher\UrlMatcherInterface');
Chris@0 104 }
Chris@0 105 $this->matcher = $matcher;
Chris@0 106 $this->generator = $generator;
Chris@0 107 $this->eventDispatcher = $eventDispatcher;
Chris@0 108 $this->uriFilterRegexp = $uriFilterRegexp;
Chris@0 109 $this->provider = $provider;
Chris@0 110
Chris@0 111 $this->generator->setContext($context);
Chris@0 112 }
Chris@0 113
Chris@0 114 /**
Chris@0 115 * {@inheritdoc}
Chris@0 116 */
Chris@0 117 public function getRouteCollection()
Chris@0 118 {
Chris@0 119 if (!$this->routeCollection instanceof RouteCollection) {
Chris@0 120 $this->routeCollection = $this->provider
Chris@0 121 ? new LazyRouteCollection($this->provider) : new RouteCollection();
Chris@0 122 }
Chris@0 123
Chris@0 124 return $this->routeCollection;
Chris@0 125 }
Chris@0 126
Chris@0 127 /**
Chris@0 128 * @return RequestMatcherInterface|UrlMatcherInterface
Chris@0 129 */
Chris@0 130 public function getMatcher()
Chris@0 131 {
Chris@0 132 /* we may not set the context in DynamicRouter::setContext as this
Chris@0 133 * would lead to symfony cache warmup problems.
Chris@0 134 * a request matcher does not need the request context separately as it
Chris@0 135 * can get it from the request.
Chris@0 136 */
Chris@0 137 if ($this->matcher instanceof RequestContextAwareInterface) {
Chris@0 138 $this->matcher->setContext($this->getContext());
Chris@0 139 }
Chris@0 140
Chris@0 141 return $this->matcher;
Chris@0 142 }
Chris@0 143
Chris@0 144 /**
Chris@0 145 * @return UrlGeneratorInterface
Chris@0 146 */
Chris@0 147 public function getGenerator()
Chris@0 148 {
Chris@0 149 $this->generator->setContext($this->getContext());
Chris@0 150
Chris@0 151 return $this->generator;
Chris@0 152 }
Chris@0 153
Chris@0 154 /**
Chris@0 155 * Generates a URL from the given parameters.
Chris@0 156 *
Chris@0 157 * If the generator is not able to generate the url, it must throw the
Chris@0 158 * RouteNotFoundException as documented below.
Chris@0 159 *
Chris@0 160 * @param string|Route $name The name of the route or the Route instance
Chris@0 161 * @param mixed $parameters An array of parameters
Chris@0 162 * @param bool|string $referenceType The type of reference to be generated (one of the constants in UrlGeneratorInterface)
Chris@0 163 *
Chris@0 164 * @return string The generated URL
Chris@0 165 *
Chris@0 166 * @throws RouteNotFoundException if route doesn't exist
Chris@0 167 *
Chris@0 168 * @api
Chris@0 169 */
Chris@0 170 public function generate($name, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH)
Chris@0 171 {
Chris@0 172 if ($this->eventDispatcher) {
Chris@0 173 $event = new RouterGenerateEvent($name, $parameters, $referenceType);
Chris@0 174 $this->eventDispatcher->dispatch(Events::PRE_DYNAMIC_GENERATE, $event);
Chris@0 175 $name = $event->getRoute();
Chris@0 176 $parameters = $event->getParameters();
Chris@0 177 $referenceType = $event->getReferenceType();
Chris@0 178 }
Chris@0 179
Chris@0 180 return $this->getGenerator()->generate($name, $parameters, $referenceType);
Chris@0 181 }
Chris@0 182
Chris@0 183 /**
Chris@0 184 * Delegate to our generator.
Chris@0 185 *
Chris@0 186 * {@inheritdoc}
Chris@0 187 */
Chris@0 188 public function supports($name)
Chris@0 189 {
Chris@0 190 if ($this->generator instanceof VersatileGeneratorInterface) {
Chris@0 191 return $this->generator->supports($name);
Chris@0 192 }
Chris@0 193
Chris@0 194 return is_string($name);
Chris@0 195 }
Chris@0 196
Chris@0 197 /**
Chris@0 198 * Tries to match a URL path with a set of routes.
Chris@0 199 *
Chris@0 200 * If the matcher can not find information, it must throw one of the
Chris@0 201 * exceptions documented below.
Chris@0 202 *
Chris@0 203 * @param string $pathinfo The path info to be parsed (raw format, i.e. not
Chris@0 204 * urldecoded)
Chris@0 205 *
Chris@0 206 * @return array An array of parameters
Chris@0 207 *
Chris@0 208 * @throws ResourceNotFoundException If the resource could not be found
Chris@0 209 * @throws MethodNotAllowedException If the resource was found but the
Chris@0 210 * request method is not allowed
Chris@0 211 *
Chris@0 212 * @deprecated Use matchRequest exclusively to avoid problems. This method will be removed in version 2.0
Chris@0 213 *
Chris@0 214 * @api
Chris@0 215 */
Chris@0 216 public function match($pathinfo)
Chris@0 217 {
Chris@0 218 @trigger_error(__METHOD__.'() is deprecated since version 1.3 and will be removed in 2.0. Use matchRequest() instead.', E_USER_DEPRECATED);
Chris@0 219
Chris@0 220 $request = Request::create($pathinfo);
Chris@0 221 if ($this->eventDispatcher) {
Chris@0 222 $event = new RouterMatchEvent();
Chris@0 223 $this->eventDispatcher->dispatch(Events::PRE_DYNAMIC_MATCH, $event);
Chris@0 224 }
Chris@0 225
Chris@0 226 if (!empty($this->uriFilterRegexp) && !preg_match($this->uriFilterRegexp, $pathinfo)) {
Chris@0 227 throw new ResourceNotFoundException("$pathinfo does not match the '{$this->uriFilterRegexp}' pattern");
Chris@0 228 }
Chris@0 229
Chris@0 230 $matcher = $this->getMatcher();
Chris@0 231 if (!$matcher instanceof UrlMatcherInterface) {
Chris@0 232 throw new \InvalidArgumentException('Wrong matcher type, you need to call matchRequest');
Chris@0 233 }
Chris@0 234
Chris@0 235 $defaults = $matcher->match($pathinfo);
Chris@0 236
Chris@0 237 return $this->applyRouteEnhancers($defaults, $request);
Chris@0 238 }
Chris@0 239
Chris@0 240 /**
Chris@0 241 * Tries to match a request with a set of routes and returns the array of
Chris@0 242 * information for that route.
Chris@0 243 *
Chris@0 244 * If the matcher can not find information, it must throw one of the
Chris@0 245 * exceptions documented below.
Chris@0 246 *
Chris@0 247 * @param Request $request The request to match
Chris@0 248 *
Chris@0 249 * @return array An array of parameters
Chris@0 250 *
Chris@0 251 * @throws ResourceNotFoundException If no matching resource could be found
Chris@0 252 * @throws MethodNotAllowedException If a matching resource was found but
Chris@0 253 * the request method is not allowed
Chris@0 254 */
Chris@0 255 public function matchRequest(Request $request)
Chris@0 256 {
Chris@0 257 if ($this->eventDispatcher) {
Chris@0 258 $event = new RouterMatchEvent($request);
Chris@0 259 $this->eventDispatcher->dispatch(Events::PRE_DYNAMIC_MATCH_REQUEST, $event);
Chris@0 260 }
Chris@0 261
Chris@0 262 if (!empty($this->uriFilterRegexp)
Chris@0 263 && !preg_match($this->uriFilterRegexp, $request->getPathInfo())
Chris@0 264 ) {
Chris@0 265 throw new ResourceNotFoundException("{$request->getPathInfo()} does not match the '{$this->uriFilterRegexp}' pattern");
Chris@0 266 }
Chris@0 267
Chris@0 268 $matcher = $this->getMatcher();
Chris@0 269 if ($matcher instanceof UrlMatcherInterface) {
Chris@0 270 $defaults = $matcher->match($request->getPathInfo());
Chris@0 271 } else {
Chris@0 272 $defaults = $matcher->matchRequest($request);
Chris@0 273 }
Chris@0 274
Chris@0 275 return $this->applyRouteEnhancers($defaults, $request);
Chris@0 276 }
Chris@0 277
Chris@0 278 /**
Chris@0 279 * Apply the route enhancers to the defaults, according to priorities.
Chris@0 280 *
Chris@0 281 * @param array $defaults
Chris@0 282 * @param Request $request
Chris@0 283 *
Chris@0 284 * @return array
Chris@0 285 */
Chris@0 286 protected function applyRouteEnhancers($defaults, Request $request)
Chris@0 287 {
Chris@0 288 foreach ($this->getRouteEnhancers() as $enhancer) {
Chris@0 289 $defaults = $enhancer->enhance($defaults, $request);
Chris@0 290 }
Chris@0 291
Chris@0 292 return $defaults;
Chris@0 293 }
Chris@0 294
Chris@0 295 /**
Chris@0 296 * Add route enhancers to the router to let them generate information on
Chris@0 297 * matched routes.
Chris@0 298 *
Chris@0 299 * The order of the enhancers is determined by the priority, the higher the
Chris@0 300 * value, the earlier the enhancer is run.
Chris@0 301 *
Chris@0 302 * @param RouteEnhancerInterface $enhancer
Chris@0 303 * @param int $priority
Chris@0 304 */
Chris@0 305 public function addRouteEnhancer(RouteEnhancerInterface $enhancer, $priority = 0)
Chris@0 306 {
Chris@0 307 if (empty($this->enhancers[$priority])) {
Chris@0 308 $this->enhancers[$priority] = array();
Chris@0 309 }
Chris@0 310
Chris@0 311 $this->enhancers[$priority][] = $enhancer;
Chris@0 312 $this->sortedEnhancers = array();
Chris@0 313
Chris@0 314 return $this;
Chris@0 315 }
Chris@0 316
Chris@0 317 /**
Chris@0 318 * Sorts the enhancers and flattens them.
Chris@0 319 *
Chris@0 320 * @return RouteEnhancerInterface[] the enhancers ordered by priority
Chris@0 321 */
Chris@0 322 public function getRouteEnhancers()
Chris@0 323 {
Chris@0 324 if (empty($this->sortedEnhancers)) {
Chris@0 325 $this->sortedEnhancers = $this->sortRouteEnhancers();
Chris@0 326 }
Chris@0 327
Chris@0 328 return $this->sortedEnhancers;
Chris@0 329 }
Chris@0 330
Chris@0 331 /**
Chris@0 332 * Sort enhancers by priority.
Chris@0 333 *
Chris@0 334 * The highest priority number is the highest priority (reverse sorting).
Chris@0 335 *
Chris@0 336 * @return RouteEnhancerInterface[] the sorted enhancers
Chris@0 337 */
Chris@0 338 protected function sortRouteEnhancers()
Chris@0 339 {
Chris@0 340 $sortedEnhancers = array();
Chris@0 341 krsort($this->enhancers);
Chris@0 342
Chris@0 343 foreach ($this->enhancers as $enhancers) {
Chris@0 344 $sortedEnhancers = array_merge($sortedEnhancers, $enhancers);
Chris@0 345 }
Chris@0 346
Chris@0 347 return $sortedEnhancers;
Chris@0 348 }
Chris@0 349
Chris@0 350 /**
Chris@0 351 * Sets the request context.
Chris@0 352 *
Chris@0 353 * @param RequestContext $context The context
Chris@0 354 *
Chris@0 355 * @api
Chris@0 356 */
Chris@0 357 public function setContext(RequestContext $context)
Chris@0 358 {
Chris@0 359 $this->context = $context;
Chris@0 360 }
Chris@0 361
Chris@0 362 /**
Chris@0 363 * Gets the request context.
Chris@0 364 *
Chris@0 365 * @return RequestContext The context
Chris@0 366 *
Chris@0 367 * @api
Chris@0 368 */
Chris@0 369 public function getContext()
Chris@0 370 {
Chris@0 371 return $this->context;
Chris@0 372 }
Chris@0 373
Chris@0 374 /**
Chris@0 375 * {@inheritdoc}
Chris@0 376 *
Chris@0 377 * Forwards to the generator.
Chris@0 378 */
Chris@0 379 public function getRouteDebugMessage($name, array $parameters = array())
Chris@0 380 {
Chris@0 381 if ($this->generator instanceof VersatileGeneratorInterface) {
Chris@0 382 return $this->generator->getRouteDebugMessage($name, $parameters);
Chris@0 383 }
Chris@0 384
Chris@0 385 return "Route '$name' not found";
Chris@0 386 }
Chris@0 387 }