Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 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 Psr\Log\LoggerInterface;
15
use Symfony\Component\Config\ConfigCacheFactory;
16
use Symfony\Component\Config\ConfigCacheFactoryInterface;
17
use Symfony\Component\Config\ConfigCacheInterface;
18
use Symfony\Component\Config\Loader\LoaderInterface;
19
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\Routing\Generator\CompiledUrlGenerator;
22
use Symfony\Component\Routing\Generator\ConfigurableRequirementsInterface;
23
use Symfony\Component\Routing\Generator\Dumper\CompiledUrlGeneratorDumper;
24
use Symfony\Component\Routing\Generator\Dumper\GeneratorDumperInterface;
25
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
26
use Symfony\Component\Routing\Matcher\CompiledUrlMatcher;
27
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper;
28
use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface;
29
use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
30
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
31
 
32
/**
33
 * The Router class is an example of the integration of all pieces of the
34
 * routing system for easier use.
35
 *
36
 * @author Fabien Potencier <fabien@symfony.com>
37
 */
38
class Router implements RouterInterface, RequestMatcherInterface
39
{
40
    /**
41
     * @var UrlMatcherInterface|null
42
     */
43
    protected $matcher;
44
 
45
    /**
46
     * @var UrlGeneratorInterface|null
47
     */
48
    protected $generator;
49
 
50
    /**
51
     * @var RequestContext
52
     */
53
    protected $context;
54
 
55
    /**
56
     * @var LoaderInterface
57
     */
58
    protected $loader;
59
 
60
    /**
61
     * @var RouteCollection|null
62
     */
63
    protected $collection;
64
 
65
    /**
66
     * @var mixed
67
     */
68
    protected $resource;
69
 
70
    /**
71
     * @var array
72
     */
73
    protected $options = [];
74
 
75
    /**
76
     * @var LoggerInterface|null
77
     */
78
    protected $logger;
79
 
80
    /**
81
     * @var string|null
82
     */
83
    protected $defaultLocale;
84
 
85
    private ConfigCacheFactoryInterface $configCacheFactory;
86
 
87
    /**
88
     * @var ExpressionFunctionProviderInterface[]
89
     */
90
    private array $expressionLanguageProviders = [];
91
 
92
    private static ?array $cache = [];
93
 
94
    public function __construct(LoaderInterface $loader, mixed $resource, array $options = [], RequestContext $context = null, LoggerInterface $logger = null, string $defaultLocale = null)
95
    {
96
        $this->loader = $loader;
97
        $this->resource = $resource;
98
        $this->logger = $logger;
99
        $this->context = $context ?? new RequestContext();
100
        $this->setOptions($options);
101
        $this->defaultLocale = $defaultLocale;
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_dumper_class: The name of a GeneratorDumperInterface implementation
113
     *   * matcher_class:          The name of a UrlMatcherInterface implementation
114
     *   * matcher_dumper_class:   The name of a MatcherDumperInterface implementation
115
     *   * resource_type:          Type hint for the main resource (optional)
116
     *   * strict_requirements:    Configure strict requirement checking for generators
117
     *                             implementing ConfigurableRequirementsInterface (default is true)
118
     *
119
     * @throws \InvalidArgumentException When unsupported option is provided
120
     */
121
    public function setOptions(array $options)
122
    {
123
        $this->options = [
124
            'cache_dir' => null,
125
            'debug' => false,
126
            'generator_class' => CompiledUrlGenerator::class,
127
            'generator_dumper_class' => CompiledUrlGeneratorDumper::class,
128
            'matcher_class' => CompiledUrlMatcher::class,
129
            'matcher_dumper_class' => CompiledUrlMatcherDumper::class,
130
            'resource_type' => null,
131
            'strict_requirements' => true,
132
        ];
133
 
134
        // check option names and live merge, if errors are encountered Exception will be thrown
135
        $invalid = [];
136
        foreach ($options as $key => $value) {
137
            if (\array_key_exists($key, $this->options)) {
138
                $this->options[$key] = $value;
139
            } else {
140
                $invalid[] = $key;
141
            }
142
        }
143
 
144
        if ($invalid) {
145
            throw new \InvalidArgumentException(sprintf('The Router does not support the following options: "%s".', implode('", "', $invalid)));
146
        }
147
    }
148
 
149
    /**
150
     * Sets an option.
151
     *
152
     * @throws \InvalidArgumentException
153
     */
154
    public function setOption(string $key, mixed $value)
155
    {
156
        if (!\array_key_exists($key, $this->options)) {
157
            throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
158
        }
159
 
160
        $this->options[$key] = $value;
161
    }
162
 
163
    /**
164
     * Gets an option value.
165
     *
166
     * @throws \InvalidArgumentException
167
     */
168
    public function getOption(string $key): mixed
169
    {
170
        if (!\array_key_exists($key, $this->options)) {
171
            throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
172
        }
173
 
174
        return $this->options[$key];
175
    }
176
 
177
    public function getRouteCollection()
178
    {
179
        return $this->collection ??= $this->loader->load($this->resource, $this->options['resource_type']);
180
    }
181
 
182
    public function setContext(RequestContext $context)
183
    {
184
        $this->context = $context;
185
 
186
        if (null !== $this->matcher) {
187
            $this->getMatcher()->setContext($context);
188
        }
189
        if (null !== $this->generator) {
190
            $this->getGenerator()->setContext($context);
191
        }
192
    }
193
 
194
    public function getContext(): RequestContext
195
    {
196
        return $this->context;
197
    }
198
 
199
    /**
200
     * Sets the ConfigCache factory to use.
201
     */
202
    public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
203
    {
204
        $this->configCacheFactory = $configCacheFactory;
205
    }
206
 
207
    public function generate(string $name, array $parameters = [], int $referenceType = self::ABSOLUTE_PATH): string
208
    {
209
        return $this->getGenerator()->generate($name, $parameters, $referenceType);
210
    }
211
 
212
    public function match(string $pathinfo): array
213
    {
214
        return $this->getMatcher()->match($pathinfo);
215
    }
216
 
217
    public function matchRequest(Request $request): array
218
    {
219
        $matcher = $this->getMatcher();
220
        if (!$matcher instanceof RequestMatcherInterface) {
221
            // fallback to the default UrlMatcherInterface
222
            return $matcher->match($request->getPathInfo());
223
        }
224
 
225
        return $matcher->matchRequest($request);
226
    }
227
 
228
    /**
229
     * Gets the UrlMatcher or RequestMatcher instance associated with this Router.
230
     */
231
    public function getMatcher(): UrlMatcherInterface|RequestMatcherInterface
232
    {
233
        if (null !== $this->matcher) {
234
            return $this->matcher;
235
        }
236
 
237
        if (null === $this->options['cache_dir']) {
238
            $routes = $this->getRouteCollection();
239
            $compiled = is_a($this->options['matcher_class'], CompiledUrlMatcher::class, true);
240
            if ($compiled) {
241
                $routes = (new CompiledUrlMatcherDumper($routes))->getCompiledRoutes();
242
            }
243
            $this->matcher = new $this->options['matcher_class']($routes, $this->context);
244
            if (method_exists($this->matcher, 'addExpressionLanguageProvider')) {
245
                foreach ($this->expressionLanguageProviders as $provider) {
246
                    $this->matcher->addExpressionLanguageProvider($provider);
247
                }
248
            }
249
 
250
            return $this->matcher;
251
        }
252
 
253
        $cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/url_matching_routes.php',
254
            function (ConfigCacheInterface $cache) {
255
                $dumper = $this->getMatcherDumperInstance();
256
                if (method_exists($dumper, 'addExpressionLanguageProvider')) {
257
                    foreach ($this->expressionLanguageProviders as $provider) {
258
                        $dumper->addExpressionLanguageProvider($provider);
259
                    }
260
                }
261
 
262
                $cache->write($dumper->dump(), $this->getRouteCollection()->getResources());
263
            }
264
        );
265
 
266
        return $this->matcher = new $this->options['matcher_class'](self::getCompiledRoutes($cache->getPath()), $this->context);
267
    }
268
 
269
    /**
270
     * Gets the UrlGenerator instance associated with this Router.
271
     */
272
    public function getGenerator(): UrlGeneratorInterface
273
    {
274
        if (null !== $this->generator) {
275
            return $this->generator;
276
        }
277
 
278
        if (null === $this->options['cache_dir']) {
279
            $routes = $this->getRouteCollection();
280
            $compiled = is_a($this->options['generator_class'], CompiledUrlGenerator::class, true);
281
            if ($compiled) {
282
                $generatorDumper = new CompiledUrlGeneratorDumper($routes);
283
                $routes = array_merge($generatorDumper->getCompiledRoutes(), $generatorDumper->getCompiledAliases());
284
            }
285
            $this->generator = new $this->options['generator_class']($routes, $this->context, $this->logger, $this->defaultLocale);
286
        } else {
287
            $cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/url_generating_routes.php',
288
                function (ConfigCacheInterface $cache) {
289
                    $dumper = $this->getGeneratorDumperInstance();
290
 
291
                    $cache->write($dumper->dump(), $this->getRouteCollection()->getResources());
292
                }
293
            );
294
 
295
            $this->generator = new $this->options['generator_class'](self::getCompiledRoutes($cache->getPath()), $this->context, $this->logger, $this->defaultLocale);
296
        }
297
 
298
        if ($this->generator instanceof ConfigurableRequirementsInterface) {
299
            $this->generator->setStrictRequirements($this->options['strict_requirements']);
300
        }
301
 
302
        return $this->generator;
303
    }
304
 
305
    public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
306
    {
307
        $this->expressionLanguageProviders[] = $provider;
308
    }
309
 
310
    protected function getGeneratorDumperInstance(): GeneratorDumperInterface
311
    {
312
        return new $this->options['generator_dumper_class']($this->getRouteCollection());
313
    }
314
 
315
    protected function getMatcherDumperInstance(): MatcherDumperInterface
316
    {
317
        return new $this->options['matcher_dumper_class']($this->getRouteCollection());
318
    }
319
 
320
    /**
321
     * Provides the ConfigCache factory implementation, falling back to a
322
     * default implementation if necessary.
323
     */
324
    private function getConfigCacheFactory(): ConfigCacheFactoryInterface
325
    {
326
        return $this->configCacheFactory ??= new ConfigCacheFactory($this->options['debug']);
327
    }
328
 
329
    private static function getCompiledRoutes(string $path): array
330
    {
331
        if ([] === self::$cache && \function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOL) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOL))) {
332
            self::$cache = null;
333
        }
334
 
335
        if (null === self::$cache) {
336
            return require $path;
337
        }
338
 
339
        return self::$cache[$path] ??= require $path;
340
    }
341
}