Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of the Symfony package.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) Fabien Potencier <fabien@symfony.com>
|
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\Component\Routing;
|
Chris@0
|
13
|
Chris@0
|
14 use Symfony\Component\Config\Loader\LoaderInterface;
|
Chris@0
|
15 use Symfony\Component\Config\ConfigCacheInterface;
|
Chris@0
|
16 use Symfony\Component\Config\ConfigCacheFactoryInterface;
|
Chris@0
|
17 use Symfony\Component\Config\ConfigCacheFactory;
|
Chris@0
|
18 use Psr\Log\LoggerInterface;
|
Chris@0
|
19 use Symfony\Component\Routing\Generator\ConfigurableRequirementsInterface;
|
Chris@0
|
20 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
Chris@0
|
21 use Symfony\Component\Routing\Generator\Dumper\GeneratorDumperInterface;
|
Chris@0
|
22 use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
|
Chris@0
|
23 use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
|
Chris@0
|
24 use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface;
|
Chris@0
|
25 use Symfony\Component\HttpFoundation\Request;
|
Chris@0
|
26 use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
Chris@0
|
27
|
Chris@0
|
28 /**
|
Chris@0
|
29 * The Router class is an example of the integration of all pieces of the
|
Chris@0
|
30 * routing system for easier use.
|
Chris@0
|
31 *
|
Chris@0
|
32 * @author Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
33 */
|
Chris@0
|
34 class Router implements RouterInterface, RequestMatcherInterface
|
Chris@0
|
35 {
|
Chris@0
|
36 /**
|
Chris@0
|
37 * @var UrlMatcherInterface|null
|
Chris@0
|
38 */
|
Chris@0
|
39 protected $matcher;
|
Chris@0
|
40
|
Chris@0
|
41 /**
|
Chris@0
|
42 * @var UrlGeneratorInterface|null
|
Chris@0
|
43 */
|
Chris@0
|
44 protected $generator;
|
Chris@0
|
45
|
Chris@0
|
46 /**
|
Chris@0
|
47 * @var RequestContext
|
Chris@0
|
48 */
|
Chris@0
|
49 protected $context;
|
Chris@0
|
50
|
Chris@0
|
51 /**
|
Chris@0
|
52 * @var LoaderInterface
|
Chris@0
|
53 */
|
Chris@0
|
54 protected $loader;
|
Chris@0
|
55
|
Chris@0
|
56 /**
|
Chris@0
|
57 * @var RouteCollection|null
|
Chris@0
|
58 */
|
Chris@0
|
59 protected $collection;
|
Chris@0
|
60
|
Chris@0
|
61 /**
|
Chris@0
|
62 * @var mixed
|
Chris@0
|
63 */
|
Chris@0
|
64 protected $resource;
|
Chris@0
|
65
|
Chris@0
|
66 /**
|
Chris@0
|
67 * @var array
|
Chris@0
|
68 */
|
Chris@0
|
69 protected $options = array();
|
Chris@0
|
70
|
Chris@0
|
71 /**
|
Chris@0
|
72 * @var LoggerInterface|null
|
Chris@0
|
73 */
|
Chris@0
|
74 protected $logger;
|
Chris@0
|
75
|
Chris@0
|
76 /**
|
Chris@0
|
77 * @var ConfigCacheFactoryInterface|null
|
Chris@0
|
78 */
|
Chris@0
|
79 private $configCacheFactory;
|
Chris@0
|
80
|
Chris@0
|
81 /**
|
Chris@0
|
82 * @var ExpressionFunctionProviderInterface[]
|
Chris@0
|
83 */
|
Chris@0
|
84 private $expressionLanguageProviders = array();
|
Chris@0
|
85
|
Chris@0
|
86 /**
|
Chris@0
|
87 * Constructor.
|
Chris@0
|
88 *
|
Chris@0
|
89 * @param LoaderInterface $loader A LoaderInterface instance
|
Chris@0
|
90 * @param mixed $resource The main resource to load
|
Chris@0
|
91 * @param array $options An array of options
|
Chris@0
|
92 * @param RequestContext $context The context
|
Chris@0
|
93 * @param LoggerInterface $logger A logger instance
|
Chris@0
|
94 */
|
Chris@0
|
95 public function __construct(LoaderInterface $loader, $resource, array $options = array(), RequestContext $context = null, LoggerInterface $logger = null)
|
Chris@0
|
96 {
|
Chris@0
|
97 $this->loader = $loader;
|
Chris@0
|
98 $this->resource = $resource;
|
Chris@0
|
99 $this->logger = $logger;
|
Chris@0
|
100 $this->context = $context ?: new RequestContext();
|
Chris@0
|
101 $this->setOptions($options);
|
Chris@0
|
102 }
|
Chris@0
|
103
|
Chris@0
|
104 /**
|
Chris@0
|
105 * Sets options.
|
Chris@0
|
106 *
|
Chris@0
|
107 * Available options:
|
Chris@0
|
108 *
|
Chris@0
|
109 * * cache_dir: The cache directory (or null to disable caching)
|
Chris@0
|
110 * * debug: Whether to enable debugging or not (false by default)
|
Chris@0
|
111 * * generator_class: The name of a UrlGeneratorInterface implementation
|
Chris@0
|
112 * * generator_base_class: The base class for the dumped generator class
|
Chris@0
|
113 * * generator_cache_class: The class name for the dumped generator class
|
Chris@0
|
114 * * generator_dumper_class: The name of a GeneratorDumperInterface implementation
|
Chris@0
|
115 * * matcher_class: The name of a UrlMatcherInterface implementation
|
Chris@0
|
116 * * matcher_base_class: The base class for the dumped matcher class
|
Chris@0
|
117 * * matcher_dumper_class: The class name for the dumped matcher class
|
Chris@0
|
118 * * matcher_cache_class: The name of a MatcherDumperInterface implementation
|
Chris@0
|
119 * * resource_type: Type hint for the main resource (optional)
|
Chris@0
|
120 * * strict_requirements: Configure strict requirement checking for generators
|
Chris@0
|
121 * implementing ConfigurableRequirementsInterface (default is true)
|
Chris@0
|
122 *
|
Chris@0
|
123 * @param array $options An array of options
|
Chris@0
|
124 *
|
Chris@0
|
125 * @throws \InvalidArgumentException When unsupported option is provided
|
Chris@0
|
126 */
|
Chris@0
|
127 public function setOptions(array $options)
|
Chris@0
|
128 {
|
Chris@0
|
129 $this->options = array(
|
Chris@0
|
130 'cache_dir' => null,
|
Chris@0
|
131 'debug' => false,
|
Chris@0
|
132 'generator_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
|
Chris@0
|
133 'generator_base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
|
Chris@0
|
134 'generator_dumper_class' => 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper',
|
Chris@0
|
135 'generator_cache_class' => 'ProjectUrlGenerator',
|
Chris@0
|
136 'matcher_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
|
Chris@0
|
137 'matcher_base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
|
Chris@0
|
138 'matcher_dumper_class' => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper',
|
Chris@0
|
139 'matcher_cache_class' => 'ProjectUrlMatcher',
|
Chris@0
|
140 'resource_type' => null,
|
Chris@0
|
141 'strict_requirements' => true,
|
Chris@0
|
142 );
|
Chris@0
|
143
|
Chris@0
|
144 // check option names and live merge, if errors are encountered Exception will be thrown
|
Chris@0
|
145 $invalid = array();
|
Chris@0
|
146 foreach ($options as $key => $value) {
|
Chris@0
|
147 if (array_key_exists($key, $this->options)) {
|
Chris@0
|
148 $this->options[$key] = $value;
|
Chris@0
|
149 } else {
|
Chris@0
|
150 $invalid[] = $key;
|
Chris@0
|
151 }
|
Chris@0
|
152 }
|
Chris@0
|
153
|
Chris@0
|
154 if ($invalid) {
|
Chris@0
|
155 throw new \InvalidArgumentException(sprintf('The Router does not support the following options: "%s".', implode('", "', $invalid)));
|
Chris@0
|
156 }
|
Chris@0
|
157 }
|
Chris@0
|
158
|
Chris@0
|
159 /**
|
Chris@0
|
160 * Sets an option.
|
Chris@0
|
161 *
|
Chris@0
|
162 * @param string $key The key
|
Chris@0
|
163 * @param mixed $value The value
|
Chris@0
|
164 *
|
Chris@0
|
165 * @throws \InvalidArgumentException
|
Chris@0
|
166 */
|
Chris@0
|
167 public function setOption($key, $value)
|
Chris@0
|
168 {
|
Chris@0
|
169 if (!array_key_exists($key, $this->options)) {
|
Chris@0
|
170 throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
|
Chris@0
|
171 }
|
Chris@0
|
172
|
Chris@0
|
173 $this->options[$key] = $value;
|
Chris@0
|
174 }
|
Chris@0
|
175
|
Chris@0
|
176 /**
|
Chris@0
|
177 * Gets an option value.
|
Chris@0
|
178 *
|
Chris@0
|
179 * @param string $key The key
|
Chris@0
|
180 *
|
Chris@0
|
181 * @return mixed The value
|
Chris@0
|
182 *
|
Chris@0
|
183 * @throws \InvalidArgumentException
|
Chris@0
|
184 */
|
Chris@0
|
185 public function getOption($key)
|
Chris@0
|
186 {
|
Chris@0
|
187 if (!array_key_exists($key, $this->options)) {
|
Chris@0
|
188 throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
|
Chris@0
|
189 }
|
Chris@0
|
190
|
Chris@0
|
191 return $this->options[$key];
|
Chris@0
|
192 }
|
Chris@0
|
193
|
Chris@0
|
194 /**
|
Chris@0
|
195 * {@inheritdoc}
|
Chris@0
|
196 */
|
Chris@0
|
197 public function getRouteCollection()
|
Chris@0
|
198 {
|
Chris@0
|
199 if (null === $this->collection) {
|
Chris@0
|
200 $this->collection = $this->loader->load($this->resource, $this->options['resource_type']);
|
Chris@0
|
201 }
|
Chris@0
|
202
|
Chris@0
|
203 return $this->collection;
|
Chris@0
|
204 }
|
Chris@0
|
205
|
Chris@0
|
206 /**
|
Chris@0
|
207 * {@inheritdoc}
|
Chris@0
|
208 */
|
Chris@0
|
209 public function setContext(RequestContext $context)
|
Chris@0
|
210 {
|
Chris@0
|
211 $this->context = $context;
|
Chris@0
|
212
|
Chris@0
|
213 if (null !== $this->matcher) {
|
Chris@0
|
214 $this->getMatcher()->setContext($context);
|
Chris@0
|
215 }
|
Chris@0
|
216 if (null !== $this->generator) {
|
Chris@0
|
217 $this->getGenerator()->setContext($context);
|
Chris@0
|
218 }
|
Chris@0
|
219 }
|
Chris@0
|
220
|
Chris@0
|
221 /**
|
Chris@0
|
222 * {@inheritdoc}
|
Chris@0
|
223 */
|
Chris@0
|
224 public function getContext()
|
Chris@0
|
225 {
|
Chris@0
|
226 return $this->context;
|
Chris@0
|
227 }
|
Chris@0
|
228
|
Chris@0
|
229 /**
|
Chris@0
|
230 * Sets the ConfigCache factory to use.
|
Chris@0
|
231 *
|
Chris@0
|
232 * @param ConfigCacheFactoryInterface $configCacheFactory The factory to use
|
Chris@0
|
233 */
|
Chris@0
|
234 public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
|
Chris@0
|
235 {
|
Chris@0
|
236 $this->configCacheFactory = $configCacheFactory;
|
Chris@0
|
237 }
|
Chris@0
|
238
|
Chris@0
|
239 /**
|
Chris@0
|
240 * {@inheritdoc}
|
Chris@0
|
241 */
|
Chris@0
|
242 public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
|
Chris@0
|
243 {
|
Chris@0
|
244 return $this->getGenerator()->generate($name, $parameters, $referenceType);
|
Chris@0
|
245 }
|
Chris@0
|
246
|
Chris@0
|
247 /**
|
Chris@0
|
248 * {@inheritdoc}
|
Chris@0
|
249 */
|
Chris@0
|
250 public function match($pathinfo)
|
Chris@0
|
251 {
|
Chris@0
|
252 return $this->getMatcher()->match($pathinfo);
|
Chris@0
|
253 }
|
Chris@0
|
254
|
Chris@0
|
255 /**
|
Chris@0
|
256 * {@inheritdoc}
|
Chris@0
|
257 */
|
Chris@0
|
258 public function matchRequest(Request $request)
|
Chris@0
|
259 {
|
Chris@0
|
260 $matcher = $this->getMatcher();
|
Chris@0
|
261 if (!$matcher instanceof RequestMatcherInterface) {
|
Chris@0
|
262 // fallback to the default UrlMatcherInterface
|
Chris@0
|
263 return $matcher->match($request->getPathInfo());
|
Chris@0
|
264 }
|
Chris@0
|
265
|
Chris@0
|
266 return $matcher->matchRequest($request);
|
Chris@0
|
267 }
|
Chris@0
|
268
|
Chris@0
|
269 /**
|
Chris@0
|
270 * Gets the UrlMatcher instance associated with this Router.
|
Chris@0
|
271 *
|
Chris@0
|
272 * @return UrlMatcherInterface A UrlMatcherInterface instance
|
Chris@0
|
273 */
|
Chris@0
|
274 public function getMatcher()
|
Chris@0
|
275 {
|
Chris@0
|
276 if (null !== $this->matcher) {
|
Chris@0
|
277 return $this->matcher;
|
Chris@0
|
278 }
|
Chris@0
|
279
|
Chris@0
|
280 if (null === $this->options['cache_dir'] || null === $this->options['matcher_cache_class']) {
|
Chris@0
|
281 $this->matcher = new $this->options['matcher_class']($this->getRouteCollection(), $this->context);
|
Chris@0
|
282 if (method_exists($this->matcher, 'addExpressionLanguageProvider')) {
|
Chris@0
|
283 foreach ($this->expressionLanguageProviders as $provider) {
|
Chris@0
|
284 $this->matcher->addExpressionLanguageProvider($provider);
|
Chris@0
|
285 }
|
Chris@0
|
286 }
|
Chris@0
|
287
|
Chris@0
|
288 return $this->matcher;
|
Chris@0
|
289 }
|
Chris@0
|
290
|
Chris@0
|
291 $cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/'.$this->options['matcher_cache_class'].'.php',
|
Chris@0
|
292 function (ConfigCacheInterface $cache) {
|
Chris@0
|
293 $dumper = $this->getMatcherDumperInstance();
|
Chris@0
|
294 if (method_exists($dumper, 'addExpressionLanguageProvider')) {
|
Chris@0
|
295 foreach ($this->expressionLanguageProviders as $provider) {
|
Chris@0
|
296 $dumper->addExpressionLanguageProvider($provider);
|
Chris@0
|
297 }
|
Chris@0
|
298 }
|
Chris@0
|
299
|
Chris@0
|
300 $options = array(
|
Chris@0
|
301 'class' => $this->options['matcher_cache_class'],
|
Chris@0
|
302 'base_class' => $this->options['matcher_base_class'],
|
Chris@0
|
303 );
|
Chris@0
|
304
|
Chris@0
|
305 $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources());
|
Chris@0
|
306 }
|
Chris@0
|
307 );
|
Chris@0
|
308
|
Chris@0
|
309 require_once $cache->getPath();
|
Chris@0
|
310
|
Chris@0
|
311 return $this->matcher = new $this->options['matcher_cache_class']($this->context);
|
Chris@0
|
312 }
|
Chris@0
|
313
|
Chris@0
|
314 /**
|
Chris@0
|
315 * Gets the UrlGenerator instance associated with this Router.
|
Chris@0
|
316 *
|
Chris@0
|
317 * @return UrlGeneratorInterface A UrlGeneratorInterface instance
|
Chris@0
|
318 */
|
Chris@0
|
319 public function getGenerator()
|
Chris@0
|
320 {
|
Chris@0
|
321 if (null !== $this->generator) {
|
Chris@0
|
322 return $this->generator;
|
Chris@0
|
323 }
|
Chris@0
|
324
|
Chris@0
|
325 if (null === $this->options['cache_dir'] || null === $this->options['generator_cache_class']) {
|
Chris@0
|
326 $this->generator = new $this->options['generator_class']($this->getRouteCollection(), $this->context, $this->logger);
|
Chris@0
|
327 } else {
|
Chris@0
|
328 $cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/'.$this->options['generator_cache_class'].'.php',
|
Chris@0
|
329 function (ConfigCacheInterface $cache) {
|
Chris@0
|
330 $dumper = $this->getGeneratorDumperInstance();
|
Chris@0
|
331
|
Chris@0
|
332 $options = array(
|
Chris@0
|
333 'class' => $this->options['generator_cache_class'],
|
Chris@0
|
334 'base_class' => $this->options['generator_base_class'],
|
Chris@0
|
335 );
|
Chris@0
|
336
|
Chris@0
|
337 $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources());
|
Chris@0
|
338 }
|
Chris@0
|
339 );
|
Chris@0
|
340
|
Chris@0
|
341 require_once $cache->getPath();
|
Chris@0
|
342
|
Chris@0
|
343 $this->generator = new $this->options['generator_cache_class']($this->context, $this->logger);
|
Chris@0
|
344 }
|
Chris@0
|
345
|
Chris@0
|
346 if ($this->generator instanceof ConfigurableRequirementsInterface) {
|
Chris@0
|
347 $this->generator->setStrictRequirements($this->options['strict_requirements']);
|
Chris@0
|
348 }
|
Chris@0
|
349
|
Chris@0
|
350 return $this->generator;
|
Chris@0
|
351 }
|
Chris@0
|
352
|
Chris@0
|
353 public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
|
Chris@0
|
354 {
|
Chris@0
|
355 $this->expressionLanguageProviders[] = $provider;
|
Chris@0
|
356 }
|
Chris@0
|
357
|
Chris@0
|
358 /**
|
Chris@0
|
359 * @return GeneratorDumperInterface
|
Chris@0
|
360 */
|
Chris@0
|
361 protected function getGeneratorDumperInstance()
|
Chris@0
|
362 {
|
Chris@0
|
363 return new $this->options['generator_dumper_class']($this->getRouteCollection());
|
Chris@0
|
364 }
|
Chris@0
|
365
|
Chris@0
|
366 /**
|
Chris@0
|
367 * @return MatcherDumperInterface
|
Chris@0
|
368 */
|
Chris@0
|
369 protected function getMatcherDumperInstance()
|
Chris@0
|
370 {
|
Chris@0
|
371 return new $this->options['matcher_dumper_class']($this->getRouteCollection());
|
Chris@0
|
372 }
|
Chris@0
|
373
|
Chris@0
|
374 /**
|
Chris@0
|
375 * Provides the ConfigCache factory implementation, falling back to a
|
Chris@0
|
376 * default implementation if necessary.
|
Chris@0
|
377 *
|
Chris@0
|
378 * @return ConfigCacheFactoryInterface $configCacheFactory
|
Chris@0
|
379 */
|
Chris@0
|
380 private function getConfigCacheFactory()
|
Chris@0
|
381 {
|
Chris@0
|
382 if (null === $this->configCacheFactory) {
|
Chris@0
|
383 $this->configCacheFactory = new ConfigCacheFactory($this->options['debug']);
|
Chris@0
|
384 }
|
Chris@0
|
385
|
Chris@0
|
386 return $this->configCacheFactory;
|
Chris@0
|
387 }
|
Chris@0
|
388 }
|