comparison vendor/symfony-cmf/routing/DynamicRouter.php @ 0:4c8ae668cc8c

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