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