Fix bug in style changes for the Use cases on the live site.
[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      * Constructor.
88      *
89      * @param LoaderInterface $loader   A LoaderInterface instance
90      * @param mixed           $resource The main resource to load
91      * @param array           $options  An array of options
92      * @param RequestContext  $context  The context
93      * @param LoggerInterface $logger   A logger instance
94      */
95     public function __construct(LoaderInterface $loader, $resource, array $options = array(), RequestContext $context = null, LoggerInterface $logger = null)
96     {
97         $this->loader = $loader;
98         $this->resource = $resource;
99         $this->logger = $logger;
100         $this->context = $context ?: new RequestContext();
101         $this->setOptions($options);
102     }
103
104     /**
105      * Sets options.
106      *
107      * Available options:
108      *
109      *   * cache_dir:              The cache directory (or null to disable caching)
110      *   * debug:                  Whether to enable debugging or not (false by default)
111      *   * generator_class:        The name of a UrlGeneratorInterface implementation
112      *   * generator_base_class:   The base class for the dumped generator class
113      *   * generator_cache_class:  The class name for the dumped generator class
114      *   * generator_dumper_class: The name of a GeneratorDumperInterface implementation
115      *   * matcher_class:          The name of a UrlMatcherInterface implementation
116      *   * matcher_base_class:     The base class for the dumped matcher class
117      *   * matcher_dumper_class:   The class name for the dumped matcher class
118      *   * matcher_cache_class:    The name of a MatcherDumperInterface implementation
119      *   * resource_type:          Type hint for the main resource (optional)
120      *   * strict_requirements:    Configure strict requirement checking for generators
121      *                             implementing ConfigurableRequirementsInterface (default is true)
122      *
123      * @param array $options An array of options
124      *
125      * @throws \InvalidArgumentException When unsupported option is provided
126      */
127     public function setOptions(array $options)
128     {
129         $this->options = array(
130             'cache_dir' => null,
131             'debug' => false,
132             'generator_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
133             'generator_base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
134             'generator_dumper_class' => 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper',
135             'generator_cache_class' => 'ProjectUrlGenerator',
136             'matcher_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
137             'matcher_base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
138             'matcher_dumper_class' => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper',
139             'matcher_cache_class' => 'ProjectUrlMatcher',
140             'resource_type' => null,
141             'strict_requirements' => true,
142         );
143
144         // check option names and live merge, if errors are encountered Exception will be thrown
145         $invalid = array();
146         foreach ($options as $key => $value) {
147             if (array_key_exists($key, $this->options)) {
148                 $this->options[$key] = $value;
149             } else {
150                 $invalid[] = $key;
151             }
152         }
153
154         if ($invalid) {
155             throw new \InvalidArgumentException(sprintf('The Router does not support the following options: "%s".', implode('", "', $invalid)));
156         }
157     }
158
159     /**
160      * Sets an option.
161      *
162      * @param string $key   The key
163      * @param mixed  $value The value
164      *
165      * @throws \InvalidArgumentException
166      */
167     public function setOption($key, $value)
168     {
169         if (!array_key_exists($key, $this->options)) {
170             throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
171         }
172
173         $this->options[$key] = $value;
174     }
175
176     /**
177      * Gets an option value.
178      *
179      * @param string $key The key
180      *
181      * @return mixed The value
182      *
183      * @throws \InvalidArgumentException
184      */
185     public function getOption($key)
186     {
187         if (!array_key_exists($key, $this->options)) {
188             throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
189         }
190
191         return $this->options[$key];
192     }
193
194     /**
195      * {@inheritdoc}
196      */
197     public function getRouteCollection()
198     {
199         if (null === $this->collection) {
200             $this->collection = $this->loader->load($this->resource, $this->options['resource_type']);
201         }
202
203         return $this->collection;
204     }
205
206     /**
207      * {@inheritdoc}
208      */
209     public function setContext(RequestContext $context)
210     {
211         $this->context = $context;
212
213         if (null !== $this->matcher) {
214             $this->getMatcher()->setContext($context);
215         }
216         if (null !== $this->generator) {
217             $this->getGenerator()->setContext($context);
218         }
219     }
220
221     /**
222      * {@inheritdoc}
223      */
224     public function getContext()
225     {
226         return $this->context;
227     }
228
229     /**
230      * Sets the ConfigCache factory to use.
231      *
232      * @param ConfigCacheFactoryInterface $configCacheFactory The factory to use
233      */
234     public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
235     {
236         $this->configCacheFactory = $configCacheFactory;
237     }
238
239     /**
240      * {@inheritdoc}
241      */
242     public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
243     {
244         return $this->getGenerator()->generate($name, $parameters, $referenceType);
245     }
246
247     /**
248      * {@inheritdoc}
249      */
250     public function match($pathinfo)
251     {
252         return $this->getMatcher()->match($pathinfo);
253     }
254
255     /**
256      * {@inheritdoc}
257      */
258     public function matchRequest(Request $request)
259     {
260         $matcher = $this->getMatcher();
261         if (!$matcher instanceof RequestMatcherInterface) {
262             // fallback to the default UrlMatcherInterface
263             return $matcher->match($request->getPathInfo());
264         }
265
266         return $matcher->matchRequest($request);
267     }
268
269     /**
270      * Gets the UrlMatcher instance associated with this Router.
271      *
272      * @return UrlMatcherInterface A UrlMatcherInterface instance
273      */
274     public function getMatcher()
275     {
276         if (null !== $this->matcher) {
277             return $this->matcher;
278         }
279
280         if (null === $this->options['cache_dir'] || null === $this->options['matcher_cache_class']) {
281             $this->matcher = new $this->options['matcher_class']($this->getRouteCollection(), $this->context);
282             if (method_exists($this->matcher, 'addExpressionLanguageProvider')) {
283                 foreach ($this->expressionLanguageProviders as $provider) {
284                     $this->matcher->addExpressionLanguageProvider($provider);
285                 }
286             }
287
288             return $this->matcher;
289         }
290
291         $class = $this->options['matcher_cache_class'];
292         $baseClass = $this->options['matcher_base_class'];
293         $expressionLanguageProviders = $this->expressionLanguageProviders;
294         $that = $this; // required for PHP 5.3 where "$this" cannot be use()d in anonymous functions. Change in Symfony 3.0.
295
296         $cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/'.$class.'.php',
297             function (ConfigCacheInterface $cache) use ($that, $class, $baseClass, $expressionLanguageProviders) {
298                 $dumper = $that->getMatcherDumperInstance();
299                 if (method_exists($dumper, 'addExpressionLanguageProvider')) {
300                     foreach ($expressionLanguageProviders as $provider) {
301                         $dumper->addExpressionLanguageProvider($provider);
302                     }
303                 }
304
305                 $options = array(
306                     'class' => $class,
307                     'base_class' => $baseClass,
308                 );
309
310                 $cache->write($dumper->dump($options), $that->getRouteCollection()->getResources());
311             }
312         );
313
314         require_once $cache->getPath();
315
316         return $this->matcher = new $class($this->context);
317     }
318
319     /**
320      * Gets the UrlGenerator instance associated with this Router.
321      *
322      * @return UrlGeneratorInterface A UrlGeneratorInterface instance
323      */
324     public function getGenerator()
325     {
326         if (null !== $this->generator) {
327             return $this->generator;
328         }
329
330         if (null === $this->options['cache_dir'] || null === $this->options['generator_cache_class']) {
331             $this->generator = new $this->options['generator_class']($this->getRouteCollection(), $this->context, $this->logger);
332         } else {
333             $class = $this->options['generator_cache_class'];
334             $baseClass = $this->options['generator_base_class'];
335             $that = $this; // required for PHP 5.3 where "$this" cannot be use()d in anonymous functions. Change in Symfony 3.0.
336             $cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/'.$class.'.php',
337                 function (ConfigCacheInterface $cache) use ($that, $class, $baseClass) {
338                     $dumper = $that->getGeneratorDumperInstance();
339
340                     $options = array(
341                         'class' => $class,
342                         'base_class' => $baseClass,
343                     );
344
345                     $cache->write($dumper->dump($options), $that->getRouteCollection()->getResources());
346                 }
347             );
348
349             require_once $cache->getPath();
350
351             $this->generator = new $class($this->context, $this->logger);
352         }
353
354         if ($this->generator instanceof ConfigurableRequirementsInterface) {
355             $this->generator->setStrictRequirements($this->options['strict_requirements']);
356         }
357
358         return $this->generator;
359     }
360
361     public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
362     {
363         $this->expressionLanguageProviders[] = $provider;
364     }
365
366     /**
367      * This method is public because it needs to be callable from a closure in PHP 5.3. It should be converted back to protected in 3.0.
368      *
369      * @internal
370      *
371      * @return GeneratorDumperInterface
372      */
373     public function getGeneratorDumperInstance()
374     {
375         return new $this->options['generator_dumper_class']($this->getRouteCollection());
376     }
377
378     /**
379      * This method is public because it needs to be callable from a closure in PHP 5.3. It should be converted back to protected in 3.0.
380      *
381      * @internal
382      *
383      * @return MatcherDumperInterface
384      */
385     public function getMatcherDumperInstance()
386     {
387         return new $this->options['matcher_dumper_class']($this->getRouteCollection());
388     }
389
390     /**
391      * Provides the ConfigCache factory implementation, falling back to a
392      * default implementation if necessary.
393      *
394      * @return ConfigCacheFactoryInterface $configCacheFactory
395      */
396     private function getConfigCacheFactory()
397     {
398         if (null === $this->configCacheFactory) {
399             $this->configCacheFactory = new ConfigCacheFactory($this->options['debug']);
400         }
401
402         return $this->configCacheFactory;
403     }
404 }