Upgrade framework
This commit is contained in:
3
vendor/symfony/routing/.gitignore
vendored
3
vendor/symfony/routing/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
93
vendor/symfony/routing/Alias.php
vendored
Normal file
93
vendor/symfony/routing/Alias.php
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing;
|
||||
|
||||
use Symfony\Component\Routing\Exception\InvalidArgumentException;
|
||||
|
||||
class Alias
|
||||
{
|
||||
private string $id;
|
||||
private array $deprecation = [];
|
||||
|
||||
public function __construct(string $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function withId(string $id): static
|
||||
{
|
||||
$new = clone $this;
|
||||
|
||||
$new->id = $id;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target name of this alias.
|
||||
*
|
||||
* @return string The target name
|
||||
*/
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this alias is deprecated, that means it should not be referenced anymore.
|
||||
*
|
||||
* @param string $package The name of the composer package that is triggering the deprecation
|
||||
* @param string $version The version of the package that introduced the deprecation
|
||||
* @param string $message The deprecation message to use
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidArgumentException when the message template is invalid
|
||||
*/
|
||||
public function setDeprecated(string $package, string $version, string $message): static
|
||||
{
|
||||
if ('' !== $message) {
|
||||
if (preg_match('#[\r\n]|\*/#', $message)) {
|
||||
throw new InvalidArgumentException('Invalid characters found in deprecation template.');
|
||||
}
|
||||
|
||||
if (!str_contains($message, '%alias_id%')) {
|
||||
throw new InvalidArgumentException('The deprecation template must contain the "%alias_id%" placeholder.');
|
||||
}
|
||||
}
|
||||
|
||||
$this->deprecation = [
|
||||
'package' => $package,
|
||||
'version' => $version,
|
||||
'message' => $message ?: 'The "%alias_id%" route alias is deprecated. You should stop using it, as it will be removed in the future.',
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isDeprecated(): bool
|
||||
{
|
||||
return (bool) $this->deprecation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Route name relying on this alias
|
||||
*/
|
||||
public function getDeprecation(string $name): array
|
||||
{
|
||||
return [
|
||||
'package' => $this->deprecation['package'],
|
||||
'version' => $this->deprecation['version'],
|
||||
'message' => str_replace('%alias_id%', $name, $this->deprecation['message']),
|
||||
];
|
||||
}
|
||||
}
|
||||
124
vendor/symfony/routing/Annotation/Route.php
vendored
124
vendor/symfony/routing/Annotation/Route.php
vendored
@@ -15,46 +15,68 @@ namespace Symfony\Component\Routing\Annotation;
|
||||
* Annotation class for @Route().
|
||||
*
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor
|
||||
* @Target({"CLASS", "METHOD"})
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Alexander M. Turek <me@derrabus.de>
|
||||
*/
|
||||
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
|
||||
class Route
|
||||
{
|
||||
private $path;
|
||||
private $name;
|
||||
private $requirements = array();
|
||||
private $options = array();
|
||||
private $defaults = array();
|
||||
private $host;
|
||||
private $methods = array();
|
||||
private $schemes = array();
|
||||
private $condition;
|
||||
private ?string $path = null;
|
||||
private array $localizedPaths = [];
|
||||
private array $methods;
|
||||
private array $schemes;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $data An array of key/value parameters
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
* @param string[] $requirements
|
||||
* @param string[]|string $methods
|
||||
* @param string[]|string $schemes
|
||||
*/
|
||||
public function __construct(array $data)
|
||||
{
|
||||
if (isset($data['value'])) {
|
||||
$data['path'] = $data['value'];
|
||||
unset($data['value']);
|
||||
public function __construct(
|
||||
string|array $path = null,
|
||||
private ?string $name = null,
|
||||
private array $requirements = [],
|
||||
private array $options = [],
|
||||
private array $defaults = [],
|
||||
private ?string $host = null,
|
||||
array|string $methods = [],
|
||||
array|string $schemes = [],
|
||||
private ?string $condition = null,
|
||||
private ?int $priority = null,
|
||||
string $locale = null,
|
||||
string $format = null,
|
||||
bool $utf8 = null,
|
||||
bool $stateless = null,
|
||||
private ?string $env = null
|
||||
) {
|
||||
if (\is_array($path)) {
|
||||
$this->localizedPaths = $path;
|
||||
} else {
|
||||
$this->path = $path;
|
||||
}
|
||||
$this->setMethods($methods);
|
||||
$this->setSchemes($schemes);
|
||||
|
||||
if (null !== $locale) {
|
||||
$this->defaults['_locale'] = $locale;
|
||||
}
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$method = 'set'.str_replace('_', '', $key);
|
||||
if (!method_exists($this, $method)) {
|
||||
throw new \BadMethodCallException(sprintf('Unknown property "%s" on annotation "%s".', $key, get_class($this)));
|
||||
}
|
||||
$this->$method($value);
|
||||
if (null !== $format) {
|
||||
$this->defaults['_format'] = $format;
|
||||
}
|
||||
|
||||
if (null !== $utf8) {
|
||||
$this->options['utf8'] = $utf8;
|
||||
}
|
||||
|
||||
if (null !== $stateless) {
|
||||
$this->defaults['_stateless'] = $stateless;
|
||||
}
|
||||
}
|
||||
|
||||
public function setPath($path)
|
||||
public function setPath(string $path)
|
||||
{
|
||||
$this->path = $path;
|
||||
}
|
||||
@@ -64,7 +86,17 @@ class Route
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function setHost($pattern)
|
||||
public function setLocalizedPaths(array $localizedPaths)
|
||||
{
|
||||
$this->localizedPaths = $localizedPaths;
|
||||
}
|
||||
|
||||
public function getLocalizedPaths(): array
|
||||
{
|
||||
return $this->localizedPaths;
|
||||
}
|
||||
|
||||
public function setHost(string $pattern)
|
||||
{
|
||||
$this->host = $pattern;
|
||||
}
|
||||
@@ -74,7 +106,7 @@ class Route
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
public function setName($name)
|
||||
public function setName(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
@@ -84,7 +116,7 @@ class Route
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setRequirements($requirements)
|
||||
public function setRequirements(array $requirements)
|
||||
{
|
||||
$this->requirements = $requirements;
|
||||
}
|
||||
@@ -94,7 +126,7 @@ class Route
|
||||
return $this->requirements;
|
||||
}
|
||||
|
||||
public function setOptions($options)
|
||||
public function setOptions(array $options)
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
@@ -104,7 +136,7 @@ class Route
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
public function setDefaults($defaults)
|
||||
public function setDefaults(array $defaults)
|
||||
{
|
||||
$this->defaults = $defaults;
|
||||
}
|
||||
@@ -114,9 +146,9 @@ class Route
|
||||
return $this->defaults;
|
||||
}
|
||||
|
||||
public function setSchemes($schemes)
|
||||
public function setSchemes(array|string $schemes)
|
||||
{
|
||||
$this->schemes = is_array($schemes) ? $schemes : array($schemes);
|
||||
$this->schemes = (array) $schemes;
|
||||
}
|
||||
|
||||
public function getSchemes()
|
||||
@@ -124,9 +156,9 @@ class Route
|
||||
return $this->schemes;
|
||||
}
|
||||
|
||||
public function setMethods($methods)
|
||||
public function setMethods(array|string $methods)
|
||||
{
|
||||
$this->methods = is_array($methods) ? $methods : array($methods);
|
||||
$this->methods = (array) $methods;
|
||||
}
|
||||
|
||||
public function getMethods()
|
||||
@@ -134,7 +166,7 @@ class Route
|
||||
return $this->methods;
|
||||
}
|
||||
|
||||
public function setCondition($condition)
|
||||
public function setCondition(?string $condition)
|
||||
{
|
||||
$this->condition = $condition;
|
||||
}
|
||||
@@ -143,4 +175,24 @@ class Route
|
||||
{
|
||||
return $this->condition;
|
||||
}
|
||||
|
||||
public function setPriority(int $priority): void
|
||||
{
|
||||
$this->priority = $priority;
|
||||
}
|
||||
|
||||
public function getPriority(): ?int
|
||||
{
|
||||
return $this->priority;
|
||||
}
|
||||
|
||||
public function setEnv(?string $env): void
|
||||
{
|
||||
$this->env = $env;
|
||||
}
|
||||
|
||||
public function getEnv(): ?string
|
||||
{
|
||||
return $this->env;
|
||||
}
|
||||
}
|
||||
|
||||
111
vendor/symfony/routing/CHANGELOG.md
vendored
111
vendor/symfony/routing/CHANGELOG.md
vendored
@@ -1,25 +1,102 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
5.3
|
||||
---
|
||||
|
||||
* Already encoded slashes are not decoded nor double-encoded anymore when generating URLs
|
||||
* Add support for per-env configuration in XML and Yaml loaders
|
||||
* Deprecate creating instances of the `Route` annotation class by passing an array of parameters
|
||||
* Add `RoutingConfigurator::env()` to get the current environment
|
||||
|
||||
5.2.0
|
||||
-----
|
||||
|
||||
* Added support for inline definition of requirements and defaults for host
|
||||
* Added support for `\A` and `\z` as regex start and end for route requirement
|
||||
* Added support for `#[Route]` attributes
|
||||
|
||||
5.1.0
|
||||
-----
|
||||
|
||||
* added the protected method `PhpFileLoader::callConfigurator()` as extension point to ease custom routing configuration
|
||||
* deprecated `RouteCollectionBuilder` in favor of `RoutingConfigurator`.
|
||||
* added "priority" option to annotated routes
|
||||
* added argument `$priority` to `RouteCollection::add()`
|
||||
* deprecated the `RouteCompiler::REGEX_DELIMITER` constant
|
||||
* added `ExpressionLanguageProvider` to expose extra functions to route conditions
|
||||
* added support for a `stateless` keyword for configuring route stateless in PHP, YAML and XML configurations.
|
||||
* added the "hosts" option to be able to configure the host per locale.
|
||||
* added `RequestContext::fromUri()` to ease building the default context
|
||||
|
||||
5.0.0
|
||||
-----
|
||||
|
||||
* removed `PhpGeneratorDumper` and `PhpMatcherDumper`
|
||||
* removed `generator_base_class`, `generator_cache_class`, `matcher_base_class` and `matcher_cache_class` router options
|
||||
* `Serializable` implementing methods for `Route` and `CompiledRoute` are final
|
||||
* removed referencing service route loaders with a single colon
|
||||
* Removed `ServiceRouterLoader` and `ObjectRouteLoader`.
|
||||
|
||||
4.4.0
|
||||
-----
|
||||
|
||||
* Deprecated `ServiceRouterLoader` in favor of `ContainerLoader`.
|
||||
* Deprecated `ObjectRouteLoader` in favor of `ObjectLoader`.
|
||||
* Added a way to exclude patterns of resources from being imported by the `import()` method
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
* added `CompiledUrlMatcher` and `CompiledUrlMatcherDumper`
|
||||
* added `CompiledUrlGenerator` and `CompiledUrlGeneratorDumper`
|
||||
* deprecated `PhpGeneratorDumper` and `PhpMatcherDumper`
|
||||
* deprecated `generator_base_class`, `generator_cache_class`, `matcher_base_class` and `matcher_cache_class` router options
|
||||
* `Serializable` implementing methods for `Route` and `CompiledRoute` are marked as `@internal` and `@final`.
|
||||
Instead of overwriting them, use `__serialize` and `__unserialize` as extension points which are forward compatible
|
||||
with the new serialization methods in PHP 7.4.
|
||||
* exposed `utf8` Route option, defaults "locale" and "format" in configuration loaders and configurators
|
||||
* added support for invokable service route loaders
|
||||
|
||||
4.2.0
|
||||
-----
|
||||
|
||||
* added fallback to cultureless locale for internationalized routes
|
||||
|
||||
4.0.0
|
||||
-----
|
||||
|
||||
* dropped support for using UTF-8 route patterns without using the `utf8` option
|
||||
* dropped support for using UTF-8 route requirements without using the `utf8` option
|
||||
|
||||
3.4.0
|
||||
-----
|
||||
|
||||
* Added `NoConfigurationException`.
|
||||
* Added the possibility to define a prefix for all routes of a controller via @Route(name="prefix_")
|
||||
* Added support for prioritized routing loaders.
|
||||
* Add matched and default parameters to redirect responses
|
||||
* Added support for a `controller` keyword for configuring route controllers in YAML and XML configurations.
|
||||
|
||||
3.3.0
|
||||
-----
|
||||
|
||||
* [DEPRECATION] Class parameters have been deprecated and will be removed in 4.0.
|
||||
* router.options.generator_class
|
||||
* router.options.generator_base_class
|
||||
* router.options.generator_dumper_class
|
||||
* router.options.matcher_class
|
||||
* router.options.matcher_base_class
|
||||
* router.options.matcher_dumper_class
|
||||
* router.options.matcher.cache_class
|
||||
* router.options.generator.cache_class
|
||||
* [DEPRECATION] Class parameters have been deprecated and will be removed in 4.0.
|
||||
* router.options.generator_class
|
||||
* router.options.generator_base_class
|
||||
* router.options.generator_dumper_class
|
||||
* router.options.matcher_class
|
||||
* router.options.matcher_base_class
|
||||
* router.options.matcher_dumper_class
|
||||
* router.options.matcher.cache_class
|
||||
* router.options.generator.cache_class
|
||||
|
||||
3.2.0
|
||||
-----
|
||||
|
||||
* Added support for `bool`, `int`, `float`, `string`, `list` and `map` defaults in XML configurations.
|
||||
* Added support for UTF-8 requirements
|
||||
|
||||
|
||||
2.8.0
|
||||
-----
|
||||
|
||||
@@ -32,7 +109,7 @@ CHANGELOG
|
||||
Before:
|
||||
|
||||
```php
|
||||
$router->generate('blog_show', array('slug' => 'my-blog-post'), true);
|
||||
$router->generate('blog_show', ['slug' => 'my-blog-post'], true);
|
||||
```
|
||||
|
||||
After:
|
||||
@@ -40,7 +117,7 @@ CHANGELOG
|
||||
```php
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
$router->generate('blog_show', array('slug' => 'my-blog-post'), UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
$router->generate('blog_show', ['slug' => 'my-blog-post'], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
```
|
||||
|
||||
2.5.0
|
||||
@@ -48,7 +125,7 @@ CHANGELOG
|
||||
|
||||
* [DEPRECATION] The `ApacheMatcherDumper` and `ApacheUrlMatcher` were deprecated and
|
||||
will be removed in Symfony 3.0, since the performance gains were minimal and
|
||||
it's hard to replicate the behaviour of PHP implementation.
|
||||
it's hard to replicate the behavior of PHP implementation.
|
||||
|
||||
2.3.0
|
||||
-----
|
||||
@@ -105,7 +182,7 @@ CHANGELOG
|
||||
```php
|
||||
$route = new Route();
|
||||
$route->setPath('/article/{id}');
|
||||
$route->setMethods(array('POST', 'PUT'));
|
||||
$route->setMethods(['POST', 'PUT']);
|
||||
$route->setSchemes('https');
|
||||
```
|
||||
|
||||
@@ -160,10 +237,10 @@ CHANGELOG
|
||||
used with a single parameter. The other params `$prefix`, `$default`, `$requirements` and `$options`
|
||||
will still work, but have been deprecated. The `addPrefix` method should be used for this
|
||||
use-case instead.
|
||||
Before: `$parentCollection->addCollection($collection, '/prefix', array(...), array(...))`
|
||||
Before: `$parentCollection->addCollection($collection, '/prefix', [...], [...])`
|
||||
After:
|
||||
```php
|
||||
$collection->addPrefix('/prefix', array(...), array(...));
|
||||
$collection->addPrefix('/prefix', [...], [...]);
|
||||
$parentCollection->addCollection($collection);
|
||||
```
|
||||
* added support for the method default argument values when defining a @Route
|
||||
@@ -188,7 +265,7 @@ CHANGELOG
|
||||
(only relevant if you implemented your own RouteCompiler).
|
||||
* Added possibility to generate relative paths and network paths in the UrlGenerator, e.g.
|
||||
"../parent-file" and "//example.com/dir/file". The third parameter in
|
||||
`UrlGeneratorInterface::generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)`
|
||||
`UrlGeneratorInterface::generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH)`
|
||||
now accepts more values and you should use the constants defined in `UrlGeneratorInterface` for
|
||||
claritiy. The old method calls with a Boolean parameter will continue to work because they
|
||||
equal the signature using the constants.
|
||||
|
||||
86
vendor/symfony/routing/CompiledRoute.php
vendored
86
vendor/symfony/routing/CompiledRoute.php
vendored
@@ -18,18 +18,16 @@ namespace Symfony\Component\Routing;
|
||||
*/
|
||||
class CompiledRoute implements \Serializable
|
||||
{
|
||||
private $variables;
|
||||
private $tokens;
|
||||
private $staticPrefix;
|
||||
private $regex;
|
||||
private $pathVariables;
|
||||
private $hostVariables;
|
||||
private $hostRegex;
|
||||
private $hostTokens;
|
||||
private array $variables;
|
||||
private array $tokens;
|
||||
private string $staticPrefix;
|
||||
private string $regex;
|
||||
private array $pathVariables;
|
||||
private array $hostVariables;
|
||||
private ?string $hostRegex;
|
||||
private array $hostTokens;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $staticPrefix The static prefix of the compiled route
|
||||
* @param string $regex The regular expression to use to match this route
|
||||
* @param array $tokens An array of tokens to use to generate URL for this route
|
||||
@@ -39,9 +37,9 @@ class CompiledRoute implements \Serializable
|
||||
* @param array $hostVariables An array of host variables
|
||||
* @param array $variables An array of variables (variables defined in the path and in the host patterns)
|
||||
*/
|
||||
public function __construct($staticPrefix, $regex, array $tokens, array $pathVariables, $hostRegex = null, array $hostTokens = array(), array $hostVariables = array(), array $variables = array())
|
||||
public function __construct(string $staticPrefix, string $regex, array $tokens, array $pathVariables, string $hostRegex = null, array $hostTokens = [], array $hostVariables = [], array $variables = [])
|
||||
{
|
||||
$this->staticPrefix = (string) $staticPrefix;
|
||||
$this->staticPrefix = $staticPrefix;
|
||||
$this->regex = $regex;
|
||||
$this->tokens = $tokens;
|
||||
$this->pathVariables = $pathVariables;
|
||||
@@ -51,12 +49,9 @@ class CompiledRoute implements \Serializable
|
||||
$this->variables = $variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function serialize()
|
||||
public function __serialize(): array
|
||||
{
|
||||
return serialize(array(
|
||||
return [
|
||||
'vars' => $this->variables,
|
||||
'path_prefix' => $this->staticPrefix,
|
||||
'path_regex' => $this->regex,
|
||||
@@ -65,20 +60,19 @@ class CompiledRoute implements \Serializable
|
||||
'host_regex' => $this->hostRegex,
|
||||
'host_tokens' => $this->hostTokens,
|
||||
'host_vars' => $this->hostVariables,
|
||||
));
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @internal
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
final public function serialize(): string
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 70000) {
|
||||
$data = unserialize($serialized, array('allowed_classes' => false));
|
||||
} else {
|
||||
$data = unserialize($serialized);
|
||||
}
|
||||
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
|
||||
}
|
||||
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
$this->variables = $data['vars'];
|
||||
$this->staticPrefix = $data['path_prefix'];
|
||||
$this->regex = $data['path_regex'];
|
||||
@@ -90,81 +84,73 @@ class CompiledRoute implements \Serializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the static prefix.
|
||||
*
|
||||
* @return string The static prefix
|
||||
* @internal
|
||||
*/
|
||||
public function getStaticPrefix()
|
||||
final public function unserialize(string $serialized)
|
||||
{
|
||||
$this->__unserialize(unserialize($serialized, ['allowed_classes' => false]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the static prefix.
|
||||
*/
|
||||
public function getStaticPrefix(): string
|
||||
{
|
||||
return $this->staticPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the regex.
|
||||
*
|
||||
* @return string The regex
|
||||
*/
|
||||
public function getRegex()
|
||||
public function getRegex(): string
|
||||
{
|
||||
return $this->regex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host regex.
|
||||
*
|
||||
* @return string|null The host regex or null
|
||||
*/
|
||||
public function getHostRegex()
|
||||
public function getHostRegex(): ?string
|
||||
{
|
||||
return $this->hostRegex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tokens.
|
||||
*
|
||||
* @return array The tokens
|
||||
*/
|
||||
public function getTokens()
|
||||
public function getTokens(): array
|
||||
{
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host tokens.
|
||||
*
|
||||
* @return array The tokens
|
||||
*/
|
||||
public function getHostTokens()
|
||||
public function getHostTokens(): array
|
||||
{
|
||||
return $this->hostTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the variables.
|
||||
*
|
||||
* @return array The variables
|
||||
*/
|
||||
public function getVariables()
|
||||
public function getVariables(): array
|
||||
{
|
||||
return $this->variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path variables.
|
||||
*
|
||||
* @return array The variables
|
||||
*/
|
||||
public function getPathVariables()
|
||||
public function getPathVariables(): array
|
||||
{
|
||||
return $this->pathVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host variables.
|
||||
*
|
||||
* @return array The variables
|
||||
*/
|
||||
public function getHostVariables()
|
||||
public function getHostVariables(): array
|
||||
{
|
||||
return $this->hostVariables;
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
|
||||
namespace Symfony\Component\Routing\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Adds tagged routing.loader services to routing.resolver service.
|
||||
@@ -22,25 +23,18 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
*/
|
||||
class RoutingResolverPass implements CompilerPassInterface
|
||||
{
|
||||
private $resolverServiceId;
|
||||
private $loaderTag;
|
||||
|
||||
public function __construct($resolverServiceId = 'routing.resolver', $loaderTag = 'routing.loader')
|
||||
{
|
||||
$this->resolverServiceId = $resolverServiceId;
|
||||
$this->loaderTag = $loaderTag;
|
||||
}
|
||||
use PriorityTaggedServiceTrait;
|
||||
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (false === $container->hasDefinition($this->resolverServiceId)) {
|
||||
if (false === $container->hasDefinition('routing.resolver')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$definition = $container->getDefinition($this->resolverServiceId);
|
||||
$definition = $container->getDefinition('routing.resolver');
|
||||
|
||||
foreach ($container->findTaggedServiceIds($this->loaderTag, true) as $id => $attributes) {
|
||||
$definition->addMethodCall('addLoader', array(new Reference($id)));
|
||||
foreach ($this->findAndSortTaggedServices('routing.loader', $container) as $id) {
|
||||
$definition->addMethodCall('addLoader', [new Reference($id)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ namespace Symfony\Component\Routing\Exception;
|
||||
*
|
||||
* @author Alexandre Salomé <alexandre.salome@gmail.com>
|
||||
*/
|
||||
interface ExceptionInterface
|
||||
interface ExceptionInterface extends \Throwable
|
||||
{
|
||||
}
|
||||
|
||||
@@ -9,11 +9,8 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses;
|
||||
namespace Symfony\Component\Routing\Exception;
|
||||
|
||||
class BazClass
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,12 @@ namespace Symfony\Component\Routing\Exception;
|
||||
*/
|
||||
class MethodNotAllowedException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $allowedMethods = array();
|
||||
protected $allowedMethods = [];
|
||||
|
||||
public function __construct(array $allowedMethods, $message = null, $code = 0, \Exception $previous = null)
|
||||
/**
|
||||
* @param string[] $allowedMethods
|
||||
*/
|
||||
public function __construct(array $allowedMethods, string $message = '', int $code = 0, \Throwable $previous = null)
|
||||
{
|
||||
$this->allowedMethods = array_map('strtoupper', $allowedMethods);
|
||||
|
||||
@@ -35,9 +35,9 @@ class MethodNotAllowedException extends \RuntimeException implements ExceptionIn
|
||||
/**
|
||||
* Gets the allowed HTTP methods.
|
||||
*
|
||||
* @return array
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAllowedMethods()
|
||||
public function getAllowedMethods(): array
|
||||
{
|
||||
return $this->allowedMethods;
|
||||
}
|
||||
|
||||
@@ -9,10 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Fixtures;
|
||||
namespace Symfony\Component\Routing\Exception;
|
||||
|
||||
use Symfony\Component\Routing\CompiledRoute;
|
||||
|
||||
class CustomCompiledRoute extends CompiledRoute
|
||||
/**
|
||||
* Exception thrown when no routes are configured.
|
||||
*
|
||||
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
||||
*/
|
||||
class NoConfigurationException extends ResourceNotFoundException
|
||||
{
|
||||
}
|
||||
20
vendor/symfony/routing/Exception/RouteCircularReferenceException.php
vendored
Normal file
20
vendor/symfony/routing/Exception/RouteCircularReferenceException.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Exception;
|
||||
|
||||
class RouteCircularReferenceException extends RuntimeException
|
||||
{
|
||||
public function __construct(string $routeId, array $path)
|
||||
{
|
||||
parent::__construct(sprintf('Circular reference detected for route "%s", path: "%s".', $routeId, implode(' -> ', $path)));
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,8 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses;
|
||||
namespace Symfony\Component\Routing\Exception;
|
||||
|
||||
abstract class AbstractClass
|
||||
class RuntimeException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
69
vendor/symfony/routing/Generator/CompiledUrlGenerator.php
vendored
Normal file
69
vendor/symfony/routing/Generator/CompiledUrlGenerator.php
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Generator;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Routing\Exception\RouteNotFoundException;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* Generates URLs based on rules dumped by CompiledUrlGeneratorDumper.
|
||||
*/
|
||||
class CompiledUrlGenerator extends UrlGenerator
|
||||
{
|
||||
private array $compiledRoutes = [];
|
||||
private ?string $defaultLocale;
|
||||
|
||||
public function __construct(array $compiledRoutes, RequestContext $context, LoggerInterface $logger = null, string $defaultLocale = null)
|
||||
{
|
||||
$this->compiledRoutes = $compiledRoutes;
|
||||
$this->context = $context;
|
||||
$this->logger = $logger;
|
||||
$this->defaultLocale = $defaultLocale;
|
||||
}
|
||||
|
||||
public function generate(string $name, array $parameters = [], int $referenceType = self::ABSOLUTE_PATH): string
|
||||
{
|
||||
$locale = $parameters['_locale']
|
||||
?? $this->context->getParameter('_locale')
|
||||
?: $this->defaultLocale;
|
||||
|
||||
if (null !== $locale) {
|
||||
do {
|
||||
if (($this->compiledRoutes[$name.'.'.$locale][1]['_canonical_route'] ?? null) === $name) {
|
||||
$name .= '.'.$locale;
|
||||
break;
|
||||
}
|
||||
} while (false !== $locale = strstr($locale, '_', true));
|
||||
}
|
||||
|
||||
if (!isset($this->compiledRoutes[$name])) {
|
||||
throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name));
|
||||
}
|
||||
|
||||
[$variables, $defaults, $requirements, $tokens, $hostTokens, $requiredSchemes, $deprecations] = $this->compiledRoutes[$name] + [6 => []];
|
||||
|
||||
foreach ($deprecations as $deprecation) {
|
||||
trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']);
|
||||
}
|
||||
|
||||
if (isset($defaults['_canonical_route']) && isset($defaults['_locale'])) {
|
||||
if (!\in_array('_locale', $variables, true)) {
|
||||
unset($parameters['_locale']);
|
||||
} elseif (!isset($parameters['_locale'])) {
|
||||
$parameters['_locale'] = $defaults['_locale'];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, $requiredSchemes);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace Symfony\Component\Routing\Generator;
|
||||
* The possible configurations and use-cases:
|
||||
* - setStrictRequirements(true): Throw an exception for mismatching requirements. This
|
||||
* is mostly useful in development environment.
|
||||
* - setStrictRequirements(false): Don't throw an exception but return null as URL for
|
||||
* - setStrictRequirements(false): Don't throw an exception but return an empty string as URL for
|
||||
* mismatching requirements and log the problem. Useful when you cannot control all
|
||||
* params because they come from third party libs but don't want to have a 404 in
|
||||
* production environment. It should log the mismatch so one can review it.
|
||||
@@ -40,16 +40,12 @@ interface ConfigurableRequirementsInterface
|
||||
/**
|
||||
* Enables or disables the exception on incorrect parameters.
|
||||
* Passing null will deactivate the requirements check completely.
|
||||
*
|
||||
* @param bool|null $enabled
|
||||
*/
|
||||
public function setStrictRequirements($enabled);
|
||||
public function setStrictRequirements(?bool $enabled);
|
||||
|
||||
/**
|
||||
* Returns whether to throw an exception on incorrect parameters.
|
||||
* Null means the requirements check is deactivated completely.
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function isStrictRequirements();
|
||||
public function isStrictRequirements(): ?bool;
|
||||
}
|
||||
|
||||
124
vendor/symfony/routing/Generator/Dumper/CompiledUrlGeneratorDumper.php
vendored
Normal file
124
vendor/symfony/routing/Generator/Dumper/CompiledUrlGeneratorDumper.php
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Generator\Dumper;
|
||||
|
||||
use Symfony\Component\Routing\Exception\RouteCircularReferenceException;
|
||||
use Symfony\Component\Routing\Exception\RouteNotFoundException;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper;
|
||||
|
||||
/**
|
||||
* CompiledUrlGeneratorDumper creates a PHP array to be used with CompiledUrlGenerator.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class CompiledUrlGeneratorDumper extends GeneratorDumper
|
||||
{
|
||||
public function getCompiledRoutes(): array
|
||||
{
|
||||
$compiledRoutes = [];
|
||||
foreach ($this->getRoutes()->all() as $name => $route) {
|
||||
$compiledRoute = $route->compile();
|
||||
|
||||
$compiledRoutes[$name] = [
|
||||
$compiledRoute->getVariables(),
|
||||
$route->getDefaults(),
|
||||
$route->getRequirements(),
|
||||
$compiledRoute->getTokens(),
|
||||
$compiledRoute->getHostTokens(),
|
||||
$route->getSchemes(),
|
||||
[],
|
||||
];
|
||||
}
|
||||
|
||||
return $compiledRoutes;
|
||||
}
|
||||
|
||||
public function getCompiledAliases(): array
|
||||
{
|
||||
$routes = $this->getRoutes();
|
||||
$compiledAliases = [];
|
||||
foreach ($routes->getAliases() as $name => $alias) {
|
||||
$deprecations = $alias->isDeprecated() ? [$alias->getDeprecation($name)] : [];
|
||||
$currentId = $alias->getId();
|
||||
$visited = [];
|
||||
while (null !== $alias = $routes->getAlias($currentId) ?? null) {
|
||||
if (false !== $searchKey = array_search($currentId, $visited)) {
|
||||
$visited[] = $currentId;
|
||||
|
||||
throw new RouteCircularReferenceException($currentId, \array_slice($visited, $searchKey));
|
||||
}
|
||||
|
||||
if ($alias->isDeprecated()) {
|
||||
$deprecations[] = $deprecation = $alias->getDeprecation($currentId);
|
||||
trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']);
|
||||
}
|
||||
|
||||
$visited[] = $currentId;
|
||||
$currentId = $alias->getId();
|
||||
}
|
||||
|
||||
if (null === $target = $routes->get($currentId)) {
|
||||
throw new RouteNotFoundException(sprintf('Target route "%s" for alias "%s" does not exist.', $currentId, $name));
|
||||
}
|
||||
|
||||
$compiledTarget = $target->compile();
|
||||
|
||||
$compiledAliases[$name] = [
|
||||
$compiledTarget->getVariables(),
|
||||
$target->getDefaults(),
|
||||
$target->getRequirements(),
|
||||
$compiledTarget->getTokens(),
|
||||
$compiledTarget->getHostTokens(),
|
||||
$target->getSchemes(),
|
||||
$deprecations,
|
||||
];
|
||||
}
|
||||
|
||||
return $compiledAliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump(array $options = []): string
|
||||
{
|
||||
return <<<EOF
|
||||
<?php
|
||||
|
||||
// This file has been auto-generated by the Symfony Routing Component.
|
||||
|
||||
return [{$this->generateDeclaredRoutes()}
|
||||
];
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates PHP code representing an array of defined routes
|
||||
* together with the routes properties (e.g. requirements).
|
||||
*/
|
||||
private function generateDeclaredRoutes(): string
|
||||
{
|
||||
$routes = '';
|
||||
foreach ($this->getCompiledRoutes() as $name => $properties) {
|
||||
$routes .= sprintf("\n '%s' => %s,", $name, CompiledUrlMatcherDumper::export($properties));
|
||||
}
|
||||
|
||||
foreach ($this->getCompiledAliases() as $alias => $properties) {
|
||||
$routes .= sprintf("\n '%s' => %s,", $alias, CompiledUrlMatcherDumper::export($properties));
|
||||
}
|
||||
|
||||
return $routes;
|
||||
}
|
||||
}
|
||||
@@ -20,16 +20,8 @@ use Symfony\Component\Routing\RouteCollection;
|
||||
*/
|
||||
abstract class GeneratorDumper implements GeneratorDumperInterface
|
||||
{
|
||||
/**
|
||||
* @var RouteCollection
|
||||
*/
|
||||
private $routes;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param RouteCollection $routes The RouteCollection to dump
|
||||
*/
|
||||
public function __construct(RouteCollection $routes)
|
||||
{
|
||||
$this->routes = $routes;
|
||||
@@ -38,7 +30,7 @@ abstract class GeneratorDumper implements GeneratorDumperInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRoutes()
|
||||
public function getRoutes(): RouteCollection
|
||||
{
|
||||
return $this->routes;
|
||||
}
|
||||
|
||||
@@ -23,17 +23,11 @@ interface GeneratorDumperInterface
|
||||
/**
|
||||
* Dumps a set of routes to a string representation of executable code
|
||||
* that can then be used to generate a URL of such a route.
|
||||
*
|
||||
* @param array $options An array of options
|
||||
*
|
||||
* @return string Executable code
|
||||
*/
|
||||
public function dump(array $options = array());
|
||||
public function dump(array $options = []): string;
|
||||
|
||||
/**
|
||||
* Gets the routes to dump.
|
||||
*
|
||||
* @return RouteCollection A RouteCollection instance
|
||||
*/
|
||||
public function getRoutes();
|
||||
public function getRoutes(): RouteCollection;
|
||||
}
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Generator\Dumper;
|
||||
|
||||
/**
|
||||
* PhpGeneratorDumper creates a PHP class able to generate URLs for a given set of routes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*/
|
||||
class PhpGeneratorDumper extends GeneratorDumper
|
||||
{
|
||||
/**
|
||||
* Dumps a set of routes to a PHP class.
|
||||
*
|
||||
* Available options:
|
||||
*
|
||||
* * class: The class name
|
||||
* * base_class: The base class name
|
||||
*
|
||||
* @param array $options An array of options
|
||||
*
|
||||
* @return string A PHP class representing the generator class
|
||||
*/
|
||||
public function dump(array $options = array())
|
||||
{
|
||||
$options = array_merge(array(
|
||||
'class' => 'ProjectUrlGenerator',
|
||||
'base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
|
||||
), $options);
|
||||
|
||||
return <<<EOF
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\Exception\RouteNotFoundException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* {$options['class']}
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
class {$options['class']} extends {$options['base_class']}
|
||||
{
|
||||
private static \$declaredRoutes;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(RequestContext \$context, LoggerInterface \$logger = null)
|
||||
{
|
||||
\$this->context = \$context;
|
||||
\$this->logger = \$logger;
|
||||
if (null === self::\$declaredRoutes) {
|
||||
self::\$declaredRoutes = {$this->generateDeclaredRoutes()};
|
||||
}
|
||||
}
|
||||
|
||||
{$this->generateGenerateMethod()}
|
||||
}
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates PHP code representing an array of defined routes
|
||||
* together with the routes properties (e.g. requirements).
|
||||
*
|
||||
* @return string PHP code
|
||||
*/
|
||||
private function generateDeclaredRoutes()
|
||||
{
|
||||
$routes = "array(\n";
|
||||
foreach ($this->getRoutes()->all() as $name => $route) {
|
||||
$compiledRoute = $route->compile();
|
||||
|
||||
$properties = array();
|
||||
$properties[] = $compiledRoute->getVariables();
|
||||
$properties[] = $route->getDefaults();
|
||||
$properties[] = $route->getRequirements();
|
||||
$properties[] = $compiledRoute->getTokens();
|
||||
$properties[] = $compiledRoute->getHostTokens();
|
||||
$properties[] = $route->getSchemes();
|
||||
|
||||
$routes .= sprintf(" '%s' => %s,\n", $name, str_replace("\n", '', var_export($properties, true)));
|
||||
}
|
||||
$routes .= ' )';
|
||||
|
||||
return $routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates PHP code representing the `generate` method that implements the UrlGeneratorInterface.
|
||||
*
|
||||
* @return string PHP code
|
||||
*/
|
||||
private function generateGenerateMethod()
|
||||
{
|
||||
return <<<'EOF'
|
||||
public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
|
||||
{
|
||||
if (!isset(self::$declaredRoutes[$name])) {
|
||||
throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name));
|
||||
}
|
||||
|
||||
list($variables, $defaults, $requirements, $tokens, $hostTokens, $requiredSchemes) = self::$declaredRoutes[$name];
|
||||
|
||||
return $this->doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, $requiredSchemes);
|
||||
}
|
||||
EOF;
|
||||
}
|
||||
}
|
||||
215
vendor/symfony/routing/Generator/UrlGenerator.php
vendored
215
vendor/symfony/routing/Generator/UrlGenerator.php
vendored
@@ -11,12 +11,12 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Generator;
|
||||
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\Exception\InvalidParameterException;
|
||||
use Symfony\Component\Routing\Exception\RouteNotFoundException;
|
||||
use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Routing\Exception\InvalidParameterException;
|
||||
use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
|
||||
use Symfony\Component\Routing\Exception\RouteNotFoundException;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* UrlGenerator can generate a URL or a path for any route in the RouteCollection
|
||||
@@ -27,14 +27,21 @@ use Psr\Log\LoggerInterface;
|
||||
*/
|
||||
class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInterface
|
||||
{
|
||||
/**
|
||||
* @var RouteCollection
|
||||
*/
|
||||
protected $routes;
|
||||
private const QUERY_FRAGMENT_DECODED = [
|
||||
// RFC 3986 explicitly allows those in the query/fragment to reference other URIs unencoded
|
||||
'%2F' => '/',
|
||||
'%3F' => '?',
|
||||
// reserved chars that have no special meaning for HTTP URIs in a query or fragment
|
||||
// this excludes esp. "&", "=" and also "+" because PHP would treat it as a space (form-encoded)
|
||||
'%40' => '@',
|
||||
'%3A' => ':',
|
||||
'%21' => '!',
|
||||
'%3B' => ';',
|
||||
'%2C' => ',',
|
||||
'%2A' => '*',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var RequestContext
|
||||
*/
|
||||
protected $routes;
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
@@ -42,11 +49,10 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
||||
*/
|
||||
protected $strictRequirements = true;
|
||||
|
||||
/**
|
||||
* @var LoggerInterface|null
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
private ?string $defaultLocale;
|
||||
|
||||
/**
|
||||
* This array defines the characters (besides alphanumeric ones) that will not be percent-encoded in the path segment of the generated URL.
|
||||
*
|
||||
@@ -55,11 +61,12 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
||||
* "?" and "#" (would be interpreted wrongly as query and fragment identifier),
|
||||
* "'" and """ (are used as delimiters in HTML).
|
||||
*/
|
||||
protected $decodedChars = array(
|
||||
protected $decodedChars = [
|
||||
// the slash can be used to designate a hierarchical structure and we want allow using it with this meaning
|
||||
// some webservers don't allow the slash in encoded form in the path for security reasons anyway
|
||||
// see http://stackoverflow.com/questions/4069002/http-400-if-2f-part-of-get-url-in-jboss
|
||||
'%2F' => '/',
|
||||
'%252F' => '%2F',
|
||||
// the following chars are general delimiters in the URI specification but have only special meaning in the authority component
|
||||
// so they can safely be used in the path in unencoded form
|
||||
'%40' => '@',
|
||||
@@ -73,20 +80,14 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
||||
'%21' => '!',
|
||||
'%2A' => '*',
|
||||
'%7C' => '|',
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param RouteCollection $routes A RouteCollection instance
|
||||
* @param RequestContext $context The context
|
||||
* @param LoggerInterface|null $logger A logger instance
|
||||
*/
|
||||
public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null)
|
||||
public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null, string $defaultLocale = null)
|
||||
{
|
||||
$this->routes = $routes;
|
||||
$this->context = $context;
|
||||
$this->logger = $logger;
|
||||
$this->defaultLocale = $defaultLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +101,7 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext()
|
||||
public function getContext(): RequestContext
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
@@ -108,15 +109,15 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setStrictRequirements($enabled)
|
||||
public function setStrictRequirements(?bool $enabled)
|
||||
{
|
||||
$this->strictRequirements = null === $enabled ? null : (bool) $enabled;
|
||||
$this->strictRequirements = $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isStrictRequirements()
|
||||
public function isStrictRequirements(): ?bool
|
||||
{
|
||||
return $this->strictRequirements;
|
||||
}
|
||||
@@ -124,16 +125,40 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
|
||||
public function generate(string $name, array $parameters = [], int $referenceType = self::ABSOLUTE_PATH): string
|
||||
{
|
||||
if (null === $route = $this->routes->get($name)) {
|
||||
$route = null;
|
||||
$locale = $parameters['_locale']
|
||||
?? $this->context->getParameter('_locale')
|
||||
?: $this->defaultLocale;
|
||||
|
||||
if (null !== $locale) {
|
||||
do {
|
||||
if (null !== ($route = $this->routes->get($name.'.'.$locale)) && $route->getDefault('_canonical_route') === $name) {
|
||||
break;
|
||||
}
|
||||
} while (false !== $locale = strstr($locale, '_', true));
|
||||
}
|
||||
|
||||
if (null === $route = $route ?? $this->routes->get($name)) {
|
||||
throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name));
|
||||
}
|
||||
|
||||
// the Route has a cache of its own and is not recompiled as long as it does not get modified
|
||||
$compiledRoute = $route->compile();
|
||||
|
||||
return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $referenceType, $compiledRoute->getHostTokens(), $route->getSchemes());
|
||||
$defaults = $route->getDefaults();
|
||||
$variables = $compiledRoute->getVariables();
|
||||
|
||||
if (isset($defaults['_canonical_route']) && isset($defaults['_locale'])) {
|
||||
if (!\in_array('_locale', $variables, true)) {
|
||||
unset($parameters['_locale']);
|
||||
} elseif (!isset($parameters['_locale'])) {
|
||||
$parameters['_locale'] = $defaults['_locale'];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->doGenerate($variables, $defaults, $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $referenceType, $compiledRoute->getHostTokens(), $route->getSchemes());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,7 +166,7 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
||||
* @throws InvalidParameterException When a parameter value for a placeholder is not correct because
|
||||
* it does not match the requirement
|
||||
*/
|
||||
protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, array $requiredSchemes = array())
|
||||
protected function doGenerate(array $variables, array $defaults, array $requirements, array $tokens, array $parameters, string $name, int $referenceType, array $hostTokens, array $requiredSchemes = []): string
|
||||
{
|
||||
$variables = array_flip($variables);
|
||||
$mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters);
|
||||
@@ -156,21 +181,25 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
||||
$message = 'Parameter "{parameter}" for route "{route}" must match "{expected}" ("{given}" given) to generate a corresponding URL.';
|
||||
foreach ($tokens as $token) {
|
||||
if ('variable' === $token[0]) {
|
||||
if (!$optional || !array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) {
|
||||
// check requirement
|
||||
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
|
||||
$varName = $token[3];
|
||||
// variable is not important by default
|
||||
$important = $token[5] ?? false;
|
||||
|
||||
if (!$optional || $important || !\array_key_exists($varName, $defaults) || (null !== $mergedParams[$varName] && (string) $mergedParams[$varName] !== (string) $defaults[$varName])) {
|
||||
// check requirement (while ignoring look-around patterns)
|
||||
if (null !== $this->strictRequirements && !preg_match('#^'.preg_replace('/\(\?(?:=|<=|!|<!)((?:[^()\\\\]+|\\\\.|\((?1)\))*)\)/', '', $token[2]).'$#i'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]] ?? '')) {
|
||||
if ($this->strictRequirements) {
|
||||
throw new InvalidParameterException(strtr($message, array('{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]])));
|
||||
throw new InvalidParameterException(strtr($message, ['{parameter}' => $varName, '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$varName]]));
|
||||
}
|
||||
|
||||
if ($this->logger) {
|
||||
$this->logger->error($message, array('parameter' => $token[3], 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$token[3]]));
|
||||
$this->logger->error($message, ['parameter' => $varName, 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$varName]]);
|
||||
}
|
||||
|
||||
return;
|
||||
return '';
|
||||
}
|
||||
|
||||
$url = $token[1].$mergedParams[$token[3]].$url;
|
||||
$url = $token[1].$mergedParams[$varName].$url;
|
||||
$optional = false;
|
||||
}
|
||||
} else {
|
||||
@@ -190,63 +219,65 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
||||
// the path segments "." and ".." are interpreted as relative reference when resolving a URI; see http://tools.ietf.org/html/rfc3986#section-3.3
|
||||
// so we need to encode them as they are not used for this purpose here
|
||||
// otherwise we would generate a URI that, when followed by a user agent (e.g. browser), does not match this route
|
||||
$url = strtr($url, array('/../' => '/%2E%2E/', '/./' => '/%2E/'));
|
||||
if ('/..' === substr($url, -3)) {
|
||||
$url = strtr($url, ['/../' => '/%2E%2E/', '/./' => '/%2E/']);
|
||||
if (str_ends_with($url, '/..')) {
|
||||
$url = substr($url, 0, -2).'%2E%2E';
|
||||
} elseif ('/.' === substr($url, -2)) {
|
||||
} elseif (str_ends_with($url, '/.')) {
|
||||
$url = substr($url, 0, -1).'%2E';
|
||||
}
|
||||
|
||||
$schemeAuthority = '';
|
||||
if ($host = $this->context->getHost()) {
|
||||
$scheme = $this->context->getScheme();
|
||||
$host = $this->context->getHost();
|
||||
$scheme = $this->context->getScheme();
|
||||
|
||||
if ($requiredSchemes) {
|
||||
if (!in_array($scheme, $requiredSchemes, true)) {
|
||||
$referenceType = self::ABSOLUTE_URL;
|
||||
$scheme = current($requiredSchemes);
|
||||
}
|
||||
if ($requiredSchemes) {
|
||||
if (!\in_array($scheme, $requiredSchemes, true)) {
|
||||
$referenceType = self::ABSOLUTE_URL;
|
||||
$scheme = current($requiredSchemes);
|
||||
}
|
||||
}
|
||||
|
||||
if ($hostTokens) {
|
||||
$routeHost = '';
|
||||
foreach ($hostTokens as $token) {
|
||||
if ('variable' === $token[0]) {
|
||||
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#i'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
|
||||
if ($this->strictRequirements) {
|
||||
throw new InvalidParameterException(strtr($message, array('{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]])));
|
||||
}
|
||||
|
||||
if ($this->logger) {
|
||||
$this->logger->error($message, array('parameter' => $token[3], 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$token[3]]));
|
||||
}
|
||||
|
||||
return;
|
||||
if ($hostTokens) {
|
||||
$routeHost = '';
|
||||
foreach ($hostTokens as $token) {
|
||||
if ('variable' === $token[0]) {
|
||||
// check requirement (while ignoring look-around patterns)
|
||||
if (null !== $this->strictRequirements && !preg_match('#^'.preg_replace('/\(\?(?:=|<=|!|<!)((?:[^()\\\\]+|\\\\.|\((?1)\))*)\)/', '', $token[2]).'$#i'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
|
||||
if ($this->strictRequirements) {
|
||||
throw new InvalidParameterException(strtr($message, ['{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]]]));
|
||||
}
|
||||
|
||||
$routeHost = $token[1].$mergedParams[$token[3]].$routeHost;
|
||||
} else {
|
||||
$routeHost = $token[1].$routeHost;
|
||||
}
|
||||
}
|
||||
if ($this->logger) {
|
||||
$this->logger->error($message, ['parameter' => $token[3], 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$token[3]]]);
|
||||
}
|
||||
|
||||
if ($routeHost !== $host) {
|
||||
$host = $routeHost;
|
||||
if (self::ABSOLUTE_URL !== $referenceType) {
|
||||
$referenceType = self::NETWORK_PATH;
|
||||
return '';
|
||||
}
|
||||
|
||||
$routeHost = $token[1].$mergedParams[$token[3]].$routeHost;
|
||||
} else {
|
||||
$routeHost = $token[1].$routeHost;
|
||||
}
|
||||
}
|
||||
|
||||
if (self::ABSOLUTE_URL === $referenceType || self::NETWORK_PATH === $referenceType) {
|
||||
if ($routeHost !== $host) {
|
||||
$host = $routeHost;
|
||||
if (self::ABSOLUTE_URL !== $referenceType) {
|
||||
$referenceType = self::NETWORK_PATH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self::ABSOLUTE_URL === $referenceType || self::NETWORK_PATH === $referenceType) {
|
||||
if ('' !== $host || ('' !== $scheme && 'http' !== $scheme && 'https' !== $scheme)) {
|
||||
$port = '';
|
||||
if ('http' === $scheme && 80 != $this->context->getHttpPort()) {
|
||||
if ('http' === $scheme && 80 !== $this->context->getHttpPort()) {
|
||||
$port = ':'.$this->context->getHttpPort();
|
||||
} elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) {
|
||||
} elseif ('https' === $scheme && 443 !== $this->context->getHttpsPort()) {
|
||||
$port = ':'.$this->context->getHttpsPort();
|
||||
}
|
||||
|
||||
$schemeAuthority = self::NETWORK_PATH === $referenceType ? '//' : "$scheme://";
|
||||
$schemeAuthority = self::NETWORK_PATH === $referenceType || '' === $scheme ? '//' : "$scheme://";
|
||||
$schemeAuthority .= $host.$port;
|
||||
}
|
||||
}
|
||||
@@ -262,25 +293,31 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
||||
return $a == $b ? 0 : 1;
|
||||
});
|
||||
|
||||
array_walk_recursive($extra, $caster = static function (&$v) use (&$caster) {
|
||||
if (\is_object($v)) {
|
||||
if ($vars = get_object_vars($v)) {
|
||||
array_walk_recursive($vars, $caster);
|
||||
$v = $vars;
|
||||
} elseif (method_exists($v, '__toString')) {
|
||||
$v = (string) $v;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// extract fragment
|
||||
$fragment = '';
|
||||
if (isset($defaults['_fragment'])) {
|
||||
$fragment = $defaults['_fragment'];
|
||||
}
|
||||
$fragment = $defaults['_fragment'] ?? '';
|
||||
|
||||
if (isset($extra['_fragment'])) {
|
||||
$fragment = $extra['_fragment'];
|
||||
unset($extra['_fragment']);
|
||||
}
|
||||
|
||||
if ($extra && $query = http_build_query($extra, '', '&', PHP_QUERY_RFC3986)) {
|
||||
// "/" and "?" can be left decoded for better user experience, see
|
||||
// http://tools.ietf.org/html/rfc3986#section-3.4
|
||||
$url .= '?'.strtr($query, array('%2F' => '/'));
|
||||
if ($extra && $query = http_build_query($extra, '', '&', \PHP_QUERY_RFC3986)) {
|
||||
$url .= '?'.strtr($query, self::QUERY_FRAGMENT_DECODED);
|
||||
}
|
||||
|
||||
if ('' !== $fragment) {
|
||||
$url .= '#'.strtr(rawurlencode($fragment), array('%2F' => '/', '%3F' => '?'));
|
||||
$url .= '#'.strtr(rawurlencode($fragment), self::QUERY_FRAGMENT_DECODED);
|
||||
}
|
||||
|
||||
return $url;
|
||||
@@ -303,10 +340,8 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
||||
*
|
||||
* @param string $basePath The base path
|
||||
* @param string $targetPath The target path
|
||||
*
|
||||
* @return string The relative target path
|
||||
*/
|
||||
public static function getRelativePath($basePath, $targetPath)
|
||||
public static function getRelativePath(string $basePath, string $targetPath): string
|
||||
{
|
||||
if ($basePath === $targetPath) {
|
||||
return '';
|
||||
@@ -326,7 +361,7 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
||||
}
|
||||
|
||||
$targetDirs[] = $targetFile;
|
||||
$path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs);
|
||||
$path = str_repeat('../', \count($sourceDirs)).implode('/', $targetDirs);
|
||||
|
||||
// A reference to the same base directory or an empty subdirectory must be prefixed with "./".
|
||||
// This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
|
||||
|
||||
@@ -34,25 +34,25 @@ interface UrlGeneratorInterface extends RequestContextAwareInterface
|
||||
/**
|
||||
* Generates an absolute URL, e.g. "http://example.com/dir/file".
|
||||
*/
|
||||
const ABSOLUTE_URL = 0;
|
||||
public const ABSOLUTE_URL = 0;
|
||||
|
||||
/**
|
||||
* Generates an absolute path, e.g. "/dir/file".
|
||||
*/
|
||||
const ABSOLUTE_PATH = 1;
|
||||
public const ABSOLUTE_PATH = 1;
|
||||
|
||||
/**
|
||||
* Generates a relative path based on the current request path, e.g. "../parent-file".
|
||||
*
|
||||
* @see UrlGenerator::getRelativePath()
|
||||
*/
|
||||
const RELATIVE_PATH = 2;
|
||||
public const RELATIVE_PATH = 2;
|
||||
|
||||
/**
|
||||
* Generates a network path, e.g. "//example.com/dir/file".
|
||||
* Such reference reuses the current scheme but specifies the host.
|
||||
*/
|
||||
const NETWORK_PATH = 3;
|
||||
public const NETWORK_PATH = 3;
|
||||
|
||||
/**
|
||||
* Generates a URL or path for a specific route based on the given parameters.
|
||||
@@ -71,16 +71,10 @@ interface UrlGeneratorInterface extends RequestContextAwareInterface
|
||||
*
|
||||
* The special parameter _fragment will be used as the document fragment suffixed to the final URL.
|
||||
*
|
||||
* @param string $name The name of the route
|
||||
* @param mixed $parameters An array of parameters
|
||||
* @param int $referenceType The type of reference to be generated (one of the constants)
|
||||
*
|
||||
* @return string The generated URL
|
||||
*
|
||||
* @throws RouteNotFoundException If the named route doesn't exist
|
||||
* @throws MissingMandatoryParametersException When some parameters are missing that are mandatory for the route
|
||||
* @throws InvalidParameterException When a parameter value for a placeholder is not correct because
|
||||
* it does not match the requirement
|
||||
*/
|
||||
public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH);
|
||||
public function generate(string $name, array $parameters = [], int $referenceType = self::ABSOLUTE_PATH): string;
|
||||
}
|
||||
|
||||
2
vendor/symfony/routing/LICENSE
vendored
2
vendor/symfony/routing/LICENSE
vendored
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2004-2017 Fabien Potencier
|
||||
Copyright (c) 2004-2022 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -12,16 +12,17 @@
|
||||
namespace Symfony\Component\Routing\Loader;
|
||||
|
||||
use Doctrine\Common\Annotations\Reader;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\Config\Loader\LoaderResolverInterface;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Routing\Annotation\Route as RouteAnnotation;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* AnnotationClassLoader loads routing information from a PHP class and its methods.
|
||||
*
|
||||
* You need to define an implementation for the getRouteDefaults() method. Most of the
|
||||
* You need to define an implementation for the configureRoute() method. Most of the
|
||||
* time, this method should define some PHP callable to be called for the route
|
||||
* (a controller in MVC speak).
|
||||
*
|
||||
@@ -32,7 +33,6 @@ use Symfony\Component\Config\Loader\LoaderResolverInterface;
|
||||
* recognizes several parameters: requirements, options, defaults, schemes,
|
||||
* methods, host, and name. The name parameter is mandatory.
|
||||
* Here is an example of how you should be able to use it:
|
||||
*
|
||||
* /**
|
||||
* * @Route("/Blog")
|
||||
* * /
|
||||
@@ -44,7 +44,6 @@ use Symfony\Component\Config\Loader\LoaderResolverInterface;
|
||||
* public function index()
|
||||
* {
|
||||
* }
|
||||
*
|
||||
* /**
|
||||
* * @Route("/{id}", name="blog_post", requirements = {"id" = "\d+"})
|
||||
* * /
|
||||
@@ -52,42 +51,50 @@ use Symfony\Component\Config\Loader\LoaderResolverInterface;
|
||||
* {
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* On PHP 8, the annotation class can be used as an attribute as well:
|
||||
* #[Route('/Blog')]
|
||||
* class Blog
|
||||
* {
|
||||
* #[Route('/', name: 'blog_index')]
|
||||
* public function index()
|
||||
* {
|
||||
* }
|
||||
* #[Route('/{id}', name: 'blog_post', requirements: ["id" => '\d+'])]
|
||||
* public function show()
|
||||
* {
|
||||
* }
|
||||
* }
|
||||
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Alexander M. Turek <me@derrabus.de>
|
||||
*/
|
||||
abstract class AnnotationClassLoader implements LoaderInterface
|
||||
{
|
||||
/**
|
||||
* @var Reader
|
||||
*/
|
||||
protected $reader;
|
||||
protected $env;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $routeAnnotationClass = 'Symfony\\Component\\Routing\\Annotation\\Route';
|
||||
protected $routeAnnotationClass = RouteAnnotation::class;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $defaultRouteIndex = 0;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Reader $reader
|
||||
*/
|
||||
public function __construct(Reader $reader)
|
||||
public function __construct(Reader $reader = null, string $env = null)
|
||||
{
|
||||
$this->reader = $reader;
|
||||
$this->env = $env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the annotation class to read route properties from.
|
||||
*
|
||||
* @param string $class A fully-qualified class name
|
||||
*/
|
||||
public function setRouteAnnotationClass($class)
|
||||
public function setRouteAnnotationClass(string $class)
|
||||
{
|
||||
$this->routeAnnotationClass = $class;
|
||||
}
|
||||
@@ -95,14 +102,9 @@ abstract class AnnotationClassLoader implements LoaderInterface
|
||||
/**
|
||||
* Loads from annotations from a class.
|
||||
*
|
||||
* @param string $class A class name
|
||||
* @param string|null $type The resource type
|
||||
*
|
||||
* @return RouteCollection A RouteCollection instance
|
||||
*
|
||||
* @throws \InvalidArgumentException When route can't be parsed
|
||||
*/
|
||||
public function load($class, $type = null)
|
||||
public function load(mixed $class, string $type = null): RouteCollection
|
||||
{
|
||||
if (!class_exists($class)) {
|
||||
throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
|
||||
@@ -118,37 +120,52 @@ abstract class AnnotationClassLoader implements LoaderInterface
|
||||
$collection = new RouteCollection();
|
||||
$collection->addResource(new FileResource($class->getFileName()));
|
||||
|
||||
if ($globals['env'] && $this->env !== $globals['env']) {
|
||||
return $collection;
|
||||
}
|
||||
|
||||
foreach ($class->getMethods() as $method) {
|
||||
$this->defaultRouteIndex = 0;
|
||||
foreach ($this->reader->getMethodAnnotations($method) as $annot) {
|
||||
if ($annot instanceof $this->routeAnnotationClass) {
|
||||
$this->addRoute($collection, $annot, $globals, $class, $method);
|
||||
}
|
||||
foreach ($this->getAnnotations($method) as $annot) {
|
||||
$this->addRoute($collection, $annot, $globals, $class, $method);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 === $collection->count() && $class->hasMethod('__invoke') && $annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass)) {
|
||||
$globals['path'] = '';
|
||||
$this->addRoute($collection, $annot, $globals, $class, $class->getMethod('__invoke'));
|
||||
if (0 === $collection->count() && $class->hasMethod('__invoke')) {
|
||||
$globals = $this->resetGlobals();
|
||||
foreach ($this->getAnnotations($class) as $annot) {
|
||||
$this->addRoute($collection, $annot, $globals, $class, $class->getMethod('__invoke'));
|
||||
}
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
protected function addRoute(RouteCollection $collection, $annot, $globals, \ReflectionClass $class, \ReflectionMethod $method)
|
||||
/**
|
||||
* @param RouteAnnotation $annot or an object that exposes a similar interface
|
||||
*/
|
||||
protected function addRoute(RouteCollection $collection, object $annot, array $globals, \ReflectionClass $class, \ReflectionMethod $method)
|
||||
{
|
||||
if ($annot->getEnv() && $annot->getEnv() !== $this->env) {
|
||||
return;
|
||||
}
|
||||
|
||||
$name = $annot->getName();
|
||||
if (null === $name) {
|
||||
$name = $this->getDefaultRouteName($class, $method);
|
||||
}
|
||||
$name = $globals['name'].$name;
|
||||
|
||||
$defaults = array_replace($globals['defaults'], $annot->getDefaults());
|
||||
foreach ($method->getParameters() as $param) {
|
||||
if (false !== strpos($globals['path'].$annot->getPath(), sprintf('{%s}', $param->getName())) && !isset($defaults[$param->getName()]) && $param->isDefaultValueAvailable()) {
|
||||
$defaults[$param->getName()] = $param->getDefaultValue();
|
||||
$requirements = $annot->getRequirements();
|
||||
|
||||
foreach ($requirements as $placeholder => $requirement) {
|
||||
if (\is_int($placeholder)) {
|
||||
throw new \InvalidArgumentException(sprintf('A placeholder name must be a string (%d given). Did you forget to specify the placeholder key for the requirement "%s" of route "%s" in "%s::%s()"?', $placeholder, $requirement, $name, $class->getName(), $method->getName()));
|
||||
}
|
||||
}
|
||||
$requirements = array_replace($globals['requirements'], $annot->getRequirements());
|
||||
|
||||
$defaults = array_replace($globals['defaults'], $annot->getDefaults());
|
||||
$requirements = array_replace($globals['requirements'], $requirements);
|
||||
$options = array_replace($globals['options'], $annot->getOptions());
|
||||
$schemes = array_merge($globals['schemes'], $annot->getSchemes());
|
||||
$methods = array_merge($globals['methods'], $annot->getMethods());
|
||||
@@ -158,24 +175,69 @@ abstract class AnnotationClassLoader implements LoaderInterface
|
||||
$host = $globals['host'];
|
||||
}
|
||||
|
||||
$condition = $annot->getCondition();
|
||||
if (null === $condition) {
|
||||
$condition = $globals['condition'];
|
||||
$condition = $annot->getCondition() ?? $globals['condition'];
|
||||
$priority = $annot->getPriority() ?? $globals['priority'];
|
||||
|
||||
$path = $annot->getLocalizedPaths() ?: $annot->getPath();
|
||||
$prefix = $globals['localized_paths'] ?: $globals['path'];
|
||||
$paths = [];
|
||||
|
||||
if (\is_array($path)) {
|
||||
if (!\is_array($prefix)) {
|
||||
foreach ($path as $locale => $localePath) {
|
||||
$paths[$locale] = $prefix.$localePath;
|
||||
}
|
||||
} elseif ($missing = array_diff_key($prefix, $path)) {
|
||||
throw new \LogicException(sprintf('Route to "%s" is missing paths for locale(s) "%s".', $class->name.'::'.$method->name, implode('", "', array_keys($missing))));
|
||||
} else {
|
||||
foreach ($path as $locale => $localePath) {
|
||||
if (!isset($prefix[$locale])) {
|
||||
throw new \LogicException(sprintf('Route to "%s" with locale "%s" is missing a corresponding prefix in class "%s".', $method->name, $locale, $class->name));
|
||||
}
|
||||
|
||||
$paths[$locale] = $prefix[$locale].$localePath;
|
||||
}
|
||||
}
|
||||
} elseif (\is_array($prefix)) {
|
||||
foreach ($prefix as $locale => $localePrefix) {
|
||||
$paths[$locale] = $localePrefix.$path;
|
||||
}
|
||||
} else {
|
||||
$paths[] = $prefix.$path;
|
||||
}
|
||||
|
||||
$route = $this->createRoute($globals['path'].$annot->getPath(), $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
|
||||
foreach ($method->getParameters() as $param) {
|
||||
if (isset($defaults[$param->name]) || !$param->isDefaultValueAvailable()) {
|
||||
continue;
|
||||
}
|
||||
foreach ($paths as $locale => $path) {
|
||||
if (preg_match(sprintf('/\{%s(?:<.*?>)?\}/', preg_quote($param->name)), $path)) {
|
||||
$defaults[$param->name] = $param->getDefaultValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->configureRoute($route, $class, $method, $annot);
|
||||
|
||||
$collection->add($name, $route);
|
||||
foreach ($paths as $locale => $path) {
|
||||
$route = $this->createRoute($path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
|
||||
$this->configureRoute($route, $class, $method, $annot);
|
||||
if (0 !== $locale) {
|
||||
$route->setDefault('_locale', $locale);
|
||||
$route->setRequirement('_locale', preg_quote($locale));
|
||||
$route->setDefault('_canonical_route', $name);
|
||||
$collection->add($name.'.'.$locale, $route, $priority);
|
||||
} else {
|
||||
$collection->add($name, $route, $priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports($resource, $type = null)
|
||||
public function supports(mixed $resource, string $type = null): bool
|
||||
{
|
||||
return is_string($resource) && preg_match('/^(?:\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/', $resource) && (!$type || 'annotation' === $type);
|
||||
return \is_string($resource) && preg_match('/^(?:\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/', $resource) && (!$type || 'annotation' === $type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,21 +250,19 @@ abstract class AnnotationClassLoader implements LoaderInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getResolver()
|
||||
public function getResolver(): LoaderResolverInterface
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default route name for a class method.
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
* @param \ReflectionMethod $method
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method)
|
||||
{
|
||||
$name = strtolower(str_replace('\\', '_', $class->name).'_'.$method->name);
|
||||
$name = str_replace('\\', '_', $class->name).'_'.$method->name;
|
||||
$name = \function_exists('mb_strtolower') && preg_match('//u', $name) ? mb_strtolower($name, 'UTF-8') : strtolower($name);
|
||||
if ($this->defaultRouteIndex > 0) {
|
||||
$name .= '_'.$this->defaultRouteIndex;
|
||||
}
|
||||
@@ -213,22 +273,27 @@ abstract class AnnotationClassLoader implements LoaderInterface
|
||||
|
||||
protected function getGlobals(\ReflectionClass $class)
|
||||
{
|
||||
$globals = array(
|
||||
'path' => '',
|
||||
'requirements' => array(),
|
||||
'options' => array(),
|
||||
'defaults' => array(),
|
||||
'schemes' => array(),
|
||||
'methods' => array(),
|
||||
'host' => '',
|
||||
'condition' => '',
|
||||
);
|
||||
$globals = $this->resetGlobals();
|
||||
|
||||
$annot = null;
|
||||
if ($attribute = $class->getAttributes($this->routeAnnotationClass, \ReflectionAttribute::IS_INSTANCEOF)[0] ?? null) {
|
||||
$annot = $attribute->newInstance();
|
||||
}
|
||||
if (!$annot && $this->reader) {
|
||||
$annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass);
|
||||
}
|
||||
|
||||
if ($annot) {
|
||||
if (null !== $annot->getName()) {
|
||||
$globals['name'] = $annot->getName();
|
||||
}
|
||||
|
||||
if ($annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass)) {
|
||||
if (null !== $annot->getPath()) {
|
||||
$globals['path'] = $annot->getPath();
|
||||
}
|
||||
|
||||
$globals['localized_paths'] = $annot->getLocalizedPaths();
|
||||
|
||||
if (null !== $annot->getRequirements()) {
|
||||
$globals['requirements'] = $annot->getRequirements();
|
||||
}
|
||||
@@ -256,15 +321,68 @@ abstract class AnnotationClassLoader implements LoaderInterface
|
||||
if (null !== $annot->getCondition()) {
|
||||
$globals['condition'] = $annot->getCondition();
|
||||
}
|
||||
|
||||
$globals['priority'] = $annot->getPriority() ?? 0;
|
||||
$globals['env'] = $annot->getEnv();
|
||||
|
||||
foreach ($globals['requirements'] as $placeholder => $requirement) {
|
||||
if (\is_int($placeholder)) {
|
||||
throw new \InvalidArgumentException(sprintf('A placeholder name must be a string (%d given). Did you forget to specify the placeholder key for the requirement "%s" in "%s"?', $placeholder, $requirement, $class->getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $globals;
|
||||
}
|
||||
|
||||
protected function createRoute($path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition)
|
||||
private function resetGlobals(): array
|
||||
{
|
||||
return [
|
||||
'path' => null,
|
||||
'localized_paths' => [],
|
||||
'requirements' => [],
|
||||
'options' => [],
|
||||
'defaults' => [],
|
||||
'schemes' => [],
|
||||
'methods' => [],
|
||||
'host' => '',
|
||||
'condition' => '',
|
||||
'name' => '',
|
||||
'priority' => 0,
|
||||
'env' => null,
|
||||
];
|
||||
}
|
||||
|
||||
protected function createRoute(string $path, array $defaults, array $requirements, array $options, ?string $host, array $schemes, array $methods, ?string $condition)
|
||||
{
|
||||
return new Route($path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
|
||||
}
|
||||
|
||||
abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot);
|
||||
abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot);
|
||||
|
||||
/**
|
||||
* @param \ReflectionClass|\ReflectionMethod $reflection
|
||||
*
|
||||
* @return iterable<int, RouteAnnotation>
|
||||
*/
|
||||
private function getAnnotations(object $reflection): iterable
|
||||
{
|
||||
foreach ($reflection->getAttributes($this->routeAnnotationClass, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
|
||||
yield $attribute->newInstance();
|
||||
}
|
||||
|
||||
if (!$this->reader) {
|
||||
return;
|
||||
}
|
||||
|
||||
$anntotations = $reflection instanceof \ReflectionClass
|
||||
? $this->reader->getClassAnnotations($reflection)
|
||||
: $this->reader->getMethodAnnotations($reflection);
|
||||
|
||||
foreach ($anntotations as $annotation) {
|
||||
if ($annotation instanceof $this->routeAnnotationClass) {
|
||||
yield $annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Loader;
|
||||
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Config\Resource\DirectoryResource;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* AnnotationDirectoryLoader loads routing information from annotations set
|
||||
@@ -23,18 +23,13 @@ use Symfony\Component\Config\Resource\DirectoryResource;
|
||||
class AnnotationDirectoryLoader extends AnnotationFileLoader
|
||||
{
|
||||
/**
|
||||
* Loads from annotations from a directory.
|
||||
*
|
||||
* @param string $path A directory path
|
||||
* @param string|null $type The resource type
|
||||
*
|
||||
* @return RouteCollection A RouteCollection instance
|
||||
*
|
||||
* @throws \InvalidArgumentException When the directory does not exist or its routes cannot be parsed
|
||||
*/
|
||||
public function load($path, $type = null)
|
||||
public function load(mixed $path, string $type = null): ?RouteCollection
|
||||
{
|
||||
$dir = $this->locator->locate($path);
|
||||
if (!is_dir($dir = $this->locator->locate($path))) {
|
||||
return parent::supports($path, $type) ? parent::load($path, $type) : new RouteCollection();
|
||||
}
|
||||
|
||||
$collection = new RouteCollection();
|
||||
$collection->addResource(new DirectoryResource($dir, '/\.php$/'));
|
||||
@@ -52,7 +47,7 @@ class AnnotationDirectoryLoader extends AnnotationFileLoader
|
||||
});
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (!$file->isFile() || '.php' !== substr($file->getFilename(), -4)) {
|
||||
if (!$file->isFile() || !str_ends_with($file->getFilename(), '.php')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -72,18 +67,20 @@ class AnnotationDirectoryLoader extends AnnotationFileLoader
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports($resource, $type = null)
|
||||
public function supports(mixed $resource, string $type = null): bool
|
||||
{
|
||||
if (!is_string($resource)) {
|
||||
if ('annotation' === $type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($type || !\is_string($resource)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$path = $this->locator->locate($resource);
|
||||
return is_dir($this->locator->locate($resource));
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_dir($path) && (!$type || 'annotation' === $type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Loader;
|
||||
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Config\Loader\FileLoader;
|
||||
use Symfony\Component\Config\FileLocatorInterface;
|
||||
use Symfony\Component\Config\Loader\FileLoader;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* AnnotationFileLoader loads routing information from annotations set
|
||||
@@ -26,18 +26,10 @@ class AnnotationFileLoader extends FileLoader
|
||||
{
|
||||
protected $loader;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param FileLocatorInterface $locator A FileLocator instance
|
||||
* @param AnnotationClassLoader $loader An AnnotationClassLoader instance
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function __construct(FileLocatorInterface $locator, AnnotationClassLoader $loader)
|
||||
{
|
||||
if (!function_exists('token_get_all')) {
|
||||
throw new \RuntimeException('The Tokenizer extension is required for the routing annotation loaders.');
|
||||
if (!\function_exists('token_get_all')) {
|
||||
throw new \LogicException('The Tokenizer extension is required for the routing annotation loaders.');
|
||||
}
|
||||
|
||||
parent::__construct($locator);
|
||||
@@ -48,26 +40,24 @@ class AnnotationFileLoader extends FileLoader
|
||||
/**
|
||||
* Loads from annotations from a file.
|
||||
*
|
||||
* @param string $file A PHP file path
|
||||
* @param string|null $type The resource type
|
||||
*
|
||||
* @return RouteCollection A RouteCollection instance
|
||||
*
|
||||
* @throws \InvalidArgumentException When the file does not exist or its routes cannot be parsed
|
||||
*/
|
||||
public function load($file, $type = null)
|
||||
public function load(mixed $file, string $type = null): ?RouteCollection
|
||||
{
|
||||
$path = $this->locator->locate($file);
|
||||
|
||||
$collection = new RouteCollection();
|
||||
if ($class = $this->findClass($path)) {
|
||||
$refl = new \ReflectionClass($class);
|
||||
if ($refl->isAbstract()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$collection->addResource(new FileResource($path));
|
||||
$collection->addCollection($this->loader->load($class, $type));
|
||||
}
|
||||
if (\PHP_VERSION_ID >= 70000) {
|
||||
// PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
|
||||
gc_mem_caches();
|
||||
}
|
||||
|
||||
gc_mem_caches();
|
||||
|
||||
return $collection;
|
||||
}
|
||||
@@ -75,69 +65,71 @@ class AnnotationFileLoader extends FileLoader
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports($resource, $type = null)
|
||||
public function supports(mixed $resource, string $type = null): bool
|
||||
{
|
||||
return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'annotation' === $type);
|
||||
return \is_string($resource) && 'php' === pathinfo($resource, \PATHINFO_EXTENSION) && (!$type || 'annotation' === $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full class name for the first class in the file.
|
||||
*
|
||||
* @param string $file A PHP file path
|
||||
*
|
||||
* @return string|false Full class name if found, false otherwise
|
||||
*/
|
||||
protected function findClass($file)
|
||||
protected function findClass(string $file): string|false
|
||||
{
|
||||
$class = false;
|
||||
$namespace = false;
|
||||
$tokens = token_get_all(file_get_contents($file));
|
||||
|
||||
if (1 === count($tokens) && T_INLINE_HTML === $tokens[0][0]) {
|
||||
if (1 === \count($tokens) && \T_INLINE_HTML === $tokens[0][0]) {
|
||||
throw new \InvalidArgumentException(sprintf('The file "%s" does not contain PHP code. Did you forgot to add the "<?php" start tag at the beginning of the file?', $file));
|
||||
}
|
||||
|
||||
$nsTokens = [\T_NS_SEPARATOR => true, \T_STRING => true];
|
||||
if (\defined('T_NAME_QUALIFIED')) {
|
||||
$nsTokens[\T_NAME_QUALIFIED] = true;
|
||||
}
|
||||
for ($i = 0; isset($tokens[$i]); ++$i) {
|
||||
$token = $tokens[$i];
|
||||
|
||||
if (!isset($token[1])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (true === $class && T_STRING === $token[0]) {
|
||||
if (true === $class && \T_STRING === $token[0]) {
|
||||
return $namespace.'\\'.$token[1];
|
||||
}
|
||||
|
||||
if (true === $namespace && T_STRING === $token[0]) {
|
||||
if (true === $namespace && isset($nsTokens[$token[0]])) {
|
||||
$namespace = $token[1];
|
||||
while (isset($tokens[++$i][1]) && in_array($tokens[$i][0], array(T_NS_SEPARATOR, T_STRING))) {
|
||||
while (isset($tokens[++$i][1], $nsTokens[$tokens[$i][0]])) {
|
||||
$namespace .= $tokens[$i][1];
|
||||
}
|
||||
$token = $tokens[$i];
|
||||
}
|
||||
|
||||
if (T_CLASS === $token[0]) {
|
||||
// Skip usage of ::class constant
|
||||
$isClassConstant = false;
|
||||
if (\T_CLASS === $token[0]) {
|
||||
// Skip usage of ::class constant and anonymous classes
|
||||
$skipClassToken = false;
|
||||
for ($j = $i - 1; $j > 0; --$j) {
|
||||
if (!isset($tokens[$j][1])) {
|
||||
if ('(' === $tokens[$j] || ',' === $tokens[$j]) {
|
||||
$skipClassToken = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (T_DOUBLE_COLON === $tokens[$j][0]) {
|
||||
$isClassConstant = true;
|
||||
if (\T_DOUBLE_COLON === $tokens[$j][0] || \T_NEW === $tokens[$j][0]) {
|
||||
$skipClassToken = true;
|
||||
break;
|
||||
} elseif (!in_array($tokens[$j][0], array(T_WHITESPACE, T_DOC_COMMENT, T_COMMENT))) {
|
||||
} elseif (!\in_array($tokens[$j][0], [\T_WHITESPACE, \T_DOC_COMMENT, \T_COMMENT])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$isClassConstant) {
|
||||
if (!$skipClassToken) {
|
||||
$class = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (T_NAMESPACE === $token[0]) {
|
||||
if (\T_NAMESPACE === $token[0]) {
|
||||
$namespace = true;
|
||||
}
|
||||
}
|
||||
|
||||
11
vendor/symfony/routing/Loader/ClosureLoader.php
vendored
11
vendor/symfony/routing/Loader/ClosureLoader.php
vendored
@@ -25,21 +25,16 @@ class ClosureLoader extends Loader
|
||||
{
|
||||
/**
|
||||
* Loads a Closure.
|
||||
*
|
||||
* @param \Closure $closure A Closure
|
||||
* @param string|null $type The resource type
|
||||
*
|
||||
* @return RouteCollection A RouteCollection instance
|
||||
*/
|
||||
public function load($closure, $type = null)
|
||||
public function load(mixed $closure, string $type = null): RouteCollection
|
||||
{
|
||||
return $closure();
|
||||
return $closure($this->env);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports($resource, $type = null)
|
||||
public function supports(mixed $resource, string $type = null): bool
|
||||
{
|
||||
return $resource instanceof \Closure && (!$type || 'closure' === $type);
|
||||
}
|
||||
|
||||
43
vendor/symfony/routing/Loader/Configurator/AliasConfigurator.php
vendored
Normal file
43
vendor/symfony/routing/Loader/Configurator/AliasConfigurator.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Routing\Alias;
|
||||
|
||||
class AliasConfigurator
|
||||
{
|
||||
private $alias;
|
||||
|
||||
public function __construct(Alias $alias)
|
||||
{
|
||||
$this->alias = $alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this alias is deprecated, that means it should not be called anymore.
|
||||
*
|
||||
* @param string $package The name of the composer package that is triggering the deprecation
|
||||
* @param string $version The version of the package that introduced the deprecation
|
||||
* @param string $message The deprecation message to use
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidArgumentException when the message template is invalid
|
||||
*/
|
||||
public function deprecate(string $package, string $version, string $message): static
|
||||
{
|
||||
$this->alias->setDeprecated($package, $version, $message);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
122
vendor/symfony/routing/Loader/Configurator/CollectionConfigurator.php
vendored
Normal file
122
vendor/symfony/routing/Loader/Configurator/CollectionConfigurator.php
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator;
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class CollectionConfigurator
|
||||
{
|
||||
use Traits\AddTrait;
|
||||
use Traits\HostTrait;
|
||||
use Traits\RouteTrait;
|
||||
|
||||
private $parent;
|
||||
private $parentConfigurator;
|
||||
private ?array $parentPrefixes;
|
||||
private string|array|null $host = null;
|
||||
|
||||
public function __construct(RouteCollection $parent, string $name, self $parentConfigurator = null, array $parentPrefixes = null)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->name = $name;
|
||||
$this->collection = new RouteCollection();
|
||||
$this->route = new Route('');
|
||||
$this->parentConfigurator = $parentConfigurator; // for GC control
|
||||
$this->parentPrefixes = $parentPrefixes;
|
||||
}
|
||||
|
||||
public function __sleep(): array
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
|
||||
}
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (null === $this->prefixes) {
|
||||
$this->collection->addPrefix($this->route->getPath());
|
||||
}
|
||||
if (null !== $this->host) {
|
||||
$this->addHost($this->collection, $this->host);
|
||||
}
|
||||
|
||||
$this->parent->addCollection($this->collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a sub-collection.
|
||||
*/
|
||||
final public function collection(string $name = ''): self
|
||||
{
|
||||
return new self($this->collection, $this->name.$name, $this, $this->prefixes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the prefix to add to the path of all child routes.
|
||||
*
|
||||
* @param string|array $prefix the prefix, or the localized prefixes
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function prefix(string|array $prefix): static
|
||||
{
|
||||
if (\is_array($prefix)) {
|
||||
if (null === $this->parentPrefixes) {
|
||||
// no-op
|
||||
} elseif ($missing = array_diff_key($this->parentPrefixes, $prefix)) {
|
||||
throw new \LogicException(sprintf('Collection "%s" is missing prefixes for locale(s) "%s".', $this->name, implode('", "', array_keys($missing))));
|
||||
} else {
|
||||
foreach ($prefix as $locale => $localePrefix) {
|
||||
if (!isset($this->parentPrefixes[$locale])) {
|
||||
throw new \LogicException(sprintf('Collection "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $this->name, $locale));
|
||||
}
|
||||
|
||||
$prefix[$locale] = $this->parentPrefixes[$locale].$localePrefix;
|
||||
}
|
||||
}
|
||||
$this->prefixes = $prefix;
|
||||
$this->route->setPath('/');
|
||||
} else {
|
||||
$this->prefixes = null;
|
||||
$this->route->setPath($prefix);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the host to use for all child routes.
|
||||
*
|
||||
* @param string|array $host the host, or the localized hosts
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function host(string|array $host): static
|
||||
{
|
||||
$this->host = $host;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function createRoute(string $path): Route
|
||||
{
|
||||
return (clone $this->route)->setPath($path);
|
||||
}
|
||||
}
|
||||
87
vendor/symfony/routing/Loader/Configurator/ImportConfigurator.php
vendored
Normal file
87
vendor/symfony/routing/Loader/Configurator/ImportConfigurator.php
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator;
|
||||
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ImportConfigurator
|
||||
{
|
||||
use Traits\HostTrait;
|
||||
use Traits\PrefixTrait;
|
||||
use Traits\RouteTrait;
|
||||
|
||||
private $parent;
|
||||
|
||||
public function __construct(RouteCollection $parent, RouteCollection $route)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->route = $route;
|
||||
}
|
||||
|
||||
public function __sleep(): array
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
|
||||
}
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->parent->addCollection($this->route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the prefix to add to the path of all child routes.
|
||||
*
|
||||
* @param string|array $prefix the prefix, or the localized prefixes
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function prefix(string|array $prefix, bool $trailingSlashOnRoot = true): static
|
||||
{
|
||||
$this->addPrefix($this->route, $prefix, $trailingSlashOnRoot);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the prefix to add to the name of all child routes.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function namePrefix(string $namePrefix): static
|
||||
{
|
||||
$this->route->addNamePrefix($namePrefix);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the host to use for all child routes.
|
||||
*
|
||||
* @param string|array $host the host, or the localized hosts
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function host(string|array $host): static
|
||||
{
|
||||
$this->addHost($this->route, $host);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
49
vendor/symfony/routing/Loader/Configurator/RouteConfigurator.php
vendored
Normal file
49
vendor/symfony/routing/Loader/Configurator/RouteConfigurator.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator;
|
||||
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class RouteConfigurator
|
||||
{
|
||||
use Traits\AddTrait;
|
||||
use Traits\HostTrait;
|
||||
use Traits\RouteTrait;
|
||||
|
||||
protected $parentConfigurator;
|
||||
|
||||
public function __construct(RouteCollection $collection, RouteCollection $route, string $name = '', CollectionConfigurator $parentConfigurator = null, array $prefixes = null)
|
||||
{
|
||||
$this->collection = $collection;
|
||||
$this->route = $route;
|
||||
$this->name = $name;
|
||||
$this->parentConfigurator = $parentConfigurator; // for GC control
|
||||
$this->prefixes = $prefixes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the host to use for all child routes.
|
||||
*
|
||||
* @param string|array $host the host, or the localized hosts
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function host(string|array $host): static
|
||||
{
|
||||
$this->addHost($this->route, $host);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
78
vendor/symfony/routing/Loader/Configurator/RoutingConfigurator.php
vendored
Normal file
78
vendor/symfony/routing/Loader/Configurator/RoutingConfigurator.php
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator;
|
||||
|
||||
use Symfony\Component\Routing\Loader\PhpFileLoader;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class RoutingConfigurator
|
||||
{
|
||||
use Traits\AddTrait;
|
||||
|
||||
private $loader;
|
||||
private string $path;
|
||||
private string $file;
|
||||
private ?string $env;
|
||||
|
||||
public function __construct(RouteCollection $collection, PhpFileLoader $loader, string $path, string $file, string $env = null)
|
||||
{
|
||||
$this->collection = $collection;
|
||||
$this->loader = $loader;
|
||||
$this->path = $path;
|
||||
$this->file = $file;
|
||||
$this->env = $env;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|string[]|null $exclude Glob patterns to exclude from the import
|
||||
*/
|
||||
final public function import(string|array $resource, string $type = null, bool $ignoreErrors = false, string|array $exclude = null): ImportConfigurator
|
||||
{
|
||||
$this->loader->setCurrentDir(\dirname($this->path));
|
||||
|
||||
$imported = $this->loader->import($resource, $type, $ignoreErrors, $this->file, $exclude) ?: [];
|
||||
if (!\is_array($imported)) {
|
||||
return new ImportConfigurator($this->collection, $imported);
|
||||
}
|
||||
|
||||
$mergedCollection = new RouteCollection();
|
||||
foreach ($imported as $subCollection) {
|
||||
$mergedCollection->addCollection($subCollection);
|
||||
}
|
||||
|
||||
return new ImportConfigurator($this->collection, $mergedCollection);
|
||||
}
|
||||
|
||||
final public function collection(string $name = ''): CollectionConfigurator
|
||||
{
|
||||
return new CollectionConfigurator($this->collection, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current environment to be able to write conditional configuration.
|
||||
*/
|
||||
final public function env(): ?string
|
||||
{
|
||||
return $this->env;
|
||||
}
|
||||
|
||||
final public function withPath(string $path): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->path = $clone->file = $path;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
}
|
||||
60
vendor/symfony/routing/Loader/Configurator/Traits/AddTrait.php
vendored
Normal file
60
vendor/symfony/routing/Loader/Configurator/Traits/AddTrait.php
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator\Traits;
|
||||
|
||||
use Symfony\Component\Routing\Loader\Configurator\AliasConfigurator;
|
||||
use Symfony\Component\Routing\Loader\Configurator\CollectionConfigurator;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
trait AddTrait
|
||||
{
|
||||
use LocalizedRouteTrait;
|
||||
|
||||
/**
|
||||
* @var RouteCollection
|
||||
*/
|
||||
protected $collection;
|
||||
protected $name = '';
|
||||
protected $prefixes;
|
||||
|
||||
/**
|
||||
* Adds a route.
|
||||
*
|
||||
* @param string|array $path the path, or the localized paths of the route
|
||||
*/
|
||||
public function add(string $name, string|array $path): RouteConfigurator
|
||||
{
|
||||
$parentConfigurator = $this instanceof CollectionConfigurator ? $this : ($this instanceof RouteConfigurator ? $this->parentConfigurator : null);
|
||||
$route = $this->createLocalizedRoute($this->collection, $name, $path, $this->name, $this->prefixes);
|
||||
|
||||
return new RouteConfigurator($this->collection, $route, $this->name, $parentConfigurator, $this->prefixes);
|
||||
}
|
||||
|
||||
public function alias(string $name, string $alias): AliasConfigurator
|
||||
{
|
||||
return new AliasConfigurator($this->collection->addAlias($name, $alias));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a route.
|
||||
*
|
||||
* @param string|array $path the path, or the localized paths of the route
|
||||
*/
|
||||
public function __invoke(string $name, string|array $path): RouteConfigurator
|
||||
{
|
||||
return $this->add($name, $path);
|
||||
}
|
||||
}
|
||||
49
vendor/symfony/routing/Loader/Configurator/Traits/HostTrait.php
vendored
Normal file
49
vendor/symfony/routing/Loader/Configurator/Traits/HostTrait.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator\Traits;
|
||||
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
trait HostTrait
|
||||
{
|
||||
final protected function addHost(RouteCollection $routes, string|array $hosts)
|
||||
{
|
||||
if (!$hosts || !\is_array($hosts)) {
|
||||
$routes->setHost($hosts ?: '');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($routes->all() as $name => $route) {
|
||||
if (null === $locale = $route->getDefault('_locale')) {
|
||||
$routes->remove($name);
|
||||
foreach ($hosts as $locale => $host) {
|
||||
$localizedRoute = clone $route;
|
||||
$localizedRoute->setDefault('_locale', $locale);
|
||||
$localizedRoute->setRequirement('_locale', preg_quote($locale));
|
||||
$localizedRoute->setDefault('_canonical_route', $name);
|
||||
$localizedRoute->setHost($host);
|
||||
$routes->add($name.'.'.$locale, $localizedRoute);
|
||||
}
|
||||
} elseif (!isset($hosts[$locale])) {
|
||||
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding host in its parent collection.', $name, $locale));
|
||||
} else {
|
||||
$route->setHost($hosts[$locale]);
|
||||
$route->setRequirement('_locale', preg_quote($locale));
|
||||
$routes->add($name, $route);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
76
vendor/symfony/routing/Loader/Configurator/Traits/LocalizedRouteTrait.php
vendored
Normal file
76
vendor/symfony/routing/Loader/Configurator/Traits/LocalizedRouteTrait.php
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator\Traits;
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @author Jules Pietri <jules@heahprod.com>
|
||||
*/
|
||||
trait LocalizedRouteTrait
|
||||
{
|
||||
/**
|
||||
* Creates one or many routes.
|
||||
*
|
||||
* @param string|array $path the path, or the localized paths of the route
|
||||
*/
|
||||
final protected function createLocalizedRoute(RouteCollection $collection, string $name, string|array $path, string $namePrefix = '', array $prefixes = null): RouteCollection
|
||||
{
|
||||
$paths = [];
|
||||
|
||||
$routes = new RouteCollection();
|
||||
|
||||
if (\is_array($path)) {
|
||||
if (null === $prefixes) {
|
||||
$paths = $path;
|
||||
} elseif ($missing = array_diff_key($prefixes, $path)) {
|
||||
throw new \LogicException(sprintf('Route "%s" is missing routes for locale(s) "%s".', $name, implode('", "', array_keys($missing))));
|
||||
} else {
|
||||
foreach ($path as $locale => $localePath) {
|
||||
if (!isset($prefixes[$locale])) {
|
||||
throw new \LogicException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $locale));
|
||||
}
|
||||
|
||||
$paths[$locale] = $prefixes[$locale].$localePath;
|
||||
}
|
||||
}
|
||||
} elseif (null !== $prefixes) {
|
||||
foreach ($prefixes as $locale => $prefix) {
|
||||
$paths[$locale] = $prefix.$path;
|
||||
}
|
||||
} else {
|
||||
$routes->add($namePrefix.$name, $route = $this->createRoute($path));
|
||||
$collection->add($namePrefix.$name, $route);
|
||||
|
||||
return $routes;
|
||||
}
|
||||
|
||||
foreach ($paths as $locale => $path) {
|
||||
$routes->add($name.'.'.$locale, $route = $this->createRoute($path));
|
||||
$collection->add($namePrefix.$name.'.'.$locale, $route);
|
||||
$route->setDefault('_locale', $locale);
|
||||
$route->setRequirement('_locale', preg_quote($locale));
|
||||
$route->setDefault('_canonical_route', $namePrefix.$name);
|
||||
}
|
||||
|
||||
return $routes;
|
||||
}
|
||||
|
||||
private function createRoute(string $path): Route
|
||||
{
|
||||
return new Route($path);
|
||||
}
|
||||
}
|
||||
62
vendor/symfony/routing/Loader/Configurator/Traits/PrefixTrait.php
vendored
Normal file
62
vendor/symfony/routing/Loader/Configurator/Traits/PrefixTrait.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator\Traits;
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
trait PrefixTrait
|
||||
{
|
||||
final protected function addPrefix(RouteCollection $routes, string|array $prefix, bool $trailingSlashOnRoot)
|
||||
{
|
||||
if (\is_array($prefix)) {
|
||||
foreach ($prefix as $locale => $localePrefix) {
|
||||
$prefix[$locale] = trim(trim($localePrefix), '/');
|
||||
}
|
||||
foreach ($routes->all() as $name => $route) {
|
||||
if (null === $locale = $route->getDefault('_locale')) {
|
||||
$routes->remove($name);
|
||||
foreach ($prefix as $locale => $localePrefix) {
|
||||
$localizedRoute = clone $route;
|
||||
$localizedRoute->setDefault('_locale', $locale);
|
||||
$localizedRoute->setRequirement('_locale', preg_quote($locale));
|
||||
$localizedRoute->setDefault('_canonical_route', $name);
|
||||
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$routes->add($name.'.'.$locale, $localizedRoute);
|
||||
}
|
||||
} elseif (!isset($prefix[$locale])) {
|
||||
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $locale));
|
||||
} else {
|
||||
$route->setPath($prefix[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$routes->add($name, $route);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$routes->addPrefix($prefix);
|
||||
if (!$trailingSlashOnRoot) {
|
||||
$rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath();
|
||||
foreach ($routes->all() as $route) {
|
||||
if ($route->getPath() === $rootPath) {
|
||||
$route->setPath(rtrim($rootPath, '/'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
175
vendor/symfony/routing/Loader/Configurator/Traits/RouteTrait.php
vendored
Normal file
175
vendor/symfony/routing/Loader/Configurator/Traits/RouteTrait.php
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator\Traits;
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
trait RouteTrait
|
||||
{
|
||||
/**
|
||||
* @var RouteCollection|Route
|
||||
*/
|
||||
protected $route;
|
||||
|
||||
/**
|
||||
* Adds defaults.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function defaults(array $defaults): static
|
||||
{
|
||||
$this->route->addDefaults($defaults);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds requirements.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function requirements(array $requirements): static
|
||||
{
|
||||
$this->route->addRequirements($requirements);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds options.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function options(array $options): static
|
||||
{
|
||||
$this->route->addOptions($options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether paths should accept utf8 encoding.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function utf8(bool $utf8 = true): static
|
||||
{
|
||||
$this->route->addOptions(['utf8' => $utf8]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the condition.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function condition(string $condition): static
|
||||
{
|
||||
$this->route->setCondition($condition);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pattern for the host.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function host(string $pattern): static
|
||||
{
|
||||
$this->route->setHost($pattern);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the schemes (e.g. 'https') this route is restricted to.
|
||||
* So an empty array means that any scheme is allowed.
|
||||
*
|
||||
* @param string[] $schemes
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function schemes(array $schemes): static
|
||||
{
|
||||
$this->route->setSchemes($schemes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the HTTP methods (e.g. 'POST') this route is restricted to.
|
||||
* So an empty array means that any method is allowed.
|
||||
*
|
||||
* @param string[] $methods
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function methods(array $methods): static
|
||||
{
|
||||
$this->route->setMethods($methods);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the "_controller" entry to defaults.
|
||||
*
|
||||
* @param callable|string|array $controller a callable or parseable pseudo-callable
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function controller(callable|string|array $controller): static
|
||||
{
|
||||
$this->route->addDefaults(['_controller' => $controller]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the "_locale" entry to defaults.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function locale(string $locale): static
|
||||
{
|
||||
$this->route->addDefaults(['_locale' => $locale]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the "_format" entry to defaults.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function format(string $format): static
|
||||
{
|
||||
$this->route->addDefaults(['_format' => $format]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the "_stateless" entry to defaults.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function stateless(bool $stateless = true): static
|
||||
{
|
||||
$this->route->addDefaults(['_stateless' => $stateless]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
46
vendor/symfony/routing/Loader/ContainerLoader.php
vendored
Normal file
46
vendor/symfony/routing/Loader/ContainerLoader.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
/**
|
||||
* A route loader that executes a service from a PSR-11 container to load the routes.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*/
|
||||
class ContainerLoader extends ObjectLoader
|
||||
{
|
||||
private $container;
|
||||
|
||||
public function __construct(ContainerInterface $container, string $env = null)
|
||||
{
|
||||
$this->container = $container;
|
||||
parent::__construct($env);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(mixed $resource, string $type = null): bool
|
||||
{
|
||||
return 'service' === $type && \is_string($resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getObject(string $id): object
|
||||
{
|
||||
return $this->container->get($id);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\DependencyInjection;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\Routing\Loader\ObjectRouteLoader;
|
||||
|
||||
/**
|
||||
* A route loader that executes a service to load the routes.
|
||||
*
|
||||
* This depends on the DependencyInjection component.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*/
|
||||
class ServiceRouterLoader extends ObjectRouteLoader
|
||||
{
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
private $container;
|
||||
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
protected function getServiceObject($id)
|
||||
{
|
||||
return $this->container->get($id);
|
||||
}
|
||||
}
|
||||
@@ -12,15 +12,15 @@
|
||||
namespace Symfony\Component\Routing\Loader;
|
||||
|
||||
use Symfony\Component\Config\Loader\FileLoader;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Config\Resource\DirectoryResource;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
class DirectoryLoader extends FileLoader
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load($file, $type = null)
|
||||
public function load(mixed $file, string $type = null): mixed
|
||||
{
|
||||
$path = $this->locator->locate($file);
|
||||
|
||||
@@ -49,7 +49,7 @@ class DirectoryLoader extends FileLoader
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports($resource, $type = null)
|
||||
public function supports(mixed $resource, string $type = null): bool
|
||||
{
|
||||
// only when type is forced to directory, not to conflict with AnnotationLoader
|
||||
|
||||
|
||||
47
vendor/symfony/routing/Loader/GlobFileLoader.php
vendored
Normal file
47
vendor/symfony/routing/Loader/GlobFileLoader.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader;
|
||||
|
||||
use Symfony\Component\Config\Loader\FileLoader;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* GlobFileLoader loads files from a glob pattern.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class GlobFileLoader extends FileLoader
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load(mixed $resource, string $type = null): mixed
|
||||
{
|
||||
$collection = new RouteCollection();
|
||||
|
||||
foreach ($this->glob($resource, false, $globResource) as $path => $info) {
|
||||
$collection->addCollection($this->import($path));
|
||||
}
|
||||
|
||||
$collection->addResource($globResource);
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(mixed $resource, string $type = null): bool
|
||||
{
|
||||
return 'glob' === $type;
|
||||
}
|
||||
}
|
||||
77
vendor/symfony/routing/Loader/ObjectLoader.php
vendored
Normal file
77
vendor/symfony/routing/Loader/ObjectLoader.php
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader;
|
||||
|
||||
use Symfony\Component\Config\Loader\Loader;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* A route loader that calls a method on an object to load the routes.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*/
|
||||
abstract class ObjectLoader extends Loader
|
||||
{
|
||||
/**
|
||||
* Returns the object that the method will be called on to load routes.
|
||||
*
|
||||
* For example, if your application uses a service container,
|
||||
* the $id may be a service id.
|
||||
*/
|
||||
abstract protected function getObject(string $id): object;
|
||||
|
||||
/**
|
||||
* Calls the object method that will load the routes.
|
||||
*/
|
||||
public function load(mixed $resource, string $type = null): RouteCollection
|
||||
{
|
||||
if (!preg_match('/^[^\:]+(?:::(?:[^\:]+))?$/', $resource)) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid resource "%s" passed to the %s route loader: use the format "object_id::method" or "object_id" if your object class has an "__invoke" method.', $resource, \is_string($type) ? '"'.$type.'"' : 'object'));
|
||||
}
|
||||
|
||||
$parts = explode('::', $resource);
|
||||
$method = $parts[1] ?? '__invoke';
|
||||
|
||||
$loaderObject = $this->getObject($parts[0]);
|
||||
|
||||
if (!\is_object($loaderObject)) {
|
||||
throw new \TypeError(sprintf('"%s:getObject()" must return an object: "%s" returned.', static::class, get_debug_type($loaderObject)));
|
||||
}
|
||||
|
||||
if (!\is_callable([$loaderObject, $method])) {
|
||||
throw new \BadMethodCallException(sprintf('Method "%s" not found on "%s" when importing routing resource "%s".', $method, get_debug_type($loaderObject), $resource));
|
||||
}
|
||||
|
||||
$routeCollection = $loaderObject->$method($this, $this->env);
|
||||
|
||||
if (!$routeCollection instanceof RouteCollection) {
|
||||
$type = get_debug_type($routeCollection);
|
||||
|
||||
throw new \LogicException(sprintf('The "%s::%s()" method must return a RouteCollection: "%s" returned.', get_debug_type($loaderObject), $method, $type));
|
||||
}
|
||||
|
||||
// make the object file tracked so that if it changes, the cache rebuilds
|
||||
$this->addClassResource(new \ReflectionClass($loaderObject), $routeCollection);
|
||||
|
||||
return $routeCollection;
|
||||
}
|
||||
|
||||
private function addClassResource(\ReflectionClass $class, RouteCollection $collection)
|
||||
{
|
||||
do {
|
||||
if (is_file($class->getFileName())) {
|
||||
$collection->addResource(new FileResource($class->getFileName()));
|
||||
}
|
||||
} while ($class = $class->getParentClass());
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader;
|
||||
|
||||
use Symfony\Component\Config\Loader\Loader;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* A route loader that calls a method on an object to load the routes.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*/
|
||||
abstract class ObjectRouteLoader extends Loader
|
||||
{
|
||||
/**
|
||||
* Returns the object that the method will be called on to load routes.
|
||||
*
|
||||
* For example, if your application uses a service container,
|
||||
* the $id may be a service id.
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
abstract protected function getServiceObject($id);
|
||||
|
||||
/**
|
||||
* Calls the service that will load the routes.
|
||||
*
|
||||
* @param mixed $resource Some value that will resolve to a callable
|
||||
* @param string|null $type The resource type
|
||||
*
|
||||
* @return RouteCollection
|
||||
*/
|
||||
public function load($resource, $type = null)
|
||||
{
|
||||
$parts = explode(':', $resource);
|
||||
if (count($parts) != 2) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid resource "%s" passed to the "service" route loader: use the format "service_name:methodName"', $resource));
|
||||
}
|
||||
|
||||
$serviceString = $parts[0];
|
||||
$method = $parts[1];
|
||||
|
||||
$loaderObject = $this->getServiceObject($serviceString);
|
||||
|
||||
if (!is_object($loaderObject)) {
|
||||
throw new \LogicException(sprintf('%s:getServiceObject() must return an object: %s returned', get_class($this), gettype($loaderObject)));
|
||||
}
|
||||
|
||||
if (!method_exists($loaderObject, $method)) {
|
||||
throw new \BadMethodCallException(sprintf('Method "%s" not found on "%s" when importing routing resource "%s"', $method, get_class($loaderObject), $resource));
|
||||
}
|
||||
|
||||
$routeCollection = call_user_func(array($loaderObject, $method), $this);
|
||||
|
||||
if (!$routeCollection instanceof RouteCollection) {
|
||||
$type = is_object($routeCollection) ? get_class($routeCollection) : gettype($routeCollection);
|
||||
|
||||
throw new \LogicException(sprintf('The %s::%s method must return a RouteCollection: %s returned', get_class($loaderObject), $method, $type));
|
||||
}
|
||||
|
||||
// make the service file tracked so that if it changes, the cache rebuilds
|
||||
$this->addClassResource(new \ReflectionClass($loaderObject), $routeCollection);
|
||||
|
||||
return $routeCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports($resource, $type = null)
|
||||
{
|
||||
return 'service' === $type;
|
||||
}
|
||||
|
||||
private function addClassResource(\ReflectionClass $class, RouteCollection $collection)
|
||||
{
|
||||
do {
|
||||
if (is_file($class->getFileName())) {
|
||||
$collection->addResource(new FileResource($class->getFileName()));
|
||||
}
|
||||
} while ($class = $class->getParentClass());
|
||||
}
|
||||
}
|
||||
54
vendor/symfony/routing/Loader/PhpFileLoader.php
vendored
54
vendor/symfony/routing/Loader/PhpFileLoader.php
vendored
@@ -13,6 +13,7 @@ namespace Symfony\Component\Routing\Loader;
|
||||
|
||||
use Symfony\Component\Config\Loader\FileLoader;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
@@ -21,23 +22,33 @@ use Symfony\Component\Routing\RouteCollection;
|
||||
* The file must return a RouteCollection instance.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Nicolas grekas <p@tchwork.com>
|
||||
* @author Jules Pietri <jules@heahprod.com>
|
||||
*/
|
||||
class PhpFileLoader extends FileLoader
|
||||
{
|
||||
/**
|
||||
* Loads a PHP file.
|
||||
*
|
||||
* @param string $file A PHP file path
|
||||
* @param string|null $type The resource type
|
||||
*
|
||||
* @return RouteCollection A RouteCollection instance
|
||||
*/
|
||||
public function load($file, $type = null)
|
||||
public function load(mixed $file, string $type = null): RouteCollection
|
||||
{
|
||||
$path = $this->locator->locate($file);
|
||||
$this->setCurrentDir(dirname($path));
|
||||
$this->setCurrentDir(\dirname($path));
|
||||
|
||||
// the closure forbids access to the private scope in the included file
|
||||
$loader = $this;
|
||||
$load = \Closure::bind(static function ($file) use ($loader) {
|
||||
return include $file;
|
||||
}, null, ProtectedPhpFileLoader::class);
|
||||
|
||||
$result = $load($path);
|
||||
|
||||
if (\is_object($result) && \is_callable($result)) {
|
||||
$collection = $this->callConfigurator($result, $path, $file);
|
||||
} else {
|
||||
$collection = $result;
|
||||
}
|
||||
|
||||
$collection = self::includeFile($path, $this);
|
||||
$collection->addResource(new FileResource($path));
|
||||
|
||||
return $collection;
|
||||
@@ -46,21 +57,24 @@ class PhpFileLoader extends FileLoader
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports($resource, $type = null)
|
||||
public function supports(mixed $resource, string $type = null): bool
|
||||
{
|
||||
return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'php' === $type);
|
||||
return \is_string($resource) && 'php' === pathinfo($resource, \PATHINFO_EXTENSION) && (!$type || 'php' === $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe include. Used for scope isolation.
|
||||
*
|
||||
* @param string $file File to include
|
||||
* @param PhpFileLoader $loader the loader variable is exposed to the included file below
|
||||
*
|
||||
* @return RouteCollection
|
||||
*/
|
||||
private static function includeFile($file, PhpFileLoader $loader)
|
||||
protected function callConfigurator(callable $result, string $path, string $file): RouteCollection
|
||||
{
|
||||
return include $file;
|
||||
$collection = new RouteCollection();
|
||||
|
||||
$result(new RoutingConfigurator($collection, $this, $path, $file, $this->env));
|
||||
|
||||
return $collection;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ProtectedPhpFileLoader extends PhpFileLoader
|
||||
{
|
||||
}
|
||||
|
||||
312
vendor/symfony/routing/Loader/XmlFileLoader.php
vendored
312
vendor/symfony/routing/Loader/XmlFileLoader.php
vendored
@@ -11,11 +11,13 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Loader;
|
||||
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Config\Loader\FileLoader;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Config\Util\XmlUtils;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\HostTrait;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\LocalizedRouteTrait;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\PrefixTrait;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* XmlFileLoader loads XML routing files.
|
||||
@@ -25,21 +27,18 @@ use Symfony\Component\Config\Util\XmlUtils;
|
||||
*/
|
||||
class XmlFileLoader extends FileLoader
|
||||
{
|
||||
const NAMESPACE_URI = 'http://symfony.com/schema/routing';
|
||||
const SCHEME_PATH = '/schema/routing/routing-1.0.xsd';
|
||||
use HostTrait;
|
||||
use LocalizedRouteTrait;
|
||||
use PrefixTrait;
|
||||
|
||||
public const NAMESPACE_URI = 'http://symfony.com/schema/routing';
|
||||
public const SCHEME_PATH = '/schema/routing/routing-1.0.xsd';
|
||||
|
||||
/**
|
||||
* Loads an XML file.
|
||||
*
|
||||
* @param string $file An XML file path
|
||||
* @param string|null $type The resource type
|
||||
*
|
||||
* @return RouteCollection A RouteCollection instance
|
||||
*
|
||||
* @throws \InvalidArgumentException When the file cannot be loaded or when the XML cannot be
|
||||
* parsed because it does not validate against the scheme.
|
||||
* @throws \InvalidArgumentException when the file cannot be loaded or when the XML cannot be
|
||||
* parsed because it does not validate against the scheme
|
||||
*/
|
||||
public function load($file, $type = null)
|
||||
public function load(mixed $file, string $type = null): RouteCollection
|
||||
{
|
||||
$path = $this->locator->locate($file);
|
||||
|
||||
@@ -63,14 +62,9 @@ class XmlFileLoader extends FileLoader
|
||||
/**
|
||||
* Parses a node from a loaded XML file.
|
||||
*
|
||||
* @param RouteCollection $collection Collection to associate with the node
|
||||
* @param \DOMElement $node Element to parse
|
||||
* @param string $path Full path of the XML file being processed
|
||||
* @param string $file Loaded file name
|
||||
*
|
||||
* @throws \InvalidArgumentException When the XML is invalid
|
||||
*/
|
||||
protected function parseNode(RouteCollection $collection, \DOMElement $node, $path, $file)
|
||||
protected function parseNode(RouteCollection $collection, \DOMElement $node, string $path, string $file)
|
||||
{
|
||||
if (self::NAMESPACE_URI !== $node->namespaceURI) {
|
||||
return;
|
||||
@@ -83,6 +77,16 @@ class XmlFileLoader extends FileLoader
|
||||
case 'import':
|
||||
$this->parseImport($collection, $node, $path, $file);
|
||||
break;
|
||||
case 'when':
|
||||
if (!$this->env || $node->getAttribute('env') !== $this->env) {
|
||||
break;
|
||||
}
|
||||
foreach ($node->childNodes as $node) {
|
||||
if ($node instanceof \DOMElement) {
|
||||
$this->parseNode($collection, $node, $path, $file);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "route" or "import".', $node->localName, $path));
|
||||
}
|
||||
@@ -91,46 +95,64 @@ class XmlFileLoader extends FileLoader
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports($resource, $type = null)
|
||||
public function supports(mixed $resource, string $type = null): bool
|
||||
{
|
||||
return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'xml' === $type);
|
||||
return \is_string($resource) && 'xml' === pathinfo($resource, \PATHINFO_EXTENSION) && (!$type || 'xml' === $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a route and adds it to the RouteCollection.
|
||||
*
|
||||
* @param RouteCollection $collection RouteCollection instance
|
||||
* @param \DOMElement $node Element to parse that represents a Route
|
||||
* @param string $path Full path of the XML file being processed
|
||||
*
|
||||
* @throws \InvalidArgumentException When the XML is invalid
|
||||
*/
|
||||
protected function parseRoute(RouteCollection $collection, \DOMElement $node, $path)
|
||||
protected function parseRoute(RouteCollection $collection, \DOMElement $node, string $path)
|
||||
{
|
||||
if ('' === ($id = $node->getAttribute('id')) || !$node->hasAttribute('path')) {
|
||||
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" and a "path" attribute.', $path));
|
||||
if ('' === $id = $node->getAttribute('id')) {
|
||||
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" attribute.', $path));
|
||||
}
|
||||
|
||||
$schemes = preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY);
|
||||
$methods = preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY);
|
||||
if ('' !== $alias = $node->getAttribute('alias')) {
|
||||
$alias = $collection->addAlias($id, $alias);
|
||||
|
||||
list($defaults, $requirements, $options, $condition) = $this->parseConfigs($node, $path);
|
||||
if ($deprecationInfo = $this->parseDeprecation($node, $path)) {
|
||||
$alias->setDeprecated($deprecationInfo['package'], $deprecationInfo['version'], $deprecationInfo['message']);
|
||||
}
|
||||
|
||||
$route = new Route($node->getAttribute('path'), $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition);
|
||||
$collection->add($id, $route);
|
||||
return;
|
||||
}
|
||||
|
||||
$schemes = preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, \PREG_SPLIT_NO_EMPTY);
|
||||
$methods = preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, \PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
[$defaults, $requirements, $options, $condition, $paths, /* $prefixes */, $hosts] = $this->parseConfigs($node, $path);
|
||||
|
||||
if (!$paths && '' === $node->getAttribute('path')) {
|
||||
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have a "path" attribute or <path> child nodes.', $path));
|
||||
}
|
||||
|
||||
if ($paths && '' !== $node->getAttribute('path')) {
|
||||
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must not have both a "path" attribute and <path> child nodes.', $path));
|
||||
}
|
||||
|
||||
$routes = $this->createLocalizedRoute($collection, $id, $paths ?: $node->getAttribute('path'));
|
||||
$routes->addDefaults($defaults);
|
||||
$routes->addRequirements($requirements);
|
||||
$routes->addOptions($options);
|
||||
$routes->setSchemes($schemes);
|
||||
$routes->setMethods($methods);
|
||||
$routes->setCondition($condition);
|
||||
|
||||
if (null !== $hosts) {
|
||||
$this->addHost($routes, $hosts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an import and adds the routes in the resource to the RouteCollection.
|
||||
*
|
||||
* @param RouteCollection $collection RouteCollection instance
|
||||
* @param \DOMElement $node Element to parse that represents a Route
|
||||
* @param string $path Full path of the XML file being processed
|
||||
* @param string $file Loaded file name
|
||||
*
|
||||
* @throws \InvalidArgumentException When the XML is invalid
|
||||
*/
|
||||
protected function parseImport(RouteCollection $collection, \DOMElement $node, $path, $file)
|
||||
protected function parseImport(RouteCollection $collection, \DOMElement $node, string $path, string $file)
|
||||
{
|
||||
if ('' === $resource = $node->getAttribute('resource')) {
|
||||
throw new \InvalidArgumentException(sprintf('The <import> element in file "%s" must have a "resource" attribute.', $path));
|
||||
@@ -138,48 +160,73 @@ class XmlFileLoader extends FileLoader
|
||||
|
||||
$type = $node->getAttribute('type');
|
||||
$prefix = $node->getAttribute('prefix');
|
||||
$host = $node->hasAttribute('host') ? $node->getAttribute('host') : null;
|
||||
$schemes = $node->hasAttribute('schemes') ? preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY) : null;
|
||||
$methods = $node->hasAttribute('methods') ? preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY) : null;
|
||||
$schemes = $node->hasAttribute('schemes') ? preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, \PREG_SPLIT_NO_EMPTY) : null;
|
||||
$methods = $node->hasAttribute('methods') ? preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, \PREG_SPLIT_NO_EMPTY) : null;
|
||||
$trailingSlashOnRoot = $node->hasAttribute('trailing-slash-on-root') ? XmlUtils::phpize($node->getAttribute('trailing-slash-on-root')) : true;
|
||||
$namePrefix = $node->getAttribute('name-prefix') ?: null;
|
||||
|
||||
list($defaults, $requirements, $options, $condition) = $this->parseConfigs($node, $path);
|
||||
[$defaults, $requirements, $options, $condition, /* $paths */, $prefixes, $hosts] = $this->parseConfigs($node, $path);
|
||||
|
||||
$this->setCurrentDir(dirname($path));
|
||||
if ('' !== $prefix && $prefixes) {
|
||||
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must not have both a "prefix" attribute and <prefix> child nodes.', $path));
|
||||
}
|
||||
|
||||
$subCollection = $this->import($resource, ('' !== $type ? $type : null), false, $file);
|
||||
/* @var $subCollection RouteCollection */
|
||||
$subCollection->addPrefix($prefix);
|
||||
if (null !== $host) {
|
||||
$subCollection->setHost($host);
|
||||
$exclude = [];
|
||||
foreach ($node->childNodes as $child) {
|
||||
if ($child instanceof \DOMElement && $child->localName === $exclude && self::NAMESPACE_URI === $child->namespaceURI) {
|
||||
$exclude[] = $child->nodeValue;
|
||||
}
|
||||
}
|
||||
if (null !== $condition) {
|
||||
$subCollection->setCondition($condition);
|
||||
}
|
||||
if (null !== $schemes) {
|
||||
$subCollection->setSchemes($schemes);
|
||||
}
|
||||
if (null !== $methods) {
|
||||
$subCollection->setMethods($methods);
|
||||
}
|
||||
$subCollection->addDefaults($defaults);
|
||||
$subCollection->addRequirements($requirements);
|
||||
$subCollection->addOptions($options);
|
||||
|
||||
$collection->addCollection($subCollection);
|
||||
if ($node->hasAttribute('exclude')) {
|
||||
if ($exclude) {
|
||||
throw new \InvalidArgumentException('You cannot use both the attribute "exclude" and <exclude> tags at the same time.');
|
||||
}
|
||||
$exclude = [$node->getAttribute('exclude')];
|
||||
}
|
||||
|
||||
$this->setCurrentDir(\dirname($path));
|
||||
|
||||
/** @var RouteCollection[] $imported */
|
||||
$imported = $this->import($resource, '' !== $type ? $type : null, false, $file, $exclude) ?: [];
|
||||
|
||||
if (!\is_array($imported)) {
|
||||
$imported = [$imported];
|
||||
}
|
||||
|
||||
foreach ($imported as $subCollection) {
|
||||
$this->addPrefix($subCollection, $prefixes ?: $prefix, $trailingSlashOnRoot);
|
||||
|
||||
if (null !== $hosts) {
|
||||
$this->addHost($subCollection, $hosts);
|
||||
}
|
||||
|
||||
if (null !== $condition) {
|
||||
$subCollection->setCondition($condition);
|
||||
}
|
||||
if (null !== $schemes) {
|
||||
$subCollection->setSchemes($schemes);
|
||||
}
|
||||
if (null !== $methods) {
|
||||
$subCollection->setMethods($methods);
|
||||
}
|
||||
if (null !== $namePrefix) {
|
||||
$subCollection->addNamePrefix($namePrefix);
|
||||
}
|
||||
$subCollection->addDefaults($defaults);
|
||||
$subCollection->addRequirements($requirements);
|
||||
$subCollection->addOptions($options);
|
||||
|
||||
$collection->addCollection($subCollection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an XML file.
|
||||
*
|
||||
* @param string $file An XML file path
|
||||
*
|
||||
* @return \DOMDocument
|
||||
*
|
||||
* @throws \InvalidArgumentException When loading of XML file fails because of syntax errors
|
||||
* or when the XML structure is not as expected by the scheme -
|
||||
* see validate()
|
||||
*/
|
||||
protected function loadFile($file)
|
||||
protected function loadFile(string $file): \DOMDocument
|
||||
{
|
||||
return XmlUtils::loadFile($file, __DIR__.static::SCHEME_PATH);
|
||||
}
|
||||
@@ -187,26 +234,34 @@ class XmlFileLoader extends FileLoader
|
||||
/**
|
||||
* Parses the config elements (default, requirement, option).
|
||||
*
|
||||
* @param \DOMElement $node Element to parse that contains the configs
|
||||
* @param string $path Full path of the XML file being processed
|
||||
*
|
||||
* @return array An array with the defaults as first item, requirements as second and options as third
|
||||
*
|
||||
* @throws \InvalidArgumentException When the XML is invalid
|
||||
*/
|
||||
private function parseConfigs(\DOMElement $node, $path)
|
||||
private function parseConfigs(\DOMElement $node, string $path): array
|
||||
{
|
||||
$defaults = array();
|
||||
$requirements = array();
|
||||
$options = array();
|
||||
$defaults = [];
|
||||
$requirements = [];
|
||||
$options = [];
|
||||
$condition = null;
|
||||
$prefixes = [];
|
||||
$paths = [];
|
||||
$hosts = [];
|
||||
|
||||
/** @var \DOMElement $n */
|
||||
foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) {
|
||||
if ($node !== $n->parentNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($n->localName) {
|
||||
case 'path':
|
||||
$paths[$n->getAttribute('locale')] = trim($n->textContent);
|
||||
break;
|
||||
case 'host':
|
||||
$hosts[$n->getAttribute('locale')] = trim($n->textContent);
|
||||
break;
|
||||
case 'prefix':
|
||||
$prefixes[$n->getAttribute('locale')] = trim($n->textContent);
|
||||
break;
|
||||
case 'default':
|
||||
if ($this->isElementValueNull($n)) {
|
||||
$defaults[$n->getAttribute('key')] = null;
|
||||
@@ -219,7 +274,7 @@ class XmlFileLoader extends FileLoader
|
||||
$requirements[$n->getAttribute('key')] = trim($n->textContent);
|
||||
break;
|
||||
case 'option':
|
||||
$options[$n->getAttribute('key')] = trim($n->textContent);
|
||||
$options[$n->getAttribute('key')] = XmlUtils::phpize(trim($n->textContent));
|
||||
break;
|
||||
case 'condition':
|
||||
$condition = trim($n->textContent);
|
||||
@@ -229,21 +284,48 @@ class XmlFileLoader extends FileLoader
|
||||
}
|
||||
}
|
||||
|
||||
return array($defaults, $requirements, $options, $condition);
|
||||
if ($controller = $node->getAttribute('controller')) {
|
||||
if (isset($defaults['_controller'])) {
|
||||
$name = $node->hasAttribute('id') ? sprintf('"%s".', $node->getAttribute('id')) : sprintf('the "%s" tag.', $node->tagName);
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "controller" attribute and the defaults key "_controller" for ', $path).$name);
|
||||
}
|
||||
|
||||
$defaults['_controller'] = $controller;
|
||||
}
|
||||
if ($node->hasAttribute('locale')) {
|
||||
$defaults['_locale'] = $node->getAttribute('locale');
|
||||
}
|
||||
if ($node->hasAttribute('format')) {
|
||||
$defaults['_format'] = $node->getAttribute('format');
|
||||
}
|
||||
if ($node->hasAttribute('utf8')) {
|
||||
$options['utf8'] = XmlUtils::phpize($node->getAttribute('utf8'));
|
||||
}
|
||||
if ($stateless = $node->getAttribute('stateless')) {
|
||||
if (isset($defaults['_stateless'])) {
|
||||
$name = $node->hasAttribute('id') ? sprintf('"%s".', $node->getAttribute('id')) : sprintf('the "%s" tag.', $node->tagName);
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "stateless" attribute and the defaults key "_stateless" for ', $path).$name);
|
||||
}
|
||||
|
||||
$defaults['_stateless'] = XmlUtils::phpize($stateless);
|
||||
}
|
||||
|
||||
if (!$hosts) {
|
||||
$hosts = $node->hasAttribute('host') ? $node->getAttribute('host') : null;
|
||||
}
|
||||
|
||||
return [$defaults, $requirements, $options, $condition, $paths, $prefixes, $hosts];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the "default" elements.
|
||||
*
|
||||
* @param \DOMElement $element The "default" element to parse
|
||||
* @param string $path Full path of the XML file being processed
|
||||
*
|
||||
* @return array|bool|float|int|string|null The parsed value of the "default" element
|
||||
*/
|
||||
private function parseDefaultsConfig(\DOMElement $element, $path)
|
||||
private function parseDefaultsConfig(\DOMElement $element, string $path): array|bool|float|int|string|null
|
||||
{
|
||||
if ($this->isElementValueNull($element)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check for existing element nodes in the default element. There can
|
||||
@@ -270,17 +352,12 @@ class XmlFileLoader extends FileLoader
|
||||
/**
|
||||
* Recursively parses the value of a "default" element.
|
||||
*
|
||||
* @param \DOMElement $node The node value
|
||||
* @param string $path Full path of the XML file being processed
|
||||
*
|
||||
* @return array|bool|float|int|string The parsed value
|
||||
*
|
||||
* @throws \InvalidArgumentException when the XML is invalid
|
||||
*/
|
||||
private function parseDefaultNode(\DOMElement $node, $path)
|
||||
private function parseDefaultNode(\DOMElement $node, string $path): array|bool|float|int|string|null
|
||||
{
|
||||
if ($this->isElementValueNull($node)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
switch ($node->localName) {
|
||||
@@ -293,7 +370,7 @@ class XmlFileLoader extends FileLoader
|
||||
case 'string':
|
||||
return trim($node->nodeValue);
|
||||
case 'list':
|
||||
$list = array();
|
||||
$list = [];
|
||||
|
||||
foreach ($node->childNodes as $element) {
|
||||
if (!$element instanceof \DOMElement) {
|
||||
@@ -309,7 +386,7 @@ class XmlFileLoader extends FileLoader
|
||||
|
||||
return $list;
|
||||
case 'map':
|
||||
$map = array();
|
||||
$map = [];
|
||||
|
||||
foreach ($node->childNodes as $element) {
|
||||
if (!$element instanceof \DOMElement) {
|
||||
@@ -329,7 +406,7 @@ class XmlFileLoader extends FileLoader
|
||||
}
|
||||
}
|
||||
|
||||
private function isElementValueNull(\DOMElement $element)
|
||||
private function isElementValueNull(\DOMElement $element): bool
|
||||
{
|
||||
$namespaceUri = 'http://www.w3.org/2001/XMLSchema-instance';
|
||||
|
||||
@@ -339,4 +416,41 @@ class XmlFileLoader extends FileLoader
|
||||
|
||||
return 'true' === $element->getAttributeNS($namespaceUri, 'nil') || '1' === $element->getAttributeNS($namespaceUri, 'nil');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the deprecation elements.
|
||||
*
|
||||
* @throws \InvalidArgumentException When the XML is invalid
|
||||
*/
|
||||
private function parseDeprecation(\DOMElement $node, string $path): array
|
||||
{
|
||||
$deprecatedNode = null;
|
||||
foreach ($node->childNodes as $child) {
|
||||
if (!$child instanceof \DOMElement || self::NAMESPACE_URI !== $child->namespaceURI) {
|
||||
continue;
|
||||
}
|
||||
if ('deprecated' !== $child->localName) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid child element "%s" defined for alias "%s" in "%s".', $child->localName, $node->getAttribute('id'), $path));
|
||||
}
|
||||
|
||||
$deprecatedNode = $child;
|
||||
}
|
||||
|
||||
if (null === $deprecatedNode) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!$deprecatedNode->hasAttribute('package')) {
|
||||
throw new \InvalidArgumentException(sprintf('The <deprecated> element in file "%s" must have a "package" attribute.', $path));
|
||||
}
|
||||
if (!$deprecatedNode->hasAttribute('version')) {
|
||||
throw new \InvalidArgumentException(sprintf('The <deprecated> element in file "%s" must have a "version" attribute.', $path));
|
||||
}
|
||||
|
||||
return [
|
||||
'package' => $deprecatedNode->getAttribute('package'),
|
||||
'version' => $deprecatedNode->getAttribute('version'),
|
||||
'message' => trim($deprecatedNode->nodeValue),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
285
vendor/symfony/routing/Loader/YamlFileLoader.php
vendored
285
vendor/symfony/routing/Loader/YamlFileLoader.php
vendored
@@ -11,12 +11,14 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Loader;
|
||||
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Config\Loader\FileLoader;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\HostTrait;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\LocalizedRouteTrait;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\PrefixTrait;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Yaml\Exception\ParseException;
|
||||
use Symfony\Component\Yaml\Parser as YamlParser;
|
||||
use Symfony\Component\Config\Loader\FileLoader;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
@@ -27,22 +29,19 @@ use Symfony\Component\Yaml\Yaml;
|
||||
*/
|
||||
class YamlFileLoader extends FileLoader
|
||||
{
|
||||
private static $availableKeys = array(
|
||||
'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition',
|
||||
);
|
||||
use HostTrait;
|
||||
use LocalizedRouteTrait;
|
||||
use PrefixTrait;
|
||||
|
||||
private const AVAILABLE_KEYS = [
|
||||
'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', 'name_prefix', 'trailing_slash_on_root', 'locale', 'format', 'utf8', 'exclude', 'stateless',
|
||||
];
|
||||
private $yamlParser;
|
||||
|
||||
/**
|
||||
* Loads a Yaml file.
|
||||
*
|
||||
* @param string $file A Yaml file path
|
||||
* @param string|null $type The resource type
|
||||
*
|
||||
* @return RouteCollection A RouteCollection instance
|
||||
*
|
||||
* @throws \InvalidArgumentException When a route can't be parsed because YAML is invalid
|
||||
*/
|
||||
public function load($file, $type = null)
|
||||
public function load(mixed $file, string $type = null): RouteCollection
|
||||
{
|
||||
$path = $this->locator->locate($file);
|
||||
|
||||
@@ -54,14 +53,12 @@ class YamlFileLoader extends FileLoader
|
||||
throw new \InvalidArgumentException(sprintf('File "%s" not found.', $path));
|
||||
}
|
||||
|
||||
if (null === $this->yamlParser) {
|
||||
$this->yamlParser = new YamlParser();
|
||||
}
|
||||
$this->yamlParser ??= new YamlParser();
|
||||
|
||||
try {
|
||||
$parsedConfig = $this->yamlParser->parse(file_get_contents($path), Yaml::PARSE_KEYS_AS_STRINGS);
|
||||
$parsedConfig = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT);
|
||||
} catch (ParseException $e) {
|
||||
throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e);
|
||||
throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: ', $path).$e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
$collection = new RouteCollection();
|
||||
@@ -73,11 +70,29 @@ class YamlFileLoader extends FileLoader
|
||||
}
|
||||
|
||||
// not an array
|
||||
if (!is_array($parsedConfig)) {
|
||||
if (!\is_array($parsedConfig)) {
|
||||
throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $path));
|
||||
}
|
||||
|
||||
foreach ($parsedConfig as $name => $config) {
|
||||
if (0 === strpos($name, 'when@')) {
|
||||
if (!$this->env || 'when@'.$this->env !== $name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($config as $name => $config) {
|
||||
$this->validate($config, $name.'" when "@'.$this->env, $path);
|
||||
|
||||
if (isset($config['resource'])) {
|
||||
$this->parseImport($collection, $config, $path, $file);
|
||||
} else {
|
||||
$this->parseRoute($collection, $name, $config, $path);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->validate($config, $name, $path);
|
||||
|
||||
if (isset($config['resource'])) {
|
||||
@@ -93,116 +108,192 @@ class YamlFileLoader extends FileLoader
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports($resource, $type = null)
|
||||
public function supports(mixed $resource, string $type = null): bool
|
||||
{
|
||||
return is_string($resource) && in_array(pathinfo($resource, PATHINFO_EXTENSION), array('yml', 'yaml'), true) && (!$type || 'yaml' === $type);
|
||||
return \is_string($resource) && \in_array(pathinfo($resource, \PATHINFO_EXTENSION), ['yml', 'yaml'], true) && (!$type || 'yaml' === $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a route and adds it to the RouteCollection.
|
||||
*
|
||||
* @param RouteCollection $collection A RouteCollection instance
|
||||
* @param string $name Route name
|
||||
* @param array $config Route definition
|
||||
* @param string $path Full path of the YAML file being processed
|
||||
*/
|
||||
protected function parseRoute(RouteCollection $collection, $name, array $config, $path)
|
||||
protected function parseRoute(RouteCollection $collection, string $name, array $config, string $path)
|
||||
{
|
||||
$defaults = isset($config['defaults']) ? $config['defaults'] : array();
|
||||
$requirements = isset($config['requirements']) ? $config['requirements'] : array();
|
||||
$options = isset($config['options']) ? $config['options'] : array();
|
||||
$host = isset($config['host']) ? $config['host'] : '';
|
||||
$schemes = isset($config['schemes']) ? $config['schemes'] : array();
|
||||
$methods = isset($config['methods']) ? $config['methods'] : array();
|
||||
$condition = isset($config['condition']) ? $config['condition'] : null;
|
||||
if (isset($config['alias'])) {
|
||||
$alias = $collection->addAlias($name, $config['alias']);
|
||||
$deprecation = $config['deprecated'] ?? null;
|
||||
if (null !== $deprecation) {
|
||||
$alias->setDeprecated(
|
||||
$deprecation['package'],
|
||||
$deprecation['version'],
|
||||
$deprecation['message'] ?? ''
|
||||
);
|
||||
}
|
||||
|
||||
$route = new Route($config['path'], $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
|
||||
return;
|
||||
}
|
||||
|
||||
$collection->add($name, $route);
|
||||
$defaults = $config['defaults'] ?? [];
|
||||
$requirements = $config['requirements'] ?? [];
|
||||
$options = $config['options'] ?? [];
|
||||
|
||||
foreach ($requirements as $placeholder => $requirement) {
|
||||
if (\is_int($placeholder)) {
|
||||
throw new \InvalidArgumentException(sprintf('A placeholder name must be a string (%d given). Did you forget to specify the placeholder key for the requirement "%s" of route "%s" in "%s"?', $placeholder, $requirement, $name, $path));
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($config['controller'])) {
|
||||
$defaults['_controller'] = $config['controller'];
|
||||
}
|
||||
if (isset($config['locale'])) {
|
||||
$defaults['_locale'] = $config['locale'];
|
||||
}
|
||||
if (isset($config['format'])) {
|
||||
$defaults['_format'] = $config['format'];
|
||||
}
|
||||
if (isset($config['utf8'])) {
|
||||
$options['utf8'] = $config['utf8'];
|
||||
}
|
||||
if (isset($config['stateless'])) {
|
||||
$defaults['_stateless'] = $config['stateless'];
|
||||
}
|
||||
|
||||
$routes = $this->createLocalizedRoute($collection, $name, $config['path']);
|
||||
$routes->addDefaults($defaults);
|
||||
$routes->addRequirements($requirements);
|
||||
$routes->addOptions($options);
|
||||
$routes->setSchemes($config['schemes'] ?? []);
|
||||
$routes->setMethods($config['methods'] ?? []);
|
||||
$routes->setCondition($config['condition'] ?? null);
|
||||
|
||||
if (isset($config['host'])) {
|
||||
$this->addHost($routes, $config['host']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an import and adds the routes in the resource to the RouteCollection.
|
||||
*
|
||||
* @param RouteCollection $collection A RouteCollection instance
|
||||
* @param array $config Route definition
|
||||
* @param string $path Full path of the YAML file being processed
|
||||
* @param string $file Loaded file name
|
||||
*/
|
||||
protected function parseImport(RouteCollection $collection, array $config, $path, $file)
|
||||
protected function parseImport(RouteCollection $collection, array $config, string $path, string $file)
|
||||
{
|
||||
$type = isset($config['type']) ? $config['type'] : null;
|
||||
$prefix = isset($config['prefix']) ? $config['prefix'] : '';
|
||||
$defaults = isset($config['defaults']) ? $config['defaults'] : array();
|
||||
$requirements = isset($config['requirements']) ? $config['requirements'] : array();
|
||||
$options = isset($config['options']) ? $config['options'] : array();
|
||||
$host = isset($config['host']) ? $config['host'] : null;
|
||||
$condition = isset($config['condition']) ? $config['condition'] : null;
|
||||
$schemes = isset($config['schemes']) ? $config['schemes'] : null;
|
||||
$methods = isset($config['methods']) ? $config['methods'] : null;
|
||||
$type = $config['type'] ?? null;
|
||||
$prefix = $config['prefix'] ?? '';
|
||||
$defaults = $config['defaults'] ?? [];
|
||||
$requirements = $config['requirements'] ?? [];
|
||||
$options = $config['options'] ?? [];
|
||||
$host = $config['host'] ?? null;
|
||||
$condition = $config['condition'] ?? null;
|
||||
$schemes = $config['schemes'] ?? null;
|
||||
$methods = $config['methods'] ?? null;
|
||||
$trailingSlashOnRoot = $config['trailing_slash_on_root'] ?? true;
|
||||
$namePrefix = $config['name_prefix'] ?? null;
|
||||
$exclude = $config['exclude'] ?? null;
|
||||
|
||||
$this->setCurrentDir(dirname($path));
|
||||
if (isset($config['controller'])) {
|
||||
$defaults['_controller'] = $config['controller'];
|
||||
}
|
||||
if (isset($config['locale'])) {
|
||||
$defaults['_locale'] = $config['locale'];
|
||||
}
|
||||
if (isset($config['format'])) {
|
||||
$defaults['_format'] = $config['format'];
|
||||
}
|
||||
if (isset($config['utf8'])) {
|
||||
$options['utf8'] = $config['utf8'];
|
||||
}
|
||||
if (isset($config['stateless'])) {
|
||||
$defaults['_stateless'] = $config['stateless'];
|
||||
}
|
||||
|
||||
$subCollection = $this->import($config['resource'], $type, false, $file);
|
||||
/* @var $subCollection RouteCollection */
|
||||
$subCollection->addPrefix($prefix);
|
||||
if (null !== $host) {
|
||||
$subCollection->setHost($host);
|
||||
}
|
||||
if (null !== $condition) {
|
||||
$subCollection->setCondition($condition);
|
||||
}
|
||||
if (null !== $schemes) {
|
||||
$subCollection->setSchemes($schemes);
|
||||
}
|
||||
if (null !== $methods) {
|
||||
$subCollection->setMethods($methods);
|
||||
}
|
||||
$subCollection->addDefaults($defaults);
|
||||
$subCollection->addRequirements($requirements);
|
||||
$subCollection->addOptions($options);
|
||||
$this->setCurrentDir(\dirname($path));
|
||||
|
||||
$collection->addCollection($subCollection);
|
||||
/** @var RouteCollection[] $imported */
|
||||
$imported = $this->import($config['resource'], $type, false, $file, $exclude) ?: [];
|
||||
|
||||
if (!\is_array($imported)) {
|
||||
$imported = [$imported];
|
||||
}
|
||||
|
||||
foreach ($imported as $subCollection) {
|
||||
$this->addPrefix($subCollection, $prefix, $trailingSlashOnRoot);
|
||||
|
||||
if (null !== $host) {
|
||||
$this->addHost($subCollection, $host);
|
||||
}
|
||||
if (null !== $condition) {
|
||||
$subCollection->setCondition($condition);
|
||||
}
|
||||
if (null !== $schemes) {
|
||||
$subCollection->setSchemes($schemes);
|
||||
}
|
||||
if (null !== $methods) {
|
||||
$subCollection->setMethods($methods);
|
||||
}
|
||||
if (null !== $namePrefix) {
|
||||
$subCollection->addNamePrefix($namePrefix);
|
||||
}
|
||||
$subCollection->addDefaults($defaults);
|
||||
$subCollection->addRequirements($requirements);
|
||||
$subCollection->addOptions($options);
|
||||
|
||||
$collection->addCollection($subCollection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the route configuration.
|
||||
*
|
||||
* @param array $config A resource config
|
||||
* @param string $name The config key
|
||||
* @param string $path The loaded file path
|
||||
*
|
||||
* @throws \InvalidArgumentException If one of the provided config keys is not supported,
|
||||
* something is missing or the combination is nonsense
|
||||
*/
|
||||
protected function validate($config, $name, $path)
|
||||
protected function validate(mixed $config, string $name, string $path)
|
||||
{
|
||||
if (!is_array($config)) {
|
||||
if (!\is_array($config)) {
|
||||
throw new \InvalidArgumentException(sprintf('The definition of "%s" in "%s" must be a YAML array.', $name, $path));
|
||||
}
|
||||
if ($extraKeys = array_diff(array_keys($config), self::$availableKeys)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'The routing file "%s" contains unsupported keys for "%s": "%s". Expected one of: "%s".',
|
||||
$path, $name, implode('", "', $extraKeys), implode('", "', self::$availableKeys)
|
||||
));
|
||||
if (isset($config['alias'])) {
|
||||
$this->validateAlias($config, $name, $path);
|
||||
|
||||
return;
|
||||
}
|
||||
if ($extraKeys = array_diff(array_keys($config), self::AVAILABLE_KEYS)) {
|
||||
throw new \InvalidArgumentException(sprintf('The routing file "%s" contains unsupported keys for "%s": "%s". Expected one of: "%s".', $path, $name, implode('", "', $extraKeys), implode('", "', self::AVAILABLE_KEYS)));
|
||||
}
|
||||
if (isset($config['resource']) && isset($config['path'])) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'The routing file "%s" must not specify both the "resource" key and the "path" key for "%s". Choose between an import and a route definition.',
|
||||
$path, $name
|
||||
));
|
||||
throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "resource" key and the "path" key for "%s". Choose between an import and a route definition.', $path, $name));
|
||||
}
|
||||
if (!isset($config['resource']) && isset($config['type'])) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'The "type" key for the route definition "%s" in "%s" is unsupported. It is only available for imports in combination with the "resource" key.',
|
||||
$name, $path
|
||||
));
|
||||
throw new \InvalidArgumentException(sprintf('The "type" key for the route definition "%s" in "%s" is unsupported. It is only available for imports in combination with the "resource" key.', $name, $path));
|
||||
}
|
||||
if (!isset($config['resource']) && !isset($config['path'])) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'You must define a "path" for the route "%s" in file "%s".',
|
||||
$name, $path
|
||||
));
|
||||
throw new \InvalidArgumentException(sprintf('You must define a "path" for the route "%s" in file "%s".', $name, $path));
|
||||
}
|
||||
if (isset($config['controller']) && isset($config['defaults']['_controller'])) {
|
||||
throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "controller" key and the defaults key "_controller" for "%s".', $path, $name));
|
||||
}
|
||||
if (isset($config['stateless']) && isset($config['defaults']['_stateless'])) {
|
||||
throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "stateless" key and the defaults key "_stateless" for "%s".', $path, $name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException If one of the provided config keys is not supported,
|
||||
* something is missing or the combination is nonsense
|
||||
*/
|
||||
private function validateAlias(array $config, string $name, string $path): void
|
||||
{
|
||||
foreach ($config as $key => $value) {
|
||||
if (!\in_array($key, ['alias', 'deprecated'], true)) {
|
||||
throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify other keys than "alias" and "deprecated" for "%s".', $path, $name));
|
||||
}
|
||||
|
||||
if ('deprecated' === $key) {
|
||||
if (!isset($value['package'])) {
|
||||
throw new \InvalidArgumentException(sprintf('The routing file "%s" must specify the attribute "package" of the "deprecated" option for "%s".', $path, $name));
|
||||
}
|
||||
|
||||
if (!isset($value['version'])) {
|
||||
throw new \InvalidArgumentException(sprintf('The routing file "%s" must specify the attribute "version" of the "deprecated" option for "%s".', $path, $name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,26 @@
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="import" type="import" />
|
||||
<xsd:element name="route" type="route" />
|
||||
<xsd:element name="when" type="when" />
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="when">
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="import" type="import" />
|
||||
<xsd:element name="route" type="route" />
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="env" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="localized-path">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="locale" type="xsd:string" use="required" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:group name="configs">
|
||||
<xsd:choice>
|
||||
<xsd:element name="default" nillable="true" type="default" />
|
||||
@@ -34,24 +51,46 @@
|
||||
</xsd:group>
|
||||
|
||||
<xsd:complexType name="route">
|
||||
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
|
||||
|
||||
<xsd:sequence>
|
||||
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="path" type="localized-path" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="host" type="localized-path" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="deprecated" type="deprecated" minOccurs="0" maxOccurs="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="path" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="path" type="xsd:string" />
|
||||
<xsd:attribute name="host" type="xsd:string" />
|
||||
<xsd:attribute name="schemes" type="xsd:string" />
|
||||
<xsd:attribute name="methods" type="xsd:string" />
|
||||
<xsd:attribute name="controller" type="xsd:string" />
|
||||
<xsd:attribute name="locale" type="xsd:string" />
|
||||
<xsd:attribute name="format" type="xsd:string" />
|
||||
<xsd:attribute name="utf8" type="xsd:boolean" />
|
||||
<xsd:attribute name="stateless" type="xsd:boolean" />
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="import">
|
||||
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
|
||||
|
||||
<xsd:sequence maxOccurs="unbounded" minOccurs="0">
|
||||
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="prefix" type="localized-path" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="exclude" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="host" type="localized-path" minOccurs="0" maxOccurs="unbounded" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="resource" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="exclude" type="xsd:string" />
|
||||
<xsd:attribute name="prefix" type="xsd:string" />
|
||||
<xsd:attribute name="name-prefix" type="xsd:string" />
|
||||
<xsd:attribute name="host" type="xsd:string" />
|
||||
<xsd:attribute name="schemes" type="xsd:string" />
|
||||
<xsd:attribute name="methods" type="xsd:string" />
|
||||
<xsd:attribute name="controller" type="xsd:string" />
|
||||
<xsd:attribute name="locale" type="xsd:string" />
|
||||
<xsd:attribute name="format" type="xsd:string" />
|
||||
<xsd:attribute name="trailing-slash-on-root" type="xsd:boolean" />
|
||||
<xsd:attribute name="utf8" type="xsd:boolean" />
|
||||
<xsd:attribute name="stateless" type="xsd:boolean" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="default" mixed="true">
|
||||
@@ -143,4 +182,13 @@
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="deprecated">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="package" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="version" type="xsd:string" use="required" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:schema>
|
||||
|
||||
31
vendor/symfony/routing/Matcher/CompiledUrlMatcher.php
vendored
Normal file
31
vendor/symfony/routing/Matcher/CompiledUrlMatcher.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher;
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* Matches URLs based on rules dumped by CompiledUrlMatcherDumper.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class CompiledUrlMatcher extends UrlMatcher
|
||||
{
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(array $compiledRoutes, RequestContext $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
[$this->matchHost, $this->staticRoutes, $this->regexpList, $this->dynamicRoutes, $this->checkCondition] = $compiledRoutes;
|
||||
}
|
||||
}
|
||||
501
vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherDumper.php
vendored
Normal file
501
vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherDumper.php
vendored
Normal file
@@ -0,0 +1,501 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher\Dumper;
|
||||
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* CompiledUrlMatcherDumper creates PHP arrays to be used with CompiledUrlMatcher.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class CompiledUrlMatcherDumper extends MatcherDumper
|
||||
{
|
||||
private $expressionLanguage;
|
||||
private ?\Exception $signalingException = null;
|
||||
|
||||
/**
|
||||
* @var ExpressionFunctionProviderInterface[]
|
||||
*/
|
||||
private array $expressionLanguageProviders = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump(array $options = []): string
|
||||
{
|
||||
return <<<EOF
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
{$this->generateCompiledRoutes()}];
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
|
||||
{
|
||||
$this->expressionLanguageProviders[] = $provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the arrays for CompiledUrlMatcher's constructor.
|
||||
*/
|
||||
public function getCompiledRoutes(bool $forDump = false): array
|
||||
{
|
||||
// Group hosts by same-suffix, re-order when possible
|
||||
$matchHost = false;
|
||||
$routes = new StaticPrefixCollection();
|
||||
foreach ($this->getRoutes()->all() as $name => $route) {
|
||||
if ($host = $route->getHost()) {
|
||||
$matchHost = true;
|
||||
$host = '/'.strtr(strrev($host), '}.{', '(/)');
|
||||
}
|
||||
|
||||
$routes->addRoute($host ?: '/(.*)', [$name, $route]);
|
||||
}
|
||||
|
||||
if ($matchHost) {
|
||||
$compiledRoutes = [true];
|
||||
$routes = $routes->populateCollection(new RouteCollection());
|
||||
} else {
|
||||
$compiledRoutes = [false];
|
||||
$routes = $this->getRoutes();
|
||||
}
|
||||
|
||||
[$staticRoutes, $dynamicRoutes] = $this->groupStaticRoutes($routes);
|
||||
|
||||
$conditions = [null];
|
||||
$compiledRoutes[] = $this->compileStaticRoutes($staticRoutes, $conditions);
|
||||
$chunkLimit = \count($dynamicRoutes);
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
$this->signalingException = new \RuntimeException('Compilation failed: regular expression is too large');
|
||||
$compiledRoutes = array_merge($compiledRoutes, $this->compileDynamicRoutes($dynamicRoutes, $matchHost, $chunkLimit, $conditions));
|
||||
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
if (1 < $chunkLimit && $this->signalingException === $e) {
|
||||
$chunkLimit = 1 + ($chunkLimit >> 1);
|
||||
continue;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if ($forDump) {
|
||||
$compiledRoutes[2] = $compiledRoutes[4];
|
||||
}
|
||||
unset($conditions[0]);
|
||||
|
||||
if ($conditions) {
|
||||
foreach ($conditions as $expression => $condition) {
|
||||
$conditions[$expression] = "case {$condition}: return {$expression};";
|
||||
}
|
||||
|
||||
$checkConditionCode = <<<EOF
|
||||
static function (\$condition, \$context, \$request) { // \$checkCondition
|
||||
switch (\$condition) {
|
||||
{$this->indent(implode("\n", $conditions), 3)}
|
||||
}
|
||||
}
|
||||
EOF;
|
||||
$compiledRoutes[4] = $forDump ? $checkConditionCode.",\n" : eval('return '.$checkConditionCode.';');
|
||||
} else {
|
||||
$compiledRoutes[4] = $forDump ? " null, // \$checkCondition\n" : null;
|
||||
}
|
||||
|
||||
return $compiledRoutes;
|
||||
}
|
||||
|
||||
private function generateCompiledRoutes(): string
|
||||
{
|
||||
[$matchHost, $staticRoutes, $regexpCode, $dynamicRoutes, $checkConditionCode] = $this->getCompiledRoutes(true);
|
||||
|
||||
$code = self::export($matchHost).', // $matchHost'."\n";
|
||||
|
||||
$code .= '[ // $staticRoutes'."\n";
|
||||
foreach ($staticRoutes as $path => $routes) {
|
||||
$code .= sprintf(" %s => [\n", self::export($path));
|
||||
foreach ($routes as $route) {
|
||||
$code .= sprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", ...array_map([__CLASS__, 'export'], $route));
|
||||
}
|
||||
$code .= " ],\n";
|
||||
}
|
||||
$code .= "],\n";
|
||||
|
||||
$code .= sprintf("[ // \$regexpList%s\n],\n", $regexpCode);
|
||||
|
||||
$code .= '[ // $dynamicRoutes'."\n";
|
||||
foreach ($dynamicRoutes as $path => $routes) {
|
||||
$code .= sprintf(" %s => [\n", self::export($path));
|
||||
foreach ($routes as $route) {
|
||||
$code .= sprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", ...array_map([__CLASS__, 'export'], $route));
|
||||
}
|
||||
$code .= " ],\n";
|
||||
}
|
||||
$code .= "],\n";
|
||||
$code = preg_replace('/ => \[\n (\[.+?),\n \],/', ' => [$1],', $code);
|
||||
|
||||
return $this->indent($code, 1).$checkConditionCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits static routes from dynamic routes, so that they can be matched first, using a simple switch.
|
||||
*/
|
||||
private function groupStaticRoutes(RouteCollection $collection): array
|
||||
{
|
||||
$staticRoutes = $dynamicRegex = [];
|
||||
$dynamicRoutes = new RouteCollection();
|
||||
|
||||
foreach ($collection->all() as $name => $route) {
|
||||
$compiledRoute = $route->compile();
|
||||
$staticPrefix = rtrim($compiledRoute->getStaticPrefix(), '/');
|
||||
$hostRegex = $compiledRoute->getHostRegex();
|
||||
$regex = $compiledRoute->getRegex();
|
||||
if ($hasTrailingSlash = '/' !== $route->getPath()) {
|
||||
$pos = strrpos($regex, '$');
|
||||
$hasTrailingSlash = '/' === $regex[$pos - 1];
|
||||
$regex = substr_replace($regex, '/?$', $pos - $hasTrailingSlash, 1 + $hasTrailingSlash);
|
||||
}
|
||||
|
||||
if (!$compiledRoute->getPathVariables()) {
|
||||
$host = !$compiledRoute->getHostVariables() ? $route->getHost() : '';
|
||||
$url = $route->getPath();
|
||||
if ($hasTrailingSlash) {
|
||||
$url = substr($url, 0, -1);
|
||||
}
|
||||
foreach ($dynamicRegex as [$hostRx, $rx, $prefix]) {
|
||||
if (('' === $prefix || str_starts_with($url, $prefix)) && (preg_match($rx, $url) || preg_match($rx, $url.'/')) && (!$host || !$hostRx || preg_match($hostRx, $host))) {
|
||||
$dynamicRegex[] = [$hostRegex, $regex, $staticPrefix];
|
||||
$dynamicRoutes->add($name, $route);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
$staticRoutes[$url][$name] = [$route, $hasTrailingSlash];
|
||||
} else {
|
||||
$dynamicRegex[] = [$hostRegex, $regex, $staticPrefix];
|
||||
$dynamicRoutes->add($name, $route);
|
||||
}
|
||||
}
|
||||
|
||||
return [$staticRoutes, $dynamicRoutes];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles static routes in a switch statement.
|
||||
*
|
||||
* Condition-less paths are put in a static array in the switch's default, with generic matching logic.
|
||||
* Paths that can match two or more routes, or have user-specified conditions are put in separate switch's cases.
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
private function compileStaticRoutes(array $staticRoutes, array &$conditions): array
|
||||
{
|
||||
if (!$staticRoutes) {
|
||||
return [];
|
||||
}
|
||||
$compiledRoutes = [];
|
||||
|
||||
foreach ($staticRoutes as $url => $routes) {
|
||||
$compiledRoutes[$url] = [];
|
||||
foreach ($routes as $name => [$route, $hasTrailingSlash]) {
|
||||
$compiledRoutes[$url][] = $this->compileRoute($route, $name, (!$route->compile()->getHostVariables() ? $route->getHost() : $route->compile()->getHostRegex()) ?: null, $hasTrailingSlash, false, $conditions);
|
||||
}
|
||||
}
|
||||
|
||||
return $compiledRoutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a regular expression followed by a switch statement to match dynamic routes.
|
||||
*
|
||||
* The regular expression matches both the host and the pathinfo at the same time. For stellar performance,
|
||||
* it is built as a tree of patterns, with re-ordering logic to group same-prefix routes together when possible.
|
||||
*
|
||||
* Patterns are named so that we know which one matched (https://pcre.org/current/doc/html/pcre2syntax.html#SEC23).
|
||||
* This name is used to "switch" to the additional logic required to match the final route.
|
||||
*
|
||||
* Condition-less paths are put in a static array in the switch's default, with generic matching logic.
|
||||
* Paths that can match two or more routes, or have user-specified conditions are put in separate switch's cases.
|
||||
*
|
||||
* Last but not least:
|
||||
* - Because it is not possible to mix unicode/non-unicode patterns in a single regexp, several of them can be generated.
|
||||
* - The same regexp can be used several times when the logic in the switch rejects the match. When this happens, the
|
||||
* matching-but-failing subpattern is excluded by replacing its name by "(*F)", which forces a failure-to-match.
|
||||
* To ease this backlisting operation, the name of subpatterns is also the string offset where the replacement should occur.
|
||||
*/
|
||||
private function compileDynamicRoutes(RouteCollection $collection, bool $matchHost, int $chunkLimit, array &$conditions): array
|
||||
{
|
||||
if (!$collection->all()) {
|
||||
return [[], [], ''];
|
||||
}
|
||||
$regexpList = [];
|
||||
$code = '';
|
||||
$state = (object) [
|
||||
'regexMark' => 0,
|
||||
'regex' => [],
|
||||
'routes' => [],
|
||||
'mark' => 0,
|
||||
'markTail' => 0,
|
||||
'hostVars' => [],
|
||||
'vars' => [],
|
||||
];
|
||||
$state->getVars = static function ($m) use ($state) {
|
||||
if ('_route' === $m[1]) {
|
||||
return '?:';
|
||||
}
|
||||
|
||||
$state->vars[] = $m[1];
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
$chunkSize = 0;
|
||||
$prev = null;
|
||||
$perModifiers = [];
|
||||
foreach ($collection->all() as $name => $route) {
|
||||
preg_match('#[a-zA-Z]*$#', $route->compile()->getRegex(), $rx);
|
||||
if ($chunkLimit < ++$chunkSize || $prev !== $rx[0] && $route->compile()->getPathVariables()) {
|
||||
$chunkSize = 1;
|
||||
$routes = new RouteCollection();
|
||||
$perModifiers[] = [$rx[0], $routes];
|
||||
$prev = $rx[0];
|
||||
}
|
||||
$routes->add($name, $route);
|
||||
}
|
||||
|
||||
foreach ($perModifiers as [$modifiers, $routes]) {
|
||||
$prev = false;
|
||||
$perHost = [];
|
||||
foreach ($routes->all() as $name => $route) {
|
||||
$regex = $route->compile()->getHostRegex();
|
||||
if ($prev !== $regex) {
|
||||
$routes = new RouteCollection();
|
||||
$perHost[] = [$regex, $routes];
|
||||
$prev = $regex;
|
||||
}
|
||||
$routes->add($name, $route);
|
||||
}
|
||||
$prev = false;
|
||||
$rx = '{^(?';
|
||||
$code .= "\n {$state->mark} => ".self::export($rx);
|
||||
$startingMark = $state->mark;
|
||||
$state->mark += \strlen($rx);
|
||||
$state->regex = $rx;
|
||||
|
||||
foreach ($perHost as [$hostRegex, $routes]) {
|
||||
if ($matchHost) {
|
||||
if ($hostRegex) {
|
||||
preg_match('#^.\^(.*)\$.[a-zA-Z]*$#', $hostRegex, $rx);
|
||||
$state->vars = [];
|
||||
$hostRegex = '(?i:'.preg_replace_callback('#\?P<([^>]++)>#', $state->getVars, $rx[1]).')\.';
|
||||
$state->hostVars = $state->vars;
|
||||
} else {
|
||||
$hostRegex = '(?:(?:[^./]*+\.)++)';
|
||||
$state->hostVars = [];
|
||||
}
|
||||
$state->mark += \strlen($rx = ($prev ? ')' : '')."|{$hostRegex}(?");
|
||||
$code .= "\n .".self::export($rx);
|
||||
$state->regex .= $rx;
|
||||
$prev = true;
|
||||
}
|
||||
|
||||
$tree = new StaticPrefixCollection();
|
||||
foreach ($routes->all() as $name => $route) {
|
||||
preg_match('#^.\^(.*)\$.[a-zA-Z]*$#', $route->compile()->getRegex(), $rx);
|
||||
|
||||
$state->vars = [];
|
||||
$regex = preg_replace_callback('#\?P<([^>]++)>#', $state->getVars, $rx[1]);
|
||||
if ($hasTrailingSlash = '/' !== $regex && '/' === $regex[-1]) {
|
||||
$regex = substr($regex, 0, -1);
|
||||
}
|
||||
$hasTrailingVar = (bool) preg_match('#\{\w+\}/?$#', $route->getPath());
|
||||
|
||||
$tree->addRoute($regex, [$name, $regex, $state->vars, $route, $hasTrailingSlash, $hasTrailingVar]);
|
||||
}
|
||||
|
||||
$code .= $this->compileStaticPrefixCollection($tree, $state, 0, $conditions);
|
||||
}
|
||||
if ($matchHost) {
|
||||
$code .= "\n .')'";
|
||||
$state->regex .= ')';
|
||||
}
|
||||
$rx = ")/?$}{$modifiers}";
|
||||
$code .= "\n .'{$rx}',";
|
||||
$state->regex .= $rx;
|
||||
$state->markTail = 0;
|
||||
|
||||
// if the regex is too large, throw a signaling exception to recompute with smaller chunk size
|
||||
set_error_handler(function ($type, $message) { throw str_contains($message, $this->signalingException->getMessage()) ? $this->signalingException : new \ErrorException($message); });
|
||||
try {
|
||||
preg_match($state->regex, '');
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
$regexpList[$startingMark] = $state->regex;
|
||||
}
|
||||
|
||||
$state->routes[$state->mark][] = [null, null, null, null, false, false, 0];
|
||||
unset($state->getVars);
|
||||
|
||||
return [$regexpList, $state->routes, $code];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a regexp tree of subpatterns that matches nested same-prefix routes.
|
||||
*
|
||||
* @param \stdClass $state A simple state object that keeps track of the progress of the compilation,
|
||||
* and gathers the generated switch's "case" and "default" statements
|
||||
*/
|
||||
private function compileStaticPrefixCollection(StaticPrefixCollection $tree, \stdClass $state, int $prefixLen, array &$conditions): string
|
||||
{
|
||||
$code = '';
|
||||
$prevRegex = null;
|
||||
$routes = $tree->getRoutes();
|
||||
|
||||
foreach ($routes as $i => $route) {
|
||||
if ($route instanceof StaticPrefixCollection) {
|
||||
$prevRegex = null;
|
||||
$prefix = substr($route->getPrefix(), $prefixLen);
|
||||
$state->mark += \strlen($rx = "|{$prefix}(?");
|
||||
$code .= "\n .".self::export($rx);
|
||||
$state->regex .= $rx;
|
||||
$code .= $this->indent($this->compileStaticPrefixCollection($route, $state, $prefixLen + \strlen($prefix), $conditions));
|
||||
$code .= "\n .')'";
|
||||
$state->regex .= ')';
|
||||
++$state->markTail;
|
||||
continue;
|
||||
}
|
||||
|
||||
[$name, $regex, $vars, $route, $hasTrailingSlash, $hasTrailingVar] = $route;
|
||||
$compiledRoute = $route->compile();
|
||||
$vars = array_merge($state->hostVars, $vars);
|
||||
|
||||
if ($compiledRoute->getRegex() === $prevRegex) {
|
||||
$state->routes[$state->mark][] = $this->compileRoute($route, $name, $vars, $hasTrailingSlash, $hasTrailingVar, $conditions);
|
||||
continue;
|
||||
}
|
||||
|
||||
$state->mark += 3 + $state->markTail + \strlen($regex) - $prefixLen;
|
||||
$state->markTail = 2 + \strlen($state->mark);
|
||||
$rx = sprintf('|%s(*:%s)', substr($regex, $prefixLen), $state->mark);
|
||||
$code .= "\n .".self::export($rx);
|
||||
$state->regex .= $rx;
|
||||
|
||||
$prevRegex = $compiledRoute->getRegex();
|
||||
$state->routes[$state->mark] = [$this->compileRoute($route, $name, $vars, $hasTrailingSlash, $hasTrailingVar, $conditions)];
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a single Route to PHP code used to match it against the path info.
|
||||
*/
|
||||
private function compileRoute(Route $route, string $name, string|array|null $vars, bool $hasTrailingSlash, bool $hasTrailingVar, array &$conditions): array
|
||||
{
|
||||
$defaults = $route->getDefaults();
|
||||
|
||||
if (isset($defaults['_canonical_route'])) {
|
||||
$name = $defaults['_canonical_route'];
|
||||
unset($defaults['_canonical_route']);
|
||||
}
|
||||
|
||||
if ($condition = $route->getCondition()) {
|
||||
$condition = $this->getExpressionLanguage()->compile($condition, ['context', 'request']);
|
||||
$condition = $conditions[$condition] ?? $conditions[$condition] = (str_contains($condition, '$request') ? 1 : -1) * \count($conditions);
|
||||
} else {
|
||||
$condition = null;
|
||||
}
|
||||
|
||||
return [
|
||||
['_route' => $name] + $defaults,
|
||||
$vars,
|
||||
array_flip($route->getMethods()) ?: null,
|
||||
array_flip($route->getSchemes()) ?: null,
|
||||
$hasTrailingSlash,
|
||||
$hasTrailingVar,
|
||||
$condition,
|
||||
];
|
||||
}
|
||||
|
||||
private function getExpressionLanguage(): ExpressionLanguage
|
||||
{
|
||||
if (!isset($this->expressionLanguage)) {
|
||||
if (!class_exists(ExpressionLanguage::class)) {
|
||||
throw new \LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
|
||||
}
|
||||
$this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
|
||||
}
|
||||
|
||||
return $this->expressionLanguage;
|
||||
}
|
||||
|
||||
private function indent(string $code, int $level = 1): string
|
||||
{
|
||||
return preg_replace('/^./m', str_repeat(' ', $level).'$0', $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function export(mixed $value): string
|
||||
{
|
||||
if (null === $value) {
|
||||
return 'null';
|
||||
}
|
||||
if (!\is_array($value)) {
|
||||
if (\is_object($value)) {
|
||||
throw new \InvalidArgumentException('Symfony\Component\Routing\Route cannot contain objects.');
|
||||
}
|
||||
|
||||
return str_replace("\n", '\'."\n".\'', var_export($value, true));
|
||||
}
|
||||
if (!$value) {
|
||||
return '[]';
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$export = '[';
|
||||
|
||||
foreach ($value as $k => $v) {
|
||||
if ($i === $k) {
|
||||
++$i;
|
||||
} else {
|
||||
$export .= self::export($k).' => ';
|
||||
|
||||
if (\is_int($k) && $i < $k) {
|
||||
$i = 1 + $k;
|
||||
}
|
||||
}
|
||||
|
||||
$export .= self::export($v).', ';
|
||||
}
|
||||
|
||||
return substr_replace($export, ']', -2);
|
||||
}
|
||||
}
|
||||
191
vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php
vendored
Normal file
191
vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher\Dumper;
|
||||
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\NoConfigurationException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @property RequestContext $context
|
||||
*/
|
||||
trait CompiledUrlMatcherTrait
|
||||
{
|
||||
private bool $matchHost = false;
|
||||
private array $staticRoutes = [];
|
||||
private array $regexpList = [];
|
||||
private array $dynamicRoutes = [];
|
||||
|
||||
/**
|
||||
* @var callable|null
|
||||
*/
|
||||
private $checkCondition;
|
||||
|
||||
public function match(string $pathinfo): array
|
||||
{
|
||||
$allow = $allowSchemes = [];
|
||||
if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) {
|
||||
return $ret;
|
||||
}
|
||||
if ($allow) {
|
||||
throw new MethodNotAllowedException(array_keys($allow));
|
||||
}
|
||||
if (!$this instanceof RedirectableUrlMatcherInterface) {
|
||||
throw new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo));
|
||||
}
|
||||
if (!\in_array($this->context->getMethod(), ['HEAD', 'GET'], true)) {
|
||||
// no-op
|
||||
} elseif ($allowSchemes) {
|
||||
redirect_scheme:
|
||||
$scheme = $this->context->getScheme();
|
||||
$this->context->setScheme(key($allowSchemes));
|
||||
try {
|
||||
if ($ret = $this->doMatch($pathinfo)) {
|
||||
return $this->redirect($pathinfo, $ret['_route'], $this->context->getScheme()) + $ret;
|
||||
}
|
||||
} finally {
|
||||
$this->context->setScheme($scheme);
|
||||
}
|
||||
} elseif ('/' !== $trimmedPathinfo = rtrim($pathinfo, '/') ?: '/') {
|
||||
$pathinfo = $trimmedPathinfo === $pathinfo ? $pathinfo.'/' : $trimmedPathinfo;
|
||||
if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) {
|
||||
return $this->redirect($pathinfo, $ret['_route']) + $ret;
|
||||
}
|
||||
if ($allowSchemes) {
|
||||
goto redirect_scheme;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo));
|
||||
}
|
||||
|
||||
private function doMatch(string $pathinfo, array &$allow = [], array &$allowSchemes = []): array
|
||||
{
|
||||
$allow = $allowSchemes = [];
|
||||
$pathinfo = rawurldecode($pathinfo) ?: '/';
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/') ?: '/';
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
|
||||
if ($this->matchHost) {
|
||||
$host = strtolower($context->getHost());
|
||||
}
|
||||
|
||||
if ('HEAD' === $requestMethod) {
|
||||
$canonicalMethod = 'GET';
|
||||
}
|
||||
$supportsRedirections = 'GET' === $canonicalMethod && $this instanceof RedirectableUrlMatcherInterface;
|
||||
|
||||
foreach ($this->staticRoutes[$trimmedPathinfo] ?? [] as [$ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash, , $condition]) {
|
||||
if ($condition && !($this->checkCondition)($condition, $context, 0 < $condition ? $request ?? $request = $this->request ?: $this->createRequest($pathinfo) : null)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($requiredHost) {
|
||||
if ('{' !== $requiredHost[0] ? $requiredHost !== $host : !preg_match($requiredHost, $host, $hostMatches)) {
|
||||
continue;
|
||||
}
|
||||
if ('{' === $requiredHost[0] && $hostMatches) {
|
||||
$hostMatches['_route'] = $ret['_route'];
|
||||
$ret = $this->mergeDefaults($hostMatches, $ret);
|
||||
}
|
||||
}
|
||||
|
||||
if ('/' !== $pathinfo && $hasTrailingSlash === ($trimmedPathinfo === $pathinfo)) {
|
||||
if ($supportsRedirections && (!$requiredMethods || isset($requiredMethods['GET']))) {
|
||||
return $allow = $allowSchemes = [];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]);
|
||||
if ($hasRequiredScheme && $requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
|
||||
$allow += $requiredMethods;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$hasRequiredScheme) {
|
||||
$allowSchemes += $requiredSchemes;
|
||||
continue;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$matchedPathinfo = $this->matchHost ? $host.'.'.$pathinfo : $pathinfo;
|
||||
|
||||
foreach ($this->regexpList as $offset => $regex) {
|
||||
while (preg_match($regex, $matchedPathinfo, $matches)) {
|
||||
foreach ($this->dynamicRoutes[$m = (int) $matches['MARK']] as [$ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash, $hasTrailingVar, $condition]) {
|
||||
if (null !== $condition) {
|
||||
if (0 === $condition) { // marks the last route in the regexp
|
||||
continue 3;
|
||||
}
|
||||
if (!($this->checkCondition)($condition, $context, 0 < $condition ? $request ?? $request = $this->request ?: $this->createRequest($pathinfo) : null)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$hasTrailingVar = $trimmedPathinfo !== $pathinfo && $hasTrailingVar;
|
||||
|
||||
if ($hasTrailingVar && ($hasTrailingSlash || (null === $n = $matches[\count($vars)] ?? null) || '/' !== ($n[-1] ?? '/')) && preg_match($regex, $this->matchHost ? $host.'.'.$trimmedPathinfo : $trimmedPathinfo, $n) && $m === (int) $n['MARK']) {
|
||||
if ($hasTrailingSlash) {
|
||||
$matches = $n;
|
||||
} else {
|
||||
$hasTrailingVar = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ('/' !== $pathinfo && !$hasTrailingVar && $hasTrailingSlash === ($trimmedPathinfo === $pathinfo)) {
|
||||
if ($supportsRedirections && (!$requiredMethods || isset($requiredMethods['GET']))) {
|
||||
return $allow = $allowSchemes = [];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($vars as $i => $v) {
|
||||
if (isset($matches[1 + $i])) {
|
||||
$ret[$v] = $matches[1 + $i];
|
||||
}
|
||||
}
|
||||
|
||||
if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) {
|
||||
$allowSchemes += $requiredSchemes;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
|
||||
$allow += $requiredMethods;
|
||||
continue;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$regex = substr_replace($regex, 'F', $m - $offset, 1 + \strlen($m));
|
||||
$offset += \strlen($m);
|
||||
}
|
||||
}
|
||||
|
||||
if ('/' === $pathinfo && !$allow && !$allowSchemes) {
|
||||
throw new NoConfigurationException();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher\Dumper;
|
||||
|
||||
/**
|
||||
* Collection of routes.
|
||||
*
|
||||
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class DumperCollection implements \IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* @var DumperCollection|null
|
||||
*/
|
||||
private $parent;
|
||||
|
||||
/**
|
||||
* @var DumperCollection[]|DumperRoute[]
|
||||
*/
|
||||
private $children = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $attributes = array();
|
||||
|
||||
/**
|
||||
* Returns the children routes and collections.
|
||||
*
|
||||
* @return self[]|DumperRoute[]
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a route or collection.
|
||||
*
|
||||
* @param DumperRoute|DumperCollection The route or collection
|
||||
*/
|
||||
public function add($child)
|
||||
{
|
||||
if ($child instanceof self) {
|
||||
$child->setParent($this);
|
||||
}
|
||||
$this->children[] = $child;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets children.
|
||||
*
|
||||
* @param array $children The children
|
||||
*/
|
||||
public function setAll(array $children)
|
||||
{
|
||||
foreach ($children as $child) {
|
||||
if ($child instanceof self) {
|
||||
$child->setParent($this);
|
||||
}
|
||||
}
|
||||
$this->children = $children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the children.
|
||||
*
|
||||
* @return \Iterator|DumperCollection[]|DumperRoute[] The iterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->children);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root of the collection.
|
||||
*
|
||||
* @return self The root collection
|
||||
*/
|
||||
public function getRoot()
|
||||
{
|
||||
return (null !== $this->parent) ? $this->parent->getRoot() : $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent collection.
|
||||
*
|
||||
* @return self|null The parent collection or null if the collection has no parent
|
||||
*/
|
||||
protected function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent collection.
|
||||
*
|
||||
* @param DumperCollection $parent The parent collection
|
||||
*/
|
||||
protected function setParent(DumperCollection $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attribute is defined.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
*
|
||||
* @return bool true if the attribute is defined, false otherwise
|
||||
*/
|
||||
public function hasAttribute($name)
|
||||
{
|
||||
return array_key_exists($name, $this->attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an attribute by name.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
* @param mixed $default Default value is the attribute doesn't exist
|
||||
*
|
||||
* @return mixed The attribute value
|
||||
*/
|
||||
public function getAttribute($name, $default = null)
|
||||
{
|
||||
return $this->hasAttribute($name) ? $this->attributes[$name] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute by name.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
* @param mixed $value The attribute value
|
||||
*/
|
||||
public function setAttribute($name, $value)
|
||||
{
|
||||
$this->attributes[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets multiple attributes.
|
||||
*
|
||||
* @param array $attributes The attributes
|
||||
*/
|
||||
public function setAttributes($attributes)
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher\Dumper;
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Container for a Route.
|
||||
*
|
||||
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class DumperRoute
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var Route
|
||||
*/
|
||||
private $route;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name The route name
|
||||
* @param Route $route The route
|
||||
*/
|
||||
public function __construct($name, Route $route)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->route = $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the route name.
|
||||
*
|
||||
* @return string The route name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the route.
|
||||
*
|
||||
* @return Route The route
|
||||
*/
|
||||
public function getRoute()
|
||||
{
|
||||
return $this->route;
|
||||
}
|
||||
}
|
||||
@@ -20,16 +20,8 @@ use Symfony\Component\Routing\RouteCollection;
|
||||
*/
|
||||
abstract class MatcherDumper implements MatcherDumperInterface
|
||||
{
|
||||
/**
|
||||
* @var RouteCollection
|
||||
*/
|
||||
private $routes;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param RouteCollection $routes The RouteCollection to dump
|
||||
*/
|
||||
public function __construct(RouteCollection $routes)
|
||||
{
|
||||
$this->routes = $routes;
|
||||
@@ -38,7 +30,7 @@ abstract class MatcherDumper implements MatcherDumperInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRoutes()
|
||||
public function getRoutes(): RouteCollection
|
||||
{
|
||||
return $this->routes;
|
||||
}
|
||||
|
||||
@@ -23,17 +23,11 @@ interface MatcherDumperInterface
|
||||
/**
|
||||
* Dumps a set of routes to a string representation of executable code
|
||||
* that can then be used to match a request against these routes.
|
||||
*
|
||||
* @param array $options An array of options
|
||||
*
|
||||
* @return string Executable code
|
||||
*/
|
||||
public function dump(array $options = array());
|
||||
public function dump(array $options = []): string;
|
||||
|
||||
/**
|
||||
* Gets the routes to dump.
|
||||
*
|
||||
* @return RouteCollection A RouteCollection instance
|
||||
*/
|
||||
public function getRoutes();
|
||||
public function getRoutes(): RouteCollection;
|
||||
}
|
||||
|
||||
@@ -1,431 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher\Dumper;
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||
|
||||
/**
|
||||
* PhpMatcherDumper creates a PHP class able to match URLs for a given set of routes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
|
||||
*/
|
||||
class PhpMatcherDumper extends MatcherDumper
|
||||
{
|
||||
private $expressionLanguage;
|
||||
|
||||
/**
|
||||
* @var ExpressionFunctionProviderInterface[]
|
||||
*/
|
||||
private $expressionLanguageProviders = array();
|
||||
|
||||
/**
|
||||
* Dumps a set of routes to a PHP class.
|
||||
*
|
||||
* Available options:
|
||||
*
|
||||
* * class: The class name
|
||||
* * base_class: The base class name
|
||||
*
|
||||
* @param array $options An array of options
|
||||
*
|
||||
* @return string A PHP class representing the matcher class
|
||||
*/
|
||||
public function dump(array $options = array())
|
||||
{
|
||||
$options = array_replace(array(
|
||||
'class' => 'ProjectUrlMatcher',
|
||||
'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
|
||||
), $options);
|
||||
|
||||
// trailing slash support is only enabled if we know how to redirect the user
|
||||
$interfaces = class_implements($options['base_class']);
|
||||
$supportsRedirections = isset($interfaces['Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcherInterface']);
|
||||
|
||||
return <<<EOF
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* {$options['class']}.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
class {$options['class']} extends {$options['base_class']}
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(RequestContext \$context)
|
||||
{
|
||||
\$this->context = \$context;
|
||||
}
|
||||
|
||||
{$this->generateMatchMethod($supportsRedirections)}
|
||||
}
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
|
||||
{
|
||||
$this->expressionLanguageProviders[] = $provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the code for the match method implementing UrlMatcherInterface.
|
||||
*
|
||||
* @param bool $supportsRedirections Whether redirections are supported by the base class
|
||||
*
|
||||
* @return string Match method as PHP code
|
||||
*/
|
||||
private function generateMatchMethod($supportsRedirections)
|
||||
{
|
||||
$code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n");
|
||||
|
||||
return <<<EOF
|
||||
public function match(\$pathinfo)
|
||||
{
|
||||
\$allow = array();
|
||||
\$pathinfo = rawurldecode(\$pathinfo);
|
||||
\$trimmedPathinfo = rtrim(\$pathinfo, '/');
|
||||
\$context = \$this->context;
|
||||
\$request = \$this->request;
|
||||
\$requestMethod = \$canonicalMethod = \$context->getMethod();
|
||||
\$scheme = \$context->getScheme();
|
||||
|
||||
if ('HEAD' === \$requestMethod) {
|
||||
\$canonicalMethod = 'GET';
|
||||
}
|
||||
|
||||
|
||||
$code
|
||||
|
||||
throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates PHP code to match a RouteCollection with all its routes.
|
||||
*
|
||||
* @param RouteCollection $routes A RouteCollection instance
|
||||
* @param bool $supportsRedirections Whether redirections are supported by the base class
|
||||
*
|
||||
* @return string PHP code
|
||||
*/
|
||||
private function compileRoutes(RouteCollection $routes, $supportsRedirections)
|
||||
{
|
||||
$fetchedHost = false;
|
||||
$groups = $this->groupRoutesByHostRegex($routes);
|
||||
$code = '';
|
||||
|
||||
foreach ($groups as $collection) {
|
||||
if (null !== $regex = $collection->getAttribute('host_regex')) {
|
||||
if (!$fetchedHost) {
|
||||
$code .= " \$host = \$context->getHost();\n\n";
|
||||
$fetchedHost = true;
|
||||
}
|
||||
|
||||
$code .= sprintf(" if (preg_match(%s, \$host, \$hostMatches)) {\n", var_export($regex, true));
|
||||
}
|
||||
|
||||
$tree = $this->buildStaticPrefixCollection($collection);
|
||||
$groupCode = $this->compileStaticPrefixRoutes($tree, $supportsRedirections);
|
||||
|
||||
if (null !== $regex) {
|
||||
// apply extra indention at each line (except empty ones)
|
||||
$groupCode = preg_replace('/^.{2,}$/m', ' $0', $groupCode);
|
||||
$code .= $groupCode;
|
||||
$code .= " }\n\n";
|
||||
} else {
|
||||
$code .= $groupCode;
|
||||
}
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
private function buildStaticPrefixCollection(DumperCollection $collection)
|
||||
{
|
||||
$prefixCollection = new StaticPrefixCollection();
|
||||
|
||||
foreach ($collection as $dumperRoute) {
|
||||
$prefix = $dumperRoute->getRoute()->compile()->getStaticPrefix();
|
||||
$prefixCollection->addRoute($prefix, $dumperRoute);
|
||||
}
|
||||
|
||||
$prefixCollection->optimizeGroups();
|
||||
|
||||
return $prefixCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates PHP code to match a tree of routes.
|
||||
*
|
||||
* @param StaticPrefixCollection $collection A StaticPrefixCollection instance
|
||||
* @param bool $supportsRedirections Whether redirections are supported by the base class
|
||||
* @param string $ifOrElseIf Either "if" or "elseif" to influence chaining.
|
||||
*
|
||||
* @return string PHP code
|
||||
*/
|
||||
private function compileStaticPrefixRoutes(StaticPrefixCollection $collection, $supportsRedirections, $ifOrElseIf = 'if')
|
||||
{
|
||||
$code = '';
|
||||
$prefix = $collection->getPrefix();
|
||||
|
||||
if (!empty($prefix) && '/' !== $prefix) {
|
||||
$code .= sprintf(" %s (0 === strpos(\$pathinfo, %s)) {\n", $ifOrElseIf, var_export($prefix, true));
|
||||
}
|
||||
|
||||
$ifOrElseIf = 'if';
|
||||
|
||||
foreach ($collection->getItems() as $route) {
|
||||
if ($route instanceof StaticPrefixCollection) {
|
||||
$code .= $this->compileStaticPrefixRoutes($route, $supportsRedirections, $ifOrElseIf);
|
||||
$ifOrElseIf = 'elseif';
|
||||
} else {
|
||||
$code .= $this->compileRoute($route[1]->getRoute(), $route[1]->getName(), $supportsRedirections, $prefix)."\n";
|
||||
$ifOrElseIf = 'if';
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($prefix) && '/' !== $prefix) {
|
||||
$code .= " }\n\n";
|
||||
// apply extra indention at each line (except empty ones)
|
||||
$code = preg_replace('/^.{2,}$/m', ' $0', $code);
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a single Route to PHP code used to match it against the path info.
|
||||
*
|
||||
* @param Route $route A Route instance
|
||||
* @param string $name The name of the Route
|
||||
* @param bool $supportsRedirections Whether redirections are supported by the base class
|
||||
* @param string|null $parentPrefix The prefix of the parent collection used to optimize the code
|
||||
*
|
||||
* @return string PHP code
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
private function compileRoute(Route $route, $name, $supportsRedirections, $parentPrefix = null)
|
||||
{
|
||||
$code = '';
|
||||
$compiledRoute = $route->compile();
|
||||
$conditions = array();
|
||||
$hasTrailingSlash = false;
|
||||
$matches = false;
|
||||
$hostMatches = false;
|
||||
$methods = $route->getMethods();
|
||||
|
||||
$supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods) || in_array('GET', $methods));
|
||||
$regex = $compiledRoute->getRegex();
|
||||
|
||||
if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#'.(substr($regex, -1) === 'u' ? 'u' : ''), $regex, $m)) {
|
||||
if ($supportsTrailingSlash && substr($m['url'], -1) === '/') {
|
||||
$conditions[] = sprintf('%s === $trimmedPathinfo', var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true));
|
||||
$hasTrailingSlash = true;
|
||||
} else {
|
||||
$conditions[] = sprintf('%s === $pathinfo', var_export(str_replace('\\', '', $m['url']), true));
|
||||
}
|
||||
} else {
|
||||
if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) {
|
||||
$conditions[] = sprintf('0 === strpos($pathinfo, %s)', var_export($compiledRoute->getStaticPrefix(), true));
|
||||
}
|
||||
|
||||
if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) {
|
||||
$regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
|
||||
$hasTrailingSlash = true;
|
||||
}
|
||||
$conditions[] = sprintf('preg_match(%s, $pathinfo, $matches)', var_export($regex, true));
|
||||
|
||||
$matches = true;
|
||||
}
|
||||
|
||||
if ($compiledRoute->getHostVariables()) {
|
||||
$hostMatches = true;
|
||||
}
|
||||
|
||||
if ($route->getCondition()) {
|
||||
$conditions[] = $this->getExpressionLanguage()->compile($route->getCondition(), array('context', 'request'));
|
||||
}
|
||||
|
||||
$conditions = implode(' && ', $conditions);
|
||||
|
||||
$code .= <<<EOF
|
||||
// $name
|
||||
if ($conditions) {
|
||||
|
||||
EOF;
|
||||
|
||||
$gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name);
|
||||
|
||||
if ($methods) {
|
||||
if (1 === count($methods)) {
|
||||
if ($methods[0] === 'HEAD') {
|
||||
$code .= <<<EOF
|
||||
if ('HEAD' !== \$requestMethod) {
|
||||
\$allow[] = 'HEAD';
|
||||
goto $gotoname;
|
||||
}
|
||||
|
||||
|
||||
EOF;
|
||||
} else {
|
||||
$code .= <<<EOF
|
||||
if ('$methods[0]' !== \$canonicalMethod) {
|
||||
\$allow[] = '$methods[0]';
|
||||
goto $gotoname;
|
||||
}
|
||||
|
||||
|
||||
EOF;
|
||||
}
|
||||
} else {
|
||||
$methodVariable = 'requestMethod';
|
||||
|
||||
if (in_array('GET', $methods)) {
|
||||
// Since we treat HEAD requests like GET requests we don't need to match it.
|
||||
$methodVariable = 'canonicalMethod';
|
||||
$methods = array_filter($methods, function ($method) { return 'HEAD' !== $method; });
|
||||
}
|
||||
|
||||
if (1 === count($methods)) {
|
||||
$code .= <<<EOF
|
||||
if ('$methods[0]' !== \$$methodVariable) {
|
||||
\$allow[] = '$methods[0]';
|
||||
goto $gotoname;
|
||||
}
|
||||
|
||||
|
||||
EOF;
|
||||
} else {
|
||||
$methods = implode("', '", $methods);
|
||||
$code .= <<<EOF
|
||||
if (!in_array(\$$methodVariable, array('$methods'))) {
|
||||
\$allow = array_merge(\$allow, array('$methods'));
|
||||
goto $gotoname;
|
||||
}
|
||||
|
||||
|
||||
EOF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasTrailingSlash) {
|
||||
$code .= <<<EOF
|
||||
if (substr(\$pathinfo, -1) !== '/') {
|
||||
return \$this->redirect(\$pathinfo.'/', '$name');
|
||||
}
|
||||
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
if ($schemes = $route->getSchemes()) {
|
||||
if (!$supportsRedirections) {
|
||||
throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
|
||||
}
|
||||
$schemes = str_replace("\n", '', var_export(array_flip($schemes), true));
|
||||
$code .= <<<EOF
|
||||
\$requiredSchemes = $schemes;
|
||||
if (!isset(\$requiredSchemes[\$scheme])) {
|
||||
return \$this->redirect(\$pathinfo, '$name', key(\$requiredSchemes));
|
||||
}
|
||||
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
// optimize parameters array
|
||||
if ($matches || $hostMatches) {
|
||||
$vars = array();
|
||||
if ($hostMatches) {
|
||||
$vars[] = '$hostMatches';
|
||||
}
|
||||
if ($matches) {
|
||||
$vars[] = '$matches';
|
||||
}
|
||||
$vars[] = "array('_route' => '$name')";
|
||||
|
||||
$code .= sprintf(
|
||||
" return \$this->mergeDefaults(array_replace(%s), %s);\n",
|
||||
implode(', ', $vars),
|
||||
str_replace("\n", '', var_export($route->getDefaults(), true))
|
||||
);
|
||||
} elseif ($route->getDefaults()) {
|
||||
$code .= sprintf(" return %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true)));
|
||||
} else {
|
||||
$code .= sprintf(" return array('_route' => '%s');\n", $name);
|
||||
}
|
||||
$code .= " }\n";
|
||||
|
||||
if ($methods) {
|
||||
$code .= " $gotoname:\n";
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups consecutive routes having the same host regex.
|
||||
*
|
||||
* The result is a collection of collections of routes having the same host regex.
|
||||
*
|
||||
* @param RouteCollection $routes A flat RouteCollection
|
||||
*
|
||||
* @return DumperCollection A collection with routes grouped by host regex in sub-collections
|
||||
*/
|
||||
private function groupRoutesByHostRegex(RouteCollection $routes)
|
||||
{
|
||||
$groups = new DumperCollection();
|
||||
$currentGroup = new DumperCollection();
|
||||
$currentGroup->setAttribute('host_regex', null);
|
||||
$groups->add($currentGroup);
|
||||
|
||||
foreach ($routes as $name => $route) {
|
||||
$hostRegex = $route->compile()->getHostRegex();
|
||||
if ($currentGroup->getAttribute('host_regex') !== $hostRegex) {
|
||||
$currentGroup = new DumperCollection();
|
||||
$currentGroup->setAttribute('host_regex', $hostRegex);
|
||||
$groups->add($currentGroup);
|
||||
}
|
||||
$currentGroup->add(new DumperRoute($name, $route));
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
private function getExpressionLanguage()
|
||||
{
|
||||
if (null === $this->expressionLanguage) {
|
||||
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||
throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
|
||||
}
|
||||
$this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
|
||||
}
|
||||
|
||||
return $this->expressionLanguage;
|
||||
}
|
||||
}
|
||||
@@ -11,228 +11,193 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher\Dumper;
|
||||
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Prefix tree of routes preserving routes order.
|
||||
*
|
||||
* @author Frank de Jonge <info@frankdejonge.nl>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class StaticPrefixCollection
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $prefix;
|
||||
private string $prefix;
|
||||
|
||||
/**
|
||||
* @var array[]|StaticPrefixCollection[]
|
||||
* @var string[]
|
||||
*/
|
||||
private $items = array();
|
||||
private array $staticPrefixes = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @var string[]
|
||||
*/
|
||||
private $matchStart = 0;
|
||||
private array $prefixes = [];
|
||||
|
||||
public function __construct($prefix = '')
|
||||
/**
|
||||
* @var array[]|self[]
|
||||
*/
|
||||
private array $items = [];
|
||||
|
||||
public function __construct(string $prefix = '/')
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
public function getPrefix()
|
||||
public function getPrefix(): string
|
||||
{
|
||||
return $this->prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]|StaticPrefixCollection[]
|
||||
* @return array[]|self[]
|
||||
*/
|
||||
public function getItems()
|
||||
public function getRoutes(): array
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a route to a group.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param mixed $route
|
||||
*/
|
||||
public function addRoute($prefix, $route)
|
||||
public function addRoute(string $prefix, array|StaticPrefixCollection $route)
|
||||
{
|
||||
$prefix = '/' === $prefix ? $prefix : rtrim($prefix, '/');
|
||||
$this->guardAgainstAddingNotAcceptedRoutes($prefix);
|
||||
[$prefix, $staticPrefix] = $this->getCommonPrefix($prefix, $prefix);
|
||||
|
||||
if ($this->prefix === $prefix) {
|
||||
// When a prefix is exactly the same as the base we move up the match start position.
|
||||
// This is needed because otherwise routes that come afterwards have higher precedence
|
||||
// than a possible regular expression, which goes against the input order sorting.
|
||||
$this->items[] = array($prefix, $route);
|
||||
$this->matchStart = count($this->items);
|
||||
for ($i = \count($this->items) - 1; 0 <= $i; --$i) {
|
||||
$item = $this->items[$i];
|
||||
|
||||
return;
|
||||
}
|
||||
[$commonPrefix, $commonStaticPrefix] = $this->getCommonPrefix($prefix, $this->prefixes[$i]);
|
||||
|
||||
if ($this->prefix === $commonPrefix) {
|
||||
// the new route and a previous one have no common prefix, let's see if they are exclusive to each others
|
||||
|
||||
if ($this->prefix !== $staticPrefix && $this->prefix !== $this->staticPrefixes[$i]) {
|
||||
// the new route and the previous one have exclusive static prefixes
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->prefix === $staticPrefix && $this->prefix === $this->staticPrefixes[$i]) {
|
||||
// the new route and the previous one have no static prefix
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->prefixes[$i] !== $this->staticPrefixes[$i] && $this->prefix === $this->staticPrefixes[$i]) {
|
||||
// the previous route is non-static and has no static prefix
|
||||
break;
|
||||
}
|
||||
|
||||
if ($prefix !== $staticPrefix && $this->prefix === $staticPrefix) {
|
||||
// the new route is non-static and has no static prefix
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($this->items as $i => $item) {
|
||||
if ($i < $this->matchStart) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($item instanceof self && $item->accepts($prefix)) {
|
||||
if ($item instanceof self && $this->prefixes[$i] === $commonPrefix) {
|
||||
// the new route is a child of a previous one, let's nest it
|
||||
$item->addRoute($prefix, $route);
|
||||
} else {
|
||||
// the new route and a previous one have a common prefix, let's merge them
|
||||
$child = new self($commonPrefix);
|
||||
[$child->prefixes[0], $child->staticPrefixes[0]] = $child->getCommonPrefix($this->prefixes[$i], $this->prefixes[$i]);
|
||||
[$child->prefixes[1], $child->staticPrefixes[1]] = $child->getCommonPrefix($prefix, $prefix);
|
||||
$child->items = [$this->items[$i], $route];
|
||||
|
||||
return;
|
||||
$this->staticPrefixes[$i] = $commonStaticPrefix;
|
||||
$this->prefixes[$i] = $commonPrefix;
|
||||
$this->items[$i] = $child;
|
||||
}
|
||||
|
||||
$group = $this->groupWithItem($item, $prefix, $route);
|
||||
|
||||
if ($group instanceof self) {
|
||||
$this->items[$i] = $group;
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// No optimised case was found, in this case we simple add the route for possible
|
||||
// grouping when new routes are added.
|
||||
$this->items[] = array($prefix, $route);
|
||||
$this->staticPrefixes[] = $staticPrefix;
|
||||
$this->prefixes[] = $prefix;
|
||||
$this->items[] = $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to combine a route with another route or group.
|
||||
*
|
||||
* @param StaticPrefixCollection|array $item
|
||||
* @param string $prefix
|
||||
* @param mixed $route
|
||||
*
|
||||
* @return null|StaticPrefixCollection
|
||||
* Linearizes back a set of nested routes into a collection.
|
||||
*/
|
||||
private function groupWithItem($item, $prefix, $route)
|
||||
public function populateCollection(RouteCollection $routes): RouteCollection
|
||||
{
|
||||
$itemPrefix = $item instanceof self ? $item->prefix : $item[0];
|
||||
$commonPrefix = $this->detectCommonPrefix($prefix, $itemPrefix);
|
||||
|
||||
if (!$commonPrefix) {
|
||||
return;
|
||||
}
|
||||
|
||||
$child = new self($commonPrefix);
|
||||
|
||||
if ($item instanceof self) {
|
||||
$child->items = array($item);
|
||||
} else {
|
||||
$child->addRoute($item[0], $item[1]);
|
||||
}
|
||||
|
||||
$child->addRoute($prefix, $route);
|
||||
|
||||
return $child;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a prefix can be contained within the group.
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return bool Whether a prefix could belong in a given group
|
||||
*/
|
||||
private function accepts($prefix)
|
||||
{
|
||||
return '' === $this->prefix || strpos($prefix, $this->prefix) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects whether there's a common prefix relative to the group prefix and returns it.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param string $anotherPrefix
|
||||
*
|
||||
* @return false|string A common prefix, longer than the base/group prefix, or false when none available
|
||||
*/
|
||||
private function detectCommonPrefix($prefix, $anotherPrefix)
|
||||
{
|
||||
$baseLength = strlen($this->prefix);
|
||||
$commonLength = $baseLength;
|
||||
$end = min(strlen($prefix), strlen($anotherPrefix));
|
||||
|
||||
for ($i = $baseLength; $i <= $end; ++$i) {
|
||||
if (substr($prefix, 0, $i) !== substr($anotherPrefix, 0, $i)) {
|
||||
break;
|
||||
foreach ($this->items as $route) {
|
||||
if ($route instanceof self) {
|
||||
$route->populateCollection($routes);
|
||||
} else {
|
||||
$routes->add(...$route);
|
||||
}
|
||||
|
||||
$commonLength = $i;
|
||||
}
|
||||
|
||||
$commonPrefix = rtrim(substr($prefix, 0, $commonLength), '/');
|
||||
|
||||
if (strlen($commonPrefix) > $baseLength) {
|
||||
return $commonPrefix;
|
||||
}
|
||||
|
||||
return false;
|
||||
return $routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimizes the tree by inlining items from groups with less than 3 items.
|
||||
* Gets the full and static common prefixes between two route patterns.
|
||||
*
|
||||
* The static prefix stops at last at the first opening bracket.
|
||||
*/
|
||||
public function optimizeGroups()
|
||||
private function getCommonPrefix(string $prefix, string $anotherPrefix): array
|
||||
{
|
||||
$index = -1;
|
||||
$baseLength = \strlen($this->prefix);
|
||||
$end = min(\strlen($prefix), \strlen($anotherPrefix));
|
||||
$staticLength = null;
|
||||
set_error_handler([__CLASS__, 'handleError']);
|
||||
|
||||
while (isset($this->items[++$index])) {
|
||||
$item = $this->items[$index];
|
||||
|
||||
if ($item instanceof self) {
|
||||
$item->optimizeGroups();
|
||||
|
||||
// When a group contains only two items there's no reason to optimize because at minimum
|
||||
// the amount of prefix check is 2. In this case inline the group.
|
||||
if ($item->shouldBeInlined()) {
|
||||
array_splice($this->items, $index, 1, $item->items);
|
||||
|
||||
// Lower index to pass through the same index again after optimizing.
|
||||
// The first item of the replacements might be a group needing optimization.
|
||||
--$index;
|
||||
try {
|
||||
for ($i = $baseLength; $i < $end && $prefix[$i] === $anotherPrefix[$i]; ++$i) {
|
||||
if ('(' === $prefix[$i]) {
|
||||
$staticLength = $staticLength ?? $i;
|
||||
for ($j = 1 + $i, $n = 1; $j < $end && 0 < $n; ++$j) {
|
||||
if ($prefix[$j] !== $anotherPrefix[$j]) {
|
||||
break 2;
|
||||
}
|
||||
if ('(' === $prefix[$j]) {
|
||||
++$n;
|
||||
} elseif (')' === $prefix[$j]) {
|
||||
--$n;
|
||||
} elseif ('\\' === $prefix[$j] && (++$j === $end || $prefix[$j] !== $anotherPrefix[$j])) {
|
||||
--$j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (0 < $n) {
|
||||
break;
|
||||
}
|
||||
if (('?' === ($prefix[$j] ?? '') || '?' === ($anotherPrefix[$j] ?? '')) && ($prefix[$j] ?? '') !== ($anotherPrefix[$j] ?? '')) {
|
||||
break;
|
||||
}
|
||||
$subPattern = substr($prefix, $i, $j - $i);
|
||||
if ($prefix !== $anotherPrefix && !preg_match('/^\(\[[^\]]++\]\+\+\)$/', $subPattern) && !preg_match('{(?<!'.$subPattern.')}', '')) {
|
||||
// sub-patterns of variable length are not considered as common prefixes because their greediness would break in-order matching
|
||||
break;
|
||||
}
|
||||
$i = $j - 1;
|
||||
} elseif ('\\' === $prefix[$i] && (++$i === $end || $prefix[$i] !== $anotherPrefix[$i])) {
|
||||
--$i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
if ($i < $end && 0b10 === (\ord($prefix[$i]) >> 6) && preg_match('//u', $prefix.' '.$anotherPrefix)) {
|
||||
do {
|
||||
// Prevent cutting in the middle of an UTF-8 characters
|
||||
--$i;
|
||||
} while (0b10 === (\ord($prefix[$i]) >> 6));
|
||||
}
|
||||
|
||||
return [substr($prefix, 0, $i), substr($prefix, 0, $staticLength ?? $i)];
|
||||
}
|
||||
|
||||
private function shouldBeInlined()
|
||||
public static function handleError(int $type, string $msg)
|
||||
{
|
||||
if (count($this->items) >= 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->items as $item) {
|
||||
if ($item instanceof self) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->items as $item) {
|
||||
if (is_array($item) && $item[0] === $this->prefix) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guards against adding incompatible prefixes in a group.
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @throws \LogicException When a prefix does not belong in a group.
|
||||
*/
|
||||
private function guardAgainstAddingNotAcceptedRoutes($prefix)
|
||||
{
|
||||
if (!$this->accepts($prefix)) {
|
||||
$message = sprintf('Could not add route with prefix %s to collection with prefix %s', $prefix, $this->prefix);
|
||||
|
||||
throw new \LogicException($message);
|
||||
}
|
||||
return str_contains($msg, 'Compilation failed: lookbehind assertion is not fixed length');
|
||||
}
|
||||
}
|
||||
|
||||
58
vendor/symfony/routing/Matcher/ExpressionLanguageProvider.php
vendored
Normal file
58
vendor/symfony/routing/Matcher/ExpressionLanguageProvider.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher;
|
||||
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunction;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||
use Symfony\Contracts\Service\ServiceProviderInterface;
|
||||
|
||||
/**
|
||||
* Exposes functions defined in the request context to route conditions.
|
||||
*
|
||||
* @author Ahmed TAILOULOUTE <ahmed.tailouloute@gmail.com>
|
||||
*/
|
||||
class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface
|
||||
{
|
||||
private $functions;
|
||||
|
||||
public function __construct(ServiceProviderInterface $functions)
|
||||
{
|
||||
$this->functions = $functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFunctions(): array
|
||||
{
|
||||
$functions = [];
|
||||
|
||||
foreach ($this->functions->getProvidedServices() as $function => $type) {
|
||||
$functions[] = new ExpressionFunction(
|
||||
$function,
|
||||
static function (...$args) use ($function) {
|
||||
return sprintf('($context->getParameter(\'_functions\')->get(%s)(%s))', var_export($function, true), implode(', ', $args));
|
||||
},
|
||||
function ($values, ...$args) use ($function) {
|
||||
return $values['context']->getParameter('_functions')->get($function)(...$args);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return $functions;
|
||||
}
|
||||
|
||||
public function get(string $function): callable
|
||||
{
|
||||
return $this->functions->get($function);
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher;
|
||||
|
||||
use Symfony\Component\Routing\Exception\ExceptionInterface;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
@@ -22,44 +22,43 @@ abstract class RedirectableUrlMatcher extends UrlMatcher implements Redirectable
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function match($pathinfo)
|
||||
public function match(string $pathinfo): array
|
||||
{
|
||||
try {
|
||||
$parameters = parent::match($pathinfo);
|
||||
return parent::match($pathinfo);
|
||||
} catch (ResourceNotFoundException $e) {
|
||||
if ('/' === substr($pathinfo, -1) || !in_array($this->context->getMethod(), array('HEAD', 'GET'))) {
|
||||
if (!\in_array($this->context->getMethod(), ['HEAD', 'GET'], true)) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
try {
|
||||
parent::match($pathinfo.'/');
|
||||
if ($this->allowSchemes) {
|
||||
redirect_scheme:
|
||||
$scheme = $this->context->getScheme();
|
||||
$this->context->setScheme(current($this->allowSchemes));
|
||||
try {
|
||||
$ret = parent::match($pathinfo);
|
||||
|
||||
return $this->redirect($pathinfo.'/', null);
|
||||
} catch (ResourceNotFoundException $e2) {
|
||||
return $this->redirect($pathinfo, $ret['_route'] ?? null, $this->context->getScheme()) + $ret;
|
||||
} catch (ExceptionInterface $e2) {
|
||||
throw $e;
|
||||
} finally {
|
||||
$this->context->setScheme($scheme);
|
||||
}
|
||||
} elseif ('/' === $trimmedPathinfo = rtrim($pathinfo, '/') ?: '/') {
|
||||
throw $e;
|
||||
} else {
|
||||
try {
|
||||
$pathinfo = $trimmedPathinfo === $pathinfo ? $pathinfo.'/' : $trimmedPathinfo;
|
||||
$ret = parent::match($pathinfo);
|
||||
|
||||
return $this->redirect($pathinfo, $ret['_route'] ?? null) + $ret;
|
||||
} catch (ExceptionInterface $e2) {
|
||||
if ($this->allowSchemes) {
|
||||
goto redirect_scheme;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function handleRouteRequirements($pathinfo, $name, Route $route)
|
||||
{
|
||||
// expression condition
|
||||
if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), array('context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)))) {
|
||||
return array(self::REQUIREMENT_MISMATCH, null);
|
||||
}
|
||||
|
||||
// check HTTP scheme requirement
|
||||
$scheme = $this->context->getScheme();
|
||||
$schemes = $route->getSchemes();
|
||||
if ($schemes && !$route->hasScheme($scheme)) {
|
||||
return array(self::ROUTE_MATCH, $this->redirect($pathinfo, $name, current($schemes)));
|
||||
}
|
||||
|
||||
return array(self::REQUIREMENT_MATCH, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,11 @@ namespace Symfony\Component\Routing\Matcher;
|
||||
interface RedirectableUrlMatcherInterface
|
||||
{
|
||||
/**
|
||||
* Redirects the user to another URL.
|
||||
* Redirects the user to another URL and returns the parameters for the redirection.
|
||||
*
|
||||
* @param string $path The path info to redirect to
|
||||
* @param string $route The route name that matched
|
||||
* @param string|null $scheme The URL scheme (null to keep the current one)
|
||||
*
|
||||
* @return array An array of parameters
|
||||
*/
|
||||
public function redirect($path, $route, $scheme = null);
|
||||
public function redirect(string $path, string $route, string $scheme = null): array;
|
||||
}
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
namespace Symfony\Component\Routing\Matcher;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\NoConfigurationException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
|
||||
/**
|
||||
* RequestMatcherInterface is the interface that all request matcher classes must implement.
|
||||
@@ -25,15 +26,12 @@ interface RequestMatcherInterface
|
||||
/**
|
||||
* Tries to match a request with a set of routes.
|
||||
*
|
||||
* If the matcher can not find information, it must throw one of the exceptions documented
|
||||
* If the matcher cannot find information, it must throw one of the exceptions documented
|
||||
* below.
|
||||
*
|
||||
* @param Request $request The request to match
|
||||
*
|
||||
* @return array An array of parameters
|
||||
*
|
||||
* @throws NoConfigurationException If no routing configuration could be found
|
||||
* @throws ResourceNotFoundException If no matching resource could be found
|
||||
* @throws MethodNotAllowedException If a matching resource was found but the request method is not allowed
|
||||
*/
|
||||
public function matchRequest(Request $request);
|
||||
public function matchRequest(Request $request): array;
|
||||
}
|
||||
|
||||
@@ -23,15 +23,15 @@ use Symfony\Component\Routing\RouteCollection;
|
||||
*/
|
||||
class TraceableUrlMatcher extends UrlMatcher
|
||||
{
|
||||
const ROUTE_DOES_NOT_MATCH = 0;
|
||||
const ROUTE_ALMOST_MATCHES = 1;
|
||||
const ROUTE_MATCHES = 2;
|
||||
public const ROUTE_DOES_NOT_MATCH = 0;
|
||||
public const ROUTE_ALMOST_MATCHES = 1;
|
||||
public const ROUTE_MATCHES = 2;
|
||||
|
||||
protected $traces;
|
||||
|
||||
public function getTraces($pathinfo)
|
||||
public function getTraces(string $pathinfo)
|
||||
{
|
||||
$this->traces = array();
|
||||
$this->traces = [];
|
||||
|
||||
try {
|
||||
$this->match($pathinfo);
|
||||
@@ -50,14 +50,34 @@ class TraceableUrlMatcher extends UrlMatcher
|
||||
return $traces;
|
||||
}
|
||||
|
||||
protected function matchCollection($pathinfo, RouteCollection $routes)
|
||||
protected function matchCollection(string $pathinfo, RouteCollection $routes): array
|
||||
{
|
||||
// HEAD and GET are equivalent as per RFC
|
||||
if ('HEAD' === $method = $this->context->getMethod()) {
|
||||
$method = 'GET';
|
||||
}
|
||||
$supportsTrailingSlash = 'GET' === $method && $this instanceof RedirectableUrlMatcherInterface;
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/') ?: '/';
|
||||
|
||||
foreach ($routes as $name => $route) {
|
||||
$compiledRoute = $route->compile();
|
||||
$staticPrefix = rtrim($compiledRoute->getStaticPrefix(), '/');
|
||||
$requiredMethods = $route->getMethods();
|
||||
|
||||
if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) {
|
||||
// check the static prefix of the URL first. Only use the more expensive preg_match when it matches
|
||||
if ('' !== $staticPrefix && !str_starts_with($trimmedPathinfo, $staticPrefix)) {
|
||||
$this->addTrace(sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route);
|
||||
continue;
|
||||
}
|
||||
$regex = $compiledRoute->getRegex();
|
||||
|
||||
$pos = strrpos($regex, '$');
|
||||
$hasTrailingSlash = '/' === $regex[$pos - 1];
|
||||
$regex = substr_replace($regex, '/?$', $pos - $hasTrailingSlash, 1 + $hasTrailingSlash);
|
||||
|
||||
if (!preg_match($regex, $pathinfo, $matches)) {
|
||||
// does it match without any requirements?
|
||||
$r = new Route($route->getPath(), $route->getDefaults(), array(), $route->getOptions());
|
||||
$r = new Route($route->getPath(), $route->getDefaults(), [], $route->getOptions());
|
||||
$cr = $r->compile();
|
||||
if (!preg_match($cr->getRegex(), $pathinfo)) {
|
||||
$this->addTrace(sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route);
|
||||
@@ -66,10 +86,10 @@ class TraceableUrlMatcher extends UrlMatcher
|
||||
}
|
||||
|
||||
foreach ($route->getRequirements() as $n => $regex) {
|
||||
$r = new Route($route->getPath(), $route->getDefaults(), array($n => $regex), $route->getOptions());
|
||||
$r = new Route($route->getPath(), $route->getDefaults(), [$n => $regex], $route->getOptions());
|
||||
$cr = $r->compile();
|
||||
|
||||
if (in_array($n, $cr->getVariables()) && !preg_match($cr->getRegex(), $pathinfo)) {
|
||||
if (\in_array($n, $cr->getVariables()) && !preg_match($cr->getRegex(), $pathinfo)) {
|
||||
$this->addTrace(sprintf('Requirement for "%s" does not match (%s)', $n, $regex), self::ROUTE_ALMOST_MATCHES, $name, $route);
|
||||
|
||||
continue 2;
|
||||
@@ -79,63 +99,66 @@ class TraceableUrlMatcher extends UrlMatcher
|
||||
continue;
|
||||
}
|
||||
|
||||
// check host requirement
|
||||
$hostMatches = array();
|
||||
$hasTrailingVar = $trimmedPathinfo !== $pathinfo && preg_match('#\{\w+\}/?$#', $route->getPath());
|
||||
|
||||
if ($hasTrailingVar && ($hasTrailingSlash || (null === $m = $matches[\count($compiledRoute->getPathVariables())] ?? null) || '/' !== ($m[-1] ?? '/')) && preg_match($regex, $trimmedPathinfo, $m)) {
|
||||
if ($hasTrailingSlash) {
|
||||
$matches = $m;
|
||||
} else {
|
||||
$hasTrailingVar = false;
|
||||
}
|
||||
}
|
||||
|
||||
$hostMatches = [];
|
||||
if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) {
|
||||
$this->addTrace(sprintf('Host "%s" does not match the requirement ("%s")', $this->context->getHost(), $route->getHost()), self::ROUTE_ALMOST_MATCHES, $name, $route);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// check HTTP method requirement
|
||||
if ($requiredMethods = $route->getMethods()) {
|
||||
// HEAD and GET are equivalent as per RFC
|
||||
if ('HEAD' === $method = $this->context->getMethod()) {
|
||||
$method = 'GET';
|
||||
}
|
||||
$status = $this->handleRouteRequirements($pathinfo, $name, $route);
|
||||
|
||||
if (!in_array($method, $requiredMethods)) {
|
||||
$this->allow = array_merge($this->allow, $requiredMethods);
|
||||
|
||||
$this->addTrace(sprintf('Method "%s" does not match any of the required methods (%s)', $this->context->getMethod(), implode(', ', $requiredMethods)), self::ROUTE_ALMOST_MATCHES, $name, $route);
|
||||
|
||||
continue;
|
||||
}
|
||||
if (self::REQUIREMENT_MISMATCH === $status[0]) {
|
||||
$this->addTrace(sprintf('Condition "%s" does not evaluate to "true"', $route->getCondition()), self::ROUTE_ALMOST_MATCHES, $name, $route);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check condition
|
||||
if ($condition = $route->getCondition()) {
|
||||
if (!$this->getExpressionLanguage()->evaluate($condition, array('context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)))) {
|
||||
$this->addTrace(sprintf('Condition "%s" does not evaluate to "true"', $condition), self::ROUTE_ALMOST_MATCHES, $name, $route);
|
||||
if ('/' !== $pathinfo && !$hasTrailingVar && $hasTrailingSlash === ($trimmedPathinfo === $pathinfo)) {
|
||||
if ($supportsTrailingSlash && (!$requiredMethods || \in_array('GET', $requiredMethods))) {
|
||||
$this->addTrace('Route matches!', self::ROUTE_MATCHES, $name, $route);
|
||||
|
||||
continue;
|
||||
return $this->allow = $this->allowSchemes = [];
|
||||
}
|
||||
$this->addTrace(sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check HTTP scheme requirement
|
||||
if ($requiredSchemes = $route->getSchemes()) {
|
||||
$scheme = $this->context->getScheme();
|
||||
if ($route->getSchemes() && !$route->hasScheme($this->context->getScheme())) {
|
||||
$this->allowSchemes = array_merge($this->allowSchemes, $route->getSchemes());
|
||||
$this->addTrace(sprintf('Scheme "%s" does not match any of the required schemes (%s)', $this->context->getScheme(), implode(', ', $route->getSchemes())), self::ROUTE_ALMOST_MATCHES, $name, $route);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$route->hasScheme($scheme)) {
|
||||
$this->addTrace(sprintf('Scheme "%s" does not match any of the required schemes (%s); the user will be redirected to first required scheme', $scheme, implode(', ', $requiredSchemes)), self::ROUTE_ALMOST_MATCHES, $name, $route);
|
||||
|
||||
return true;
|
||||
}
|
||||
if ($requiredMethods && !\in_array($method, $requiredMethods)) {
|
||||
$this->allow = array_merge($this->allow, $requiredMethods);
|
||||
$this->addTrace(sprintf('Method "%s" does not match any of the required methods (%s)', $this->context->getMethod(), implode(', ', $requiredMethods)), self::ROUTE_ALMOST_MATCHES, $name, $route);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->addTrace('Route matches!', self::ROUTE_MATCHES, $name, $route);
|
||||
|
||||
return true;
|
||||
return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, $status[1] ?? []));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private function addTrace($log, $level = self::ROUTE_DOES_NOT_MATCH, $name = null, $route = null)
|
||||
private function addTrace(string $log, int $level = self::ROUTE_DOES_NOT_MATCH, string $name = null, Route $route = null)
|
||||
{
|
||||
$this->traces[] = array(
|
||||
$this->traces[] = [
|
||||
'log' => $log,
|
||||
'name' => $name,
|
||||
'level' => $level,
|
||||
'path' => null !== $route ? $route->getPath() : null,
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
187
vendor/symfony/routing/Matcher/UrlMatcher.php
vendored
187
vendor/symfony/routing/Matcher/UrlMatcher.php
vendored
@@ -11,14 +11,15 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher;
|
||||
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\NoConfigurationException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* UrlMatcher matches URL based on a set of routes.
|
||||
@@ -27,39 +28,34 @@ use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||
*/
|
||||
class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
|
||||
{
|
||||
const REQUIREMENT_MATCH = 0;
|
||||
const REQUIREMENT_MISMATCH = 1;
|
||||
const ROUTE_MATCH = 2;
|
||||
public const REQUIREMENT_MATCH = 0;
|
||||
public const REQUIREMENT_MISMATCH = 1;
|
||||
public const ROUTE_MATCH = 2;
|
||||
|
||||
/**
|
||||
* @var RequestContext
|
||||
*/
|
||||
/** @var RequestContext */
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* Collects HTTP methods that would be allowed for the request.
|
||||
*/
|
||||
protected $allow = array();
|
||||
protected $allow = [];
|
||||
|
||||
/**
|
||||
* @var RouteCollection
|
||||
* Collects URI schemes that would be allowed for the request.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected $routes;
|
||||
protected array $allowSchemes = [];
|
||||
|
||||
protected $routes;
|
||||
protected $request;
|
||||
protected $expressionLanguage;
|
||||
|
||||
/**
|
||||
* @var ExpressionFunctionProviderInterface[]
|
||||
*/
|
||||
protected $expressionLanguageProviders = array();
|
||||
protected $expressionLanguageProviders = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param RouteCollection $routes A RouteCollection instance
|
||||
* @param RequestContext $context The context
|
||||
*/
|
||||
public function __construct(RouteCollection $routes, RequestContext $context)
|
||||
{
|
||||
$this->routes = $routes;
|
||||
@@ -77,7 +73,7 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext()
|
||||
public function getContext(): RequestContext
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
@@ -85,23 +81,25 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function match($pathinfo)
|
||||
public function match(string $pathinfo): array
|
||||
{
|
||||
$this->allow = array();
|
||||
$this->allow = $this->allowSchemes = [];
|
||||
|
||||
if ($ret = $this->matchCollection(rawurldecode($pathinfo), $this->routes)) {
|
||||
if ($ret = $this->matchCollection(rawurldecode($pathinfo) ?: '/', $this->routes)) {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
throw 0 < count($this->allow)
|
||||
? new MethodNotAllowedException(array_unique($this->allow))
|
||||
: new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo));
|
||||
if ('/' === $pathinfo && !$this->allow && !$this->allowSchemes) {
|
||||
throw new NoConfigurationException();
|
||||
}
|
||||
|
||||
throw 0 < \count($this->allow) ? new MethodNotAllowedException(array_unique($this->allow)) : new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function matchRequest(Request $request)
|
||||
public function matchRequest(Request $request): array
|
||||
{
|
||||
$this->request = $request;
|
||||
|
||||
@@ -120,59 +118,82 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
|
||||
/**
|
||||
* Tries to match a URL with a set of routes.
|
||||
*
|
||||
* @param string $pathinfo The path info to be parsed
|
||||
* @param RouteCollection $routes The set of routes
|
||||
*
|
||||
* @return array An array of parameters
|
||||
* @param string $pathinfo The path info to be parsed
|
||||
*
|
||||
* @throws NoConfigurationException If no routing configuration could be found
|
||||
* @throws ResourceNotFoundException If the resource could not be found
|
||||
* @throws MethodNotAllowedException If the resource was found but the request method is not allowed
|
||||
*/
|
||||
protected function matchCollection($pathinfo, RouteCollection $routes)
|
||||
protected function matchCollection(string $pathinfo, RouteCollection $routes): array
|
||||
{
|
||||
// HEAD and GET are equivalent as per RFC
|
||||
if ('HEAD' === $method = $this->context->getMethod()) {
|
||||
$method = 'GET';
|
||||
}
|
||||
$supportsTrailingSlash = 'GET' === $method && $this instanceof RedirectableUrlMatcherInterface;
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/') ?: '/';
|
||||
|
||||
foreach ($routes as $name => $route) {
|
||||
$compiledRoute = $route->compile();
|
||||
$staticPrefix = rtrim($compiledRoute->getStaticPrefix(), '/');
|
||||
$requiredMethods = $route->getMethods();
|
||||
|
||||
// check the static prefix of the URL first. Only use the more expensive preg_match when it matches
|
||||
if ('' !== $compiledRoute->getStaticPrefix() && 0 !== strpos($pathinfo, $compiledRoute->getStaticPrefix())) {
|
||||
if ('' !== $staticPrefix && !str_starts_with($trimmedPathinfo, $staticPrefix)) {
|
||||
continue;
|
||||
}
|
||||
$regex = $compiledRoute->getRegex();
|
||||
|
||||
$pos = strrpos($regex, '$');
|
||||
$hasTrailingSlash = '/' === $regex[$pos - 1];
|
||||
$regex = substr_replace($regex, '/?$', $pos - $hasTrailingSlash, 1 + $hasTrailingSlash);
|
||||
|
||||
if (!preg_match($regex, $pathinfo, $matches)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) {
|
||||
continue;
|
||||
$hasTrailingVar = $trimmedPathinfo !== $pathinfo && preg_match('#\{\w+\}/?$#', $route->getPath());
|
||||
|
||||
if ($hasTrailingVar && ($hasTrailingSlash || (null === $m = $matches[\count($compiledRoute->getPathVariables())] ?? null) || '/' !== ($m[-1] ?? '/')) && preg_match($regex, $trimmedPathinfo, $m)) {
|
||||
if ($hasTrailingSlash) {
|
||||
$matches = $m;
|
||||
} else {
|
||||
$hasTrailingVar = false;
|
||||
}
|
||||
}
|
||||
|
||||
$hostMatches = array();
|
||||
$hostMatches = [];
|
||||
if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check HTTP method requirement
|
||||
if ($requiredMethods = $route->getMethods()) {
|
||||
// HEAD and GET are equivalent as per RFC
|
||||
if ('HEAD' === $method = $this->context->getMethod()) {
|
||||
$method = 'GET';
|
||||
}
|
||||
|
||||
if (!in_array($method, $requiredMethods)) {
|
||||
$this->allow = array_merge($this->allow, $requiredMethods);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$status = $this->handleRouteRequirements($pathinfo, $name, $route);
|
||||
|
||||
if (self::ROUTE_MATCH === $status[0]) {
|
||||
return $status[1];
|
||||
}
|
||||
|
||||
if (self::REQUIREMENT_MISMATCH === $status[0]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $this->getAttributes($route, $name, array_replace($matches, $hostMatches));
|
||||
if ('/' !== $pathinfo && !$hasTrailingVar && $hasTrailingSlash === ($trimmedPathinfo === $pathinfo)) {
|
||||
if ($supportsTrailingSlash && (!$requiredMethods || \in_array('GET', $requiredMethods))) {
|
||||
return $this->allow = $this->allowSchemes = [];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($route->getSchemes() && !$route->hasScheme($this->context->getScheme())) {
|
||||
$this->allowSchemes = array_merge($this->allowSchemes, $route->getSchemes());
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($requiredMethods && !\in_array($method, $requiredMethods)) {
|
||||
$this->allow = array_merge($this->allow, $requiredMethods);
|
||||
continue;
|
||||
}
|
||||
|
||||
return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, $status[1] ?? []));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,55 +202,41 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
|
||||
* As this method requires the Route object, it is not available
|
||||
* in matchers that do not have access to the matched Route instance
|
||||
* (like the PHP and Apache matcher dumpers).
|
||||
*
|
||||
* @param Route $route The route we are matching against
|
||||
* @param string $name The name of the route
|
||||
* @param array $attributes An array of attributes from the matcher
|
||||
*
|
||||
* @return array An array of parameters
|
||||
*/
|
||||
protected function getAttributes(Route $route, $name, array $attributes)
|
||||
protected function getAttributes(Route $route, string $name, array $attributes): array
|
||||
{
|
||||
$defaults = $route->getDefaults();
|
||||
if (isset($defaults['_canonical_route'])) {
|
||||
$name = $defaults['_canonical_route'];
|
||||
unset($defaults['_canonical_route']);
|
||||
}
|
||||
$attributes['_route'] = $name;
|
||||
|
||||
return $this->mergeDefaults($attributes, $route->getDefaults());
|
||||
return $this->mergeDefaults($attributes, $defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles specific route requirements.
|
||||
*
|
||||
* @param string $pathinfo The path
|
||||
* @param string $name The route name
|
||||
* @param Route $route The route
|
||||
*
|
||||
* @return array The first element represents the status, the second contains additional information
|
||||
*/
|
||||
protected function handleRouteRequirements($pathinfo, $name, Route $route)
|
||||
protected function handleRouteRequirements(string $pathinfo, string $name, Route $route): array
|
||||
{
|
||||
// expression condition
|
||||
if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), array('context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)))) {
|
||||
return array(self::REQUIREMENT_MISMATCH, null);
|
||||
if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), ['context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)])) {
|
||||
return [self::REQUIREMENT_MISMATCH, null];
|
||||
}
|
||||
|
||||
// check HTTP scheme requirement
|
||||
$scheme = $this->context->getScheme();
|
||||
$status = $route->getSchemes() && !$route->hasScheme($scheme) ? self::REQUIREMENT_MISMATCH : self::REQUIREMENT_MATCH;
|
||||
|
||||
return array($status, null);
|
||||
return [self::REQUIREMENT_MATCH, null];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get merged default parameters.
|
||||
*
|
||||
* @param array $params The parameters
|
||||
* @param array $defaults The defaults
|
||||
*
|
||||
* @return array Merged default parameters
|
||||
*/
|
||||
protected function mergeDefaults($params, $defaults)
|
||||
protected function mergeDefaults(array $params, array $defaults): array
|
||||
{
|
||||
foreach ($params as $key => $value) {
|
||||
if (!is_int($key)) {
|
||||
if (!\is_int($key) && null !== $value) {
|
||||
$defaults[$key] = $value;
|
||||
}
|
||||
}
|
||||
@@ -240,8 +247,8 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
|
||||
protected function getExpressionLanguage()
|
||||
{
|
||||
if (null === $this->expressionLanguage) {
|
||||
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||
throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
|
||||
if (!class_exists(ExpressionLanguage::class)) {
|
||||
throw new \LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
|
||||
}
|
||||
$this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
|
||||
}
|
||||
@@ -252,15 +259,15 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function createRequest($pathinfo)
|
||||
protected function createRequest(string $pathinfo): ?Request
|
||||
{
|
||||
if (!class_exists('Symfony\Component\HttpFoundation\Request')) {
|
||||
if (!class_exists(Request::class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Request::create($this->context->getScheme().'://'.$this->context->getHost().$this->context->getBaseUrl().$pathinfo, $this->context->getMethod(), $this->context->getParameters(), array(), array(), array(
|
||||
return Request::create($this->context->getScheme().'://'.$this->context->getHost().$this->context->getBaseUrl().$pathinfo, $this->context->getMethod(), $this->context->getParameters(), [], [], [
|
||||
'SCRIPT_FILENAME' => $this->context->getBaseUrl(),
|
||||
'SCRIPT_NAME' => $this->context->getBaseUrl(),
|
||||
));
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher;
|
||||
|
||||
use Symfony\Component\Routing\RequestContextAwareInterface;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\NoConfigurationException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\RequestContextAwareInterface;
|
||||
|
||||
/**
|
||||
* UrlMatcherInterface is the interface that all URL matcher classes must implement.
|
||||
@@ -25,15 +26,14 @@ interface UrlMatcherInterface extends RequestContextAwareInterface
|
||||
/**
|
||||
* Tries to match a URL path with a set of routes.
|
||||
*
|
||||
* If the matcher can not find information, it must throw one of the exceptions documented
|
||||
* If the matcher cannot find information, it must throw one of the exceptions documented
|
||||
* below.
|
||||
*
|
||||
* @param string $pathinfo The path info to be parsed (raw format, i.e. not urldecoded)
|
||||
*
|
||||
* @return array An array of parameters
|
||||
*
|
||||
* @throws NoConfigurationException If no routing configuration could be found
|
||||
* @throws ResourceNotFoundException If the resource could not be found
|
||||
* @throws MethodNotAllowedException If the resource was found but the request method is not allowed
|
||||
*/
|
||||
public function match($pathinfo);
|
||||
public function match(string $pathinfo): array;
|
||||
}
|
||||
|
||||
48
vendor/symfony/routing/README.md
vendored
48
vendor/symfony/routing/README.md
vendored
@@ -3,11 +3,49 @@ Routing Component
|
||||
|
||||
The Routing component maps an HTTP request to a set of configuration variables.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
```
|
||||
$ composer require symfony/routing
|
||||
```
|
||||
|
||||
```php
|
||||
use App\Controller\BlogController;
|
||||
use Symfony\Component\Routing\Generator\UrlGenerator;
|
||||
use Symfony\Component\Routing\Matcher\UrlMatcher;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
$route = new Route('/blog/{slug}', ['_controller' => BlogController::class]);
|
||||
$routes = new RouteCollection();
|
||||
$routes->add('blog_show', $route);
|
||||
|
||||
$context = new RequestContext();
|
||||
|
||||
// Routing can match routes with incoming requests
|
||||
$matcher = new UrlMatcher($routes, $context);
|
||||
$parameters = $matcher->match('/blog/lorem-ipsum');
|
||||
// $parameters = [
|
||||
// '_controller' => 'App\Controller\BlogController',
|
||||
// 'slug' => 'lorem-ipsum',
|
||||
// '_route' => 'blog_show'
|
||||
// ]
|
||||
|
||||
// Routing can also generate URLs for a given route
|
||||
$generator = new UrlGenerator($routes, $context);
|
||||
$url = $generator->generate('blog_show', [
|
||||
'slug' => 'my-blog-post',
|
||||
]);
|
||||
// $url = '/blog/my-blog-post'
|
||||
```
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/components/routing/index.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
* [Documentation](https://symfony.com/doc/current/routing.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
|
||||
165
vendor/symfony/routing/RequestContext.php
vendored
165
vendor/symfony/routing/RequestContext.php
vendored
@@ -23,33 +23,17 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
*/
|
||||
class RequestContext
|
||||
{
|
||||
private $baseUrl;
|
||||
private $pathInfo;
|
||||
private $method;
|
||||
private $host;
|
||||
private $scheme;
|
||||
private $httpPort;
|
||||
private $httpsPort;
|
||||
private $queryString;
|
||||
private string $baseUrl;
|
||||
private string $pathInfo;
|
||||
private string $method;
|
||||
private string $host;
|
||||
private string $scheme;
|
||||
private int $httpPort;
|
||||
private int $httpsPort;
|
||||
private string $queryString;
|
||||
private array $parameters = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $parameters = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $baseUrl The base URL
|
||||
* @param string $method The HTTP method
|
||||
* @param string $host The HTTP host name
|
||||
* @param string $scheme The HTTP scheme
|
||||
* @param int $httpPort The HTTP port
|
||||
* @param int $httpsPort The HTTPS port
|
||||
* @param string $path The path
|
||||
* @param string $queryString The query string
|
||||
*/
|
||||
public function __construct($baseUrl = '', $method = 'GET', $host = 'localhost', $scheme = 'http', $httpPort = 80, $httpsPort = 443, $path = '/', $queryString = '')
|
||||
public function __construct(string $baseUrl = '', string $method = 'GET', string $host = 'localhost', string $scheme = 'http', int $httpPort = 80, int $httpsPort = 443, string $path = '/', string $queryString = '')
|
||||
{
|
||||
$this->setBaseUrl($baseUrl);
|
||||
$this->setMethod($method);
|
||||
@@ -61,22 +45,37 @@ class RequestContext
|
||||
$this->setQueryString($queryString);
|
||||
}
|
||||
|
||||
public static function fromUri(string $uri, string $host = 'localhost', string $scheme = 'http', int $httpPort = 80, int $httpsPort = 443): self
|
||||
{
|
||||
$uri = parse_url($uri);
|
||||
$scheme = $uri['scheme'] ?? $scheme;
|
||||
$host = $uri['host'] ?? $host;
|
||||
|
||||
if (isset($uri['port'])) {
|
||||
if ('http' === $scheme) {
|
||||
$httpPort = $uri['port'];
|
||||
} elseif ('https' === $scheme) {
|
||||
$httpsPort = $uri['port'];
|
||||
}
|
||||
}
|
||||
|
||||
return new self($uri['path'] ?? '', 'GET', $host, $scheme, $httpPort, $httpsPort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the RequestContext information based on a HttpFoundation Request.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function fromRequest(Request $request)
|
||||
public function fromRequest(Request $request): static
|
||||
{
|
||||
$this->setBaseUrl($request->getBaseUrl());
|
||||
$this->setPathInfo($request->getPathInfo());
|
||||
$this->setMethod($request->getMethod());
|
||||
$this->setHost($request->getHost());
|
||||
$this->setScheme($request->getScheme());
|
||||
$this->setHttpPort($request->isSecure() ? $this->httpPort : $request->getPort());
|
||||
$this->setHttpsPort($request->isSecure() ? $request->getPort() : $this->httpsPort);
|
||||
$this->setHttpPort($request->isSecure() || null === $request->getPort() ? $this->httpPort : $request->getPort());
|
||||
$this->setHttpsPort($request->isSecure() && null !== $request->getPort() ? $request->getPort() : $this->httpsPort);
|
||||
$this->setQueryString($request->server->get('QUERY_STRING', ''));
|
||||
|
||||
return $this;
|
||||
@@ -84,10 +83,8 @@ class RequestContext
|
||||
|
||||
/**
|
||||
* Gets the base URL.
|
||||
*
|
||||
* @return string The base URL
|
||||
*/
|
||||
public function getBaseUrl()
|
||||
public function getBaseUrl(): string
|
||||
{
|
||||
return $this->baseUrl;
|
||||
}
|
||||
@@ -95,23 +92,19 @@ class RequestContext
|
||||
/**
|
||||
* Sets the base URL.
|
||||
*
|
||||
* @param string $baseUrl The base URL
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setBaseUrl($baseUrl)
|
||||
public function setBaseUrl(string $baseUrl): static
|
||||
{
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->baseUrl = rtrim($baseUrl, '/');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path info.
|
||||
*
|
||||
* @return string The path info
|
||||
*/
|
||||
public function getPathInfo()
|
||||
public function getPathInfo(): string
|
||||
{
|
||||
return $this->pathInfo;
|
||||
}
|
||||
@@ -119,11 +112,9 @@ class RequestContext
|
||||
/**
|
||||
* Sets the path info.
|
||||
*
|
||||
* @param string $pathInfo The path info
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPathInfo($pathInfo)
|
||||
public function setPathInfo(string $pathInfo): static
|
||||
{
|
||||
$this->pathInfo = $pathInfo;
|
||||
|
||||
@@ -134,10 +125,8 @@ class RequestContext
|
||||
* Gets the HTTP method.
|
||||
*
|
||||
* The method is always an uppercased string.
|
||||
*
|
||||
* @return string The HTTP method
|
||||
*/
|
||||
public function getMethod()
|
||||
public function getMethod(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
@@ -145,11 +134,9 @@ class RequestContext
|
||||
/**
|
||||
* Sets the HTTP method.
|
||||
*
|
||||
* @param string $method The HTTP method
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMethod($method)
|
||||
public function setMethod(string $method): static
|
||||
{
|
||||
$this->method = strtoupper($method);
|
||||
|
||||
@@ -160,10 +147,8 @@ class RequestContext
|
||||
* Gets the HTTP host.
|
||||
*
|
||||
* The host is always lowercased because it must be treated case-insensitive.
|
||||
*
|
||||
* @return string The HTTP host
|
||||
*/
|
||||
public function getHost()
|
||||
public function getHost(): string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
@@ -171,11 +156,9 @@ class RequestContext
|
||||
/**
|
||||
* Sets the HTTP host.
|
||||
*
|
||||
* @param string $host The HTTP host
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHost($host)
|
||||
public function setHost(string $host): static
|
||||
{
|
||||
$this->host = strtolower($host);
|
||||
|
||||
@@ -184,10 +167,8 @@ class RequestContext
|
||||
|
||||
/**
|
||||
* Gets the HTTP scheme.
|
||||
*
|
||||
* @return string The HTTP scheme
|
||||
*/
|
||||
public function getScheme()
|
||||
public function getScheme(): string
|
||||
{
|
||||
return $this->scheme;
|
||||
}
|
||||
@@ -195,11 +176,9 @@ class RequestContext
|
||||
/**
|
||||
* Sets the HTTP scheme.
|
||||
*
|
||||
* @param string $scheme The HTTP scheme
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setScheme($scheme)
|
||||
public function setScheme(string $scheme): static
|
||||
{
|
||||
$this->scheme = strtolower($scheme);
|
||||
|
||||
@@ -208,10 +187,8 @@ class RequestContext
|
||||
|
||||
/**
|
||||
* Gets the HTTP port.
|
||||
*
|
||||
* @return int The HTTP port
|
||||
*/
|
||||
public function getHttpPort()
|
||||
public function getHttpPort(): int
|
||||
{
|
||||
return $this->httpPort;
|
||||
}
|
||||
@@ -219,23 +196,19 @@ class RequestContext
|
||||
/**
|
||||
* Sets the HTTP port.
|
||||
*
|
||||
* @param int $httpPort The HTTP port
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHttpPort($httpPort)
|
||||
public function setHttpPort(int $httpPort): static
|
||||
{
|
||||
$this->httpPort = (int) $httpPort;
|
||||
$this->httpPort = $httpPort;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTTPS port.
|
||||
*
|
||||
* @return int The HTTPS port
|
||||
*/
|
||||
public function getHttpsPort()
|
||||
public function getHttpsPort(): int
|
||||
{
|
||||
return $this->httpsPort;
|
||||
}
|
||||
@@ -243,23 +216,19 @@ class RequestContext
|
||||
/**
|
||||
* Sets the HTTPS port.
|
||||
*
|
||||
* @param int $httpsPort The HTTPS port
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHttpsPort($httpsPort)
|
||||
public function setHttpsPort(int $httpsPort): static
|
||||
{
|
||||
$this->httpsPort = (int) $httpsPort;
|
||||
$this->httpsPort = $httpsPort;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the query string.
|
||||
*
|
||||
* @return string The query string without the "?"
|
||||
* Gets the query string without the "?".
|
||||
*/
|
||||
public function getQueryString()
|
||||
public function getQueryString(): string
|
||||
{
|
||||
return $this->queryString;
|
||||
}
|
||||
@@ -267,11 +236,9 @@ class RequestContext
|
||||
/**
|
||||
* Sets the query string.
|
||||
*
|
||||
* @param string $queryString The query string (after "?")
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setQueryString($queryString)
|
||||
public function setQueryString(?string $queryString): static
|
||||
{
|
||||
// string cast to be fault-tolerant, accepting null
|
||||
$this->queryString = (string) $queryString;
|
||||
@@ -281,10 +248,8 @@ class RequestContext
|
||||
|
||||
/**
|
||||
* Returns the parameters.
|
||||
*
|
||||
* @return array The parameters
|
||||
*/
|
||||
public function getParameters()
|
||||
public function getParameters(): array
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
@@ -296,7 +261,7 @@ class RequestContext
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParameters(array $parameters)
|
||||
public function setParameters(array $parameters): static
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
|
||||
@@ -305,40 +270,34 @@ class RequestContext
|
||||
|
||||
/**
|
||||
* Gets a parameter value.
|
||||
*
|
||||
* @param string $name A parameter name
|
||||
*
|
||||
* @return mixed The parameter value or null if nonexistent
|
||||
*/
|
||||
public function getParameter($name)
|
||||
public function getParameter(string $name): mixed
|
||||
{
|
||||
return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
|
||||
return $this->parameters[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a parameter value is set for the given parameter.
|
||||
*
|
||||
* @param string $name A parameter name
|
||||
*
|
||||
* @return bool True if the parameter value is set, false otherwise
|
||||
*/
|
||||
public function hasParameter($name)
|
||||
public function hasParameter(string $name): bool
|
||||
{
|
||||
return array_key_exists($name, $this->parameters);
|
||||
return \array_key_exists($name, $this->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a parameter value.
|
||||
*
|
||||
* @param string $name A parameter name
|
||||
* @param mixed $parameter The parameter value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParameter($name, $parameter)
|
||||
public function setParameter(string $name, mixed $parameter): static
|
||||
{
|
||||
$this->parameters[$name] = $parameter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isSecure(): bool
|
||||
{
|
||||
return 'https' === $this->scheme;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,11 @@ interface RequestContextAwareInterface
|
||||
{
|
||||
/**
|
||||
* Sets the request context.
|
||||
*
|
||||
* @param RequestContext $context The context
|
||||
*/
|
||||
public function setContext(RequestContext $context);
|
||||
|
||||
/**
|
||||
* Gets the request context.
|
||||
*
|
||||
* @return RequestContext The context
|
||||
*/
|
||||
public function getContext();
|
||||
public function getContext(): RequestContext;
|
||||
}
|
||||
|
||||
407
vendor/symfony/routing/Route.php
vendored
407
vendor/symfony/routing/Route.php
vendored
@@ -19,50 +19,15 @@ namespace Symfony\Component\Routing;
|
||||
*/
|
||||
class Route implements \Serializable
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path = '/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $host = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $schemes = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $methods = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $defaults = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $requirements = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $options = array();
|
||||
|
||||
/**
|
||||
* @var null|CompiledRoute
|
||||
*/
|
||||
private $compiled;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $condition = '';
|
||||
private string $path = '/';
|
||||
private string $host = '';
|
||||
private array $schemes = [];
|
||||
private array $methods = [];
|
||||
private array $defaults = [];
|
||||
private array $requirements = [];
|
||||
private array $options = [];
|
||||
private string $condition = '';
|
||||
private $compiled = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -72,20 +37,20 @@ class Route implements \Serializable
|
||||
* * compiler_class: A class name able to compile this route instance (RouteCompiler by default)
|
||||
* * utf8: Whether UTF-8 matching is enforced ot not
|
||||
*
|
||||
* @param string $path The path pattern to match
|
||||
* @param array $defaults An array of default parameter values
|
||||
* @param array $requirements An array of requirements for parameters (regexes)
|
||||
* @param array $options An array of options
|
||||
* @param string $host The host pattern to match
|
||||
* @param string|array $schemes A required URI scheme or an array of restricted schemes
|
||||
* @param string|array $methods A required HTTP method or an array of restricted methods
|
||||
* @param string $condition A condition that should evaluate to true for the route to match
|
||||
* @param string $path The path pattern to match
|
||||
* @param array $defaults An array of default parameter values
|
||||
* @param array $requirements An array of requirements for parameters (regexes)
|
||||
* @param array $options An array of options
|
||||
* @param string|null $host The host pattern to match
|
||||
* @param string|string[] $schemes A required URI scheme or an array of restricted schemes
|
||||
* @param string|string[] $methods A required HTTP method or an array of restricted methods
|
||||
* @param string|null $condition A condition that should evaluate to true for the route to match
|
||||
*/
|
||||
public function __construct($path, array $defaults = array(), array $requirements = array(), array $options = array(), $host = '', $schemes = array(), $methods = array(), $condition = '')
|
||||
public function __construct(string $path, array $defaults = [], array $requirements = [], array $options = [], ?string $host = '', string|array $schemes = [], string|array $methods = [], ?string $condition = '')
|
||||
{
|
||||
$this->setPath($path);
|
||||
$this->setDefaults($defaults);
|
||||
$this->setRequirements($requirements);
|
||||
$this->addDefaults($defaults);
|
||||
$this->addRequirements($requirements);
|
||||
$this->setOptions($options);
|
||||
$this->setHost($host);
|
||||
$this->setSchemes($schemes);
|
||||
@@ -93,12 +58,9 @@ class Route implements \Serializable
|
||||
$this->setCondition($condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function serialize()
|
||||
public function __serialize(): array
|
||||
{
|
||||
return serialize(array(
|
||||
return [
|
||||
'path' => $this->path,
|
||||
'host' => $this->host,
|
||||
'defaults' => $this->defaults,
|
||||
@@ -108,15 +70,19 @@ class Route implements \Serializable
|
||||
'methods' => $this->methods,
|
||||
'condition' => $this->condition,
|
||||
'compiled' => $this->compiled,
|
||||
));
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @internal
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
final public function serialize(): string
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
|
||||
}
|
||||
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
$data = unserialize($serialized);
|
||||
$this->path = $data['path'];
|
||||
$this->host = $data['host'];
|
||||
$this->defaults = $data['defaults'];
|
||||
@@ -134,26 +100,25 @@ class Route implements \Serializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pattern for the path.
|
||||
*
|
||||
* @return string The path pattern
|
||||
* @internal
|
||||
*/
|
||||
public function getPath()
|
||||
final public function unserialize(string $serialized)
|
||||
{
|
||||
$this->__unserialize(unserialize($serialized));
|
||||
}
|
||||
|
||||
public function getPath(): string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pattern for the path.
|
||||
*
|
||||
* This method implements a fluent interface.
|
||||
*
|
||||
* @param string $pattern The path pattern
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPath($pattern)
|
||||
public function setPath(string $pattern): static
|
||||
{
|
||||
$pattern = $this->extractInlineDefaultsAndRequirements($pattern);
|
||||
|
||||
// A pattern must start with a slash and must not have multiple slashes at the beginning because the
|
||||
// generated path for this route would be confused with a network path, e.g. '//domain.com/path'.
|
||||
$this->path = '/'.ltrim(trim($pattern), '/');
|
||||
@@ -162,28 +127,17 @@ class Route implements \Serializable
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pattern for the host.
|
||||
*
|
||||
* @return string The host pattern
|
||||
*/
|
||||
public function getHost()
|
||||
public function getHost(): string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pattern for the host.
|
||||
*
|
||||
* This method implements a fluent interface.
|
||||
*
|
||||
* @param string $pattern The host pattern
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHost($pattern)
|
||||
public function setHost(?string $pattern): static
|
||||
{
|
||||
$this->host = (string) $pattern;
|
||||
$this->host = $this->extractInlineDefaultsAndRequirements((string) $pattern);
|
||||
$this->compiled = null;
|
||||
|
||||
return $this;
|
||||
@@ -193,9 +147,9 @@ class Route implements \Serializable
|
||||
* Returns the lowercased schemes this route is restricted to.
|
||||
* So an empty array means that any scheme is allowed.
|
||||
*
|
||||
* @return array The schemes
|
||||
* @return string[]
|
||||
*/
|
||||
public function getSchemes()
|
||||
public function getSchemes(): array
|
||||
{
|
||||
return $this->schemes;
|
||||
}
|
||||
@@ -204,13 +158,11 @@ class Route implements \Serializable
|
||||
* Sets the schemes (e.g. 'https') this route is restricted to.
|
||||
* So an empty array means that any scheme is allowed.
|
||||
*
|
||||
* This method implements a fluent interface.
|
||||
*
|
||||
* @param string|array $schemes The scheme or an array of schemes
|
||||
* @param string|string[] $schemes The scheme or an array of schemes
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSchemes($schemes)
|
||||
public function setSchemes(string|array $schemes): static
|
||||
{
|
||||
$this->schemes = array_map('strtolower', (array) $schemes);
|
||||
$this->compiled = null;
|
||||
@@ -220,23 +172,19 @@ class Route implements \Serializable
|
||||
|
||||
/**
|
||||
* Checks if a scheme requirement has been set.
|
||||
*
|
||||
* @param string $scheme
|
||||
*
|
||||
* @return bool true if the scheme requirement exists, otherwise false
|
||||
*/
|
||||
public function hasScheme($scheme)
|
||||
public function hasScheme(string $scheme): bool
|
||||
{
|
||||
return in_array(strtolower($scheme), $this->schemes, true);
|
||||
return \in_array(strtolower($scheme), $this->schemes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the uppercased HTTP methods this route is restricted to.
|
||||
* So an empty array means that any method is allowed.
|
||||
*
|
||||
* @return array The methods
|
||||
* @return string[]
|
||||
*/
|
||||
public function getMethods()
|
||||
public function getMethods(): array
|
||||
{
|
||||
return $this->methods;
|
||||
}
|
||||
@@ -245,13 +193,11 @@ class Route implements \Serializable
|
||||
* Sets the HTTP methods (e.g. 'POST') this route is restricted to.
|
||||
* So an empty array means that any method is allowed.
|
||||
*
|
||||
* This method implements a fluent interface.
|
||||
*
|
||||
* @param string|array $methods The method or an array of methods
|
||||
* @param string|string[] $methods The method or an array of methods
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMethods($methods)
|
||||
public function setMethods(string|array $methods): static
|
||||
{
|
||||
$this->methods = array_map('strtoupper', (array) $methods);
|
||||
$this->compiled = null;
|
||||
@@ -259,44 +205,27 @@ class Route implements \Serializable
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the options.
|
||||
*
|
||||
* @return array The options
|
||||
*/
|
||||
public function getOptions()
|
||||
public function getOptions(): array
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the options.
|
||||
*
|
||||
* This method implements a fluent interface.
|
||||
*
|
||||
* @param array $options The options
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setOptions(array $options)
|
||||
public function setOptions(array $options): static
|
||||
{
|
||||
$this->options = array(
|
||||
$this->options = [
|
||||
'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler',
|
||||
);
|
||||
];
|
||||
|
||||
return $this->addOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds options.
|
||||
*
|
||||
* This method implements a fluent interface.
|
||||
*
|
||||
* @param array $options The options
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addOptions(array $options)
|
||||
public function addOptions(array $options): static
|
||||
{
|
||||
foreach ($options as $name => $option) {
|
||||
$this->options[$name] = $option;
|
||||
@@ -309,14 +238,9 @@ class Route implements \Serializable
|
||||
/**
|
||||
* Sets an option value.
|
||||
*
|
||||
* This method implements a fluent interface.
|
||||
*
|
||||
* @param string $name An option name
|
||||
* @param mixed $value The option value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setOption($name, $value)
|
||||
public function setOption(string $name, mixed $value): static
|
||||
{
|
||||
$this->options[$name] = $value;
|
||||
$this->compiled = null;
|
||||
@@ -325,66 +249,42 @@ class Route implements \Serializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an option value.
|
||||
*
|
||||
* @param string $name An option name
|
||||
*
|
||||
* @return mixed The option value or null when not given
|
||||
* Returns the option value or null when not found.
|
||||
*/
|
||||
public function getOption($name)
|
||||
public function getOption(string $name): mixed
|
||||
{
|
||||
return isset($this->options[$name]) ? $this->options[$name] : null;
|
||||
return $this->options[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an option has been set.
|
||||
*
|
||||
* @param string $name An option name
|
||||
*
|
||||
* @return bool true if the option is set, false otherwise
|
||||
*/
|
||||
public function hasOption($name)
|
||||
public function hasOption(string $name): bool
|
||||
{
|
||||
return array_key_exists($name, $this->options);
|
||||
return \array_key_exists($name, $this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the defaults.
|
||||
*
|
||||
* @return array The defaults
|
||||
*/
|
||||
public function getDefaults()
|
||||
public function getDefaults(): array
|
||||
{
|
||||
return $this->defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the defaults.
|
||||
*
|
||||
* This method implements a fluent interface.
|
||||
*
|
||||
* @param array $defaults The defaults
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDefaults(array $defaults)
|
||||
public function setDefaults(array $defaults): static
|
||||
{
|
||||
$this->defaults = array();
|
||||
$this->defaults = [];
|
||||
|
||||
return $this->addDefaults($defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds defaults.
|
||||
*
|
||||
* This method implements a fluent interface.
|
||||
*
|
||||
* @param array $defaults The defaults
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addDefaults(array $defaults)
|
||||
public function addDefaults(array $defaults): static
|
||||
{
|
||||
if (isset($defaults['_locale']) && $this->isLocalized()) {
|
||||
unset($defaults['_locale']);
|
||||
}
|
||||
|
||||
foreach ($defaults as $name => $default) {
|
||||
$this->defaults[$name] = $default;
|
||||
}
|
||||
@@ -393,83 +293,55 @@ class Route implements \Serializable
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a default value.
|
||||
*
|
||||
* @param string $name A variable name
|
||||
*
|
||||
* @return mixed The default value or null when not given
|
||||
*/
|
||||
public function getDefault($name)
|
||||
public function getDefault(string $name): mixed
|
||||
{
|
||||
return isset($this->defaults[$name]) ? $this->defaults[$name] : null;
|
||||
return $this->defaults[$name] ?? null;
|
||||
}
|
||||
|
||||
public function hasDefault(string $name): bool
|
||||
{
|
||||
return \array_key_exists($name, $this->defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a default value is set for the given variable.
|
||||
*
|
||||
* @param string $name A variable name
|
||||
*
|
||||
* @return bool true if the default value is set, false otherwise
|
||||
*/
|
||||
public function hasDefault($name)
|
||||
{
|
||||
return array_key_exists($name, $this->defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a default value.
|
||||
*
|
||||
* @param string $name A variable name
|
||||
* @param mixed $default The default value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDefault($name, $default)
|
||||
public function setDefault(string $name, mixed $default): static
|
||||
{
|
||||
if ('_locale' === $name && $this->isLocalized()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->defaults[$name] = $default;
|
||||
$this->compiled = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requirements.
|
||||
*
|
||||
* @return array The requirements
|
||||
*/
|
||||
public function getRequirements()
|
||||
public function getRequirements(): array
|
||||
{
|
||||
return $this->requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the requirements.
|
||||
*
|
||||
* This method implements a fluent interface.
|
||||
*
|
||||
* @param array $requirements The requirements
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRequirements(array $requirements)
|
||||
public function setRequirements(array $requirements): static
|
||||
{
|
||||
$this->requirements = array();
|
||||
$this->requirements = [];
|
||||
|
||||
return $this->addRequirements($requirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds requirements.
|
||||
*
|
||||
* This method implements a fluent interface.
|
||||
*
|
||||
* @param array $requirements The requirements
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addRequirements(array $requirements)
|
||||
public function addRequirements(array $requirements): static
|
||||
{
|
||||
if (isset($requirements['_locale']) && $this->isLocalized()) {
|
||||
unset($requirements['_locale']);
|
||||
}
|
||||
|
||||
foreach ($requirements as $key => $regex) {
|
||||
$this->requirements[$key] = $this->sanitizeRequirement($key, $regex);
|
||||
}
|
||||
@@ -478,66 +350,40 @@ class Route implements \Serializable
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requirement for the given key.
|
||||
*
|
||||
* @param string $key The key
|
||||
*
|
||||
* @return string|null The regex or null when not given
|
||||
*/
|
||||
public function getRequirement($key)
|
||||
public function getRequirement(string $key): ?string
|
||||
{
|
||||
return isset($this->requirements[$key]) ? $this->requirements[$key] : null;
|
||||
return $this->requirements[$key] ?? null;
|
||||
}
|
||||
|
||||
public function hasRequirement(string $key): bool
|
||||
{
|
||||
return \array_key_exists($key, $this->requirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a requirement is set for the given key.
|
||||
*
|
||||
* @param string $key A variable name
|
||||
*
|
||||
* @return bool true if a requirement is specified, false otherwise
|
||||
*/
|
||||
public function hasRequirement($key)
|
||||
{
|
||||
return array_key_exists($key, $this->requirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a requirement for the given key.
|
||||
*
|
||||
* @param string $key The key
|
||||
* @param string $regex The regex
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRequirement($key, $regex)
|
||||
public function setRequirement(string $key, string $regex): static
|
||||
{
|
||||
if ('_locale' === $key && $this->isLocalized()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->requirements[$key] = $this->sanitizeRequirement($key, $regex);
|
||||
$this->compiled = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the condition.
|
||||
*
|
||||
* @return string The condition
|
||||
*/
|
||||
public function getCondition()
|
||||
public function getCondition(): string
|
||||
{
|
||||
return $this->condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the condition.
|
||||
*
|
||||
* This method implements a fluent interface.
|
||||
*
|
||||
* @param string $condition The condition
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCondition($condition)
|
||||
public function setCondition(?string $condition): static
|
||||
{
|
||||
$this->condition = (string) $condition;
|
||||
$this->compiled = null;
|
||||
@@ -548,14 +394,12 @@ class Route implements \Serializable
|
||||
/**
|
||||
* Compiles the route.
|
||||
*
|
||||
* @return CompiledRoute A CompiledRoute instance
|
||||
*
|
||||
* @throws \LogicException If the Route cannot be compiled because the
|
||||
* path or host pattern is invalid
|
||||
*
|
||||
* @see RouteCompiler which is responsible for the compilation process
|
||||
*/
|
||||
public function compile()
|
||||
public function compile(): CompiledRoute
|
||||
{
|
||||
if (null !== $this->compiled) {
|
||||
return $this->compiled;
|
||||
@@ -566,18 +410,38 @@ class Route implements \Serializable
|
||||
return $this->compiled = $class::compile($this);
|
||||
}
|
||||
|
||||
private function sanitizeRequirement($key, $regex)
|
||||
private function extractInlineDefaultsAndRequirements(string $pattern): string
|
||||
{
|
||||
if (!is_string($regex)) {
|
||||
throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" must be a string.', $key));
|
||||
if (false === strpbrk($pattern, '?<')) {
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
if ('' !== $regex && '^' === $regex[0]) {
|
||||
$regex = (string) substr($regex, 1); // returns false for a single character
|
||||
return preg_replace_callback('#\{(!?)(\w++)(<.*?>)?(\?[^\}]*+)?\}#', function ($m) {
|
||||
if (isset($m[4][0])) {
|
||||
$this->setDefault($m[2], '?' !== $m[4] ? substr($m[4], 1) : null);
|
||||
}
|
||||
if (isset($m[3][0])) {
|
||||
$this->setRequirement($m[2], substr($m[3], 1, -1));
|
||||
}
|
||||
|
||||
return '{'.$m[1].$m[2].'}';
|
||||
}, $pattern);
|
||||
}
|
||||
|
||||
private function sanitizeRequirement(string $key, string $regex)
|
||||
{
|
||||
if ('' !== $regex) {
|
||||
if ('^' === $regex[0]) {
|
||||
$regex = substr($regex, 1);
|
||||
} elseif (0 === strpos($regex, '\\A')) {
|
||||
$regex = substr($regex, 2);
|
||||
}
|
||||
}
|
||||
|
||||
if ('$' === substr($regex, -1)) {
|
||||
if (str_ends_with($regex, '$')) {
|
||||
$regex = substr($regex, 0, -1);
|
||||
} elseif (\strlen($regex) - 2 === strpos($regex, '\\z')) {
|
||||
$regex = substr($regex, 0, -2);
|
||||
}
|
||||
|
||||
if ('' === $regex) {
|
||||
@@ -586,4 +450,9 @@ class Route implements \Serializable
|
||||
|
||||
return $regex;
|
||||
}
|
||||
|
||||
private function isLocalized(): bool
|
||||
{
|
||||
return isset($this->defaults['_locale']) && isset($this->defaults['_canonical_route']) && ($this->requirements['_locale'] ?? null) === preg_quote($this->defaults['_locale']);
|
||||
}
|
||||
}
|
||||
|
||||
225
vendor/symfony/routing/RouteCollection.php
vendored
225
vendor/symfony/routing/RouteCollection.php
vendored
@@ -12,6 +12,8 @@
|
||||
namespace Symfony\Component\Routing;
|
||||
|
||||
use Symfony\Component\Config\Resource\ResourceInterface;
|
||||
use Symfony\Component\Routing\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Routing\Exception\RouteCircularReferenceException;
|
||||
|
||||
/**
|
||||
* A RouteCollection represents a set of Route instances.
|
||||
@@ -22,24 +24,40 @@ use Symfony\Component\Config\Resource\ResourceInterface;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*
|
||||
* @implements \IteratorAggregate<string, Route>
|
||||
*/
|
||||
class RouteCollection implements \IteratorAggregate, \Countable
|
||||
{
|
||||
/**
|
||||
* @var Route[]
|
||||
* @var array<string, Route>
|
||||
*/
|
||||
private $routes = array();
|
||||
private array $routes = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @var array<string, Alias>
|
||||
*/
|
||||
private $resources = array();
|
||||
private $aliases = [];
|
||||
|
||||
/**
|
||||
* @var array<string, ResourceInterface>
|
||||
*/
|
||||
private array $resources = [];
|
||||
|
||||
/**
|
||||
* @var array<string, int>
|
||||
*/
|
||||
private array $priorities = [];
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
foreach ($this->routes as $name => $route) {
|
||||
$this->routes[$name] = clone $route;
|
||||
}
|
||||
|
||||
foreach ($this->aliases as $name => $alias) {
|
||||
$this->aliases[$name] = clone $alias;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,96 +67,120 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @see all()
|
||||
*
|
||||
* @return \ArrayIterator|Route[] An \ArrayIterator object for iterating over routes
|
||||
* @return \ArrayIterator<string, Route>
|
||||
*/
|
||||
public function getIterator()
|
||||
public function getIterator(): \ArrayIterator
|
||||
{
|
||||
return new \ArrayIterator($this->routes);
|
||||
return new \ArrayIterator($this->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of Routes in this collection.
|
||||
*
|
||||
* @return int The number of routes
|
||||
*/
|
||||
public function count()
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->routes);
|
||||
return \count($this->routes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a route.
|
||||
*
|
||||
* @param string $name The route name
|
||||
* @param Route $route A Route instance
|
||||
*/
|
||||
public function add($name, Route $route)
|
||||
public function add(string $name, Route $route, int $priority = 0)
|
||||
{
|
||||
unset($this->routes[$name]);
|
||||
unset($this->routes[$name], $this->priorities[$name], $this->aliases[$name]);
|
||||
|
||||
$this->routes[$name] = $route;
|
||||
|
||||
if ($priority) {
|
||||
$this->priorities[$name] = $priority;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all routes in this collection.
|
||||
*
|
||||
* @return Route[] An array of routes
|
||||
* @return array<string, Route>
|
||||
*/
|
||||
public function all()
|
||||
public function all(): array
|
||||
{
|
||||
if ($this->priorities) {
|
||||
$priorities = $this->priorities;
|
||||
$keysOrder = array_flip(array_keys($this->routes));
|
||||
uksort($this->routes, static function ($n1, $n2) use ($priorities, $keysOrder) {
|
||||
return (($priorities[$n2] ?? 0) <=> ($priorities[$n1] ?? 0)) ?: ($keysOrder[$n1] <=> $keysOrder[$n2]);
|
||||
});
|
||||
}
|
||||
|
||||
return $this->routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a route by name.
|
||||
*
|
||||
* @param string $name The route name
|
||||
*
|
||||
* @return Route|null A Route instance or null when not found
|
||||
*/
|
||||
public function get($name)
|
||||
public function get(string $name): ?Route
|
||||
{
|
||||
return isset($this->routes[$name]) ? $this->routes[$name] : null;
|
||||
$visited = [];
|
||||
while (null !== $alias = $this->aliases[$name] ?? null) {
|
||||
if (false !== $searchKey = array_search($name, $visited)) {
|
||||
$visited[] = $name;
|
||||
|
||||
throw new RouteCircularReferenceException($name, \array_slice($visited, $searchKey));
|
||||
}
|
||||
|
||||
if ($alias->isDeprecated()) {
|
||||
$deprecation = $alias->getDeprecation($name);
|
||||
|
||||
trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']);
|
||||
}
|
||||
|
||||
$visited[] = $name;
|
||||
$name = $alias->getId();
|
||||
}
|
||||
|
||||
return $this->routes[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a route or an array of routes by name from the collection.
|
||||
*
|
||||
* @param string|array $name The route name or an array of route names
|
||||
* @param string|string[] $name The route name or an array of route names
|
||||
*/
|
||||
public function remove($name)
|
||||
public function remove(string|array $name)
|
||||
{
|
||||
foreach ((array) $name as $n) {
|
||||
unset($this->routes[$n]);
|
||||
unset($this->routes[$n], $this->priorities[$n], $this->aliases[$n]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a route collection at the end of the current set by appending all
|
||||
* routes of the added collection.
|
||||
*
|
||||
* @param RouteCollection $collection A RouteCollection instance
|
||||
*/
|
||||
public function addCollection(RouteCollection $collection)
|
||||
public function addCollection(self $collection)
|
||||
{
|
||||
// we need to remove all routes with the same names first because just replacing them
|
||||
// would not place the new route at the end of the merged array
|
||||
foreach ($collection->all() as $name => $route) {
|
||||
unset($this->routes[$name]);
|
||||
unset($this->routes[$name], $this->priorities[$name], $this->aliases[$name]);
|
||||
$this->routes[$name] = $route;
|
||||
|
||||
if (isset($collection->priorities[$name])) {
|
||||
$this->priorities[$name] = $collection->priorities[$name];
|
||||
}
|
||||
}
|
||||
|
||||
$this->resources = array_merge($this->resources, $collection->getResources());
|
||||
foreach ($collection->getAliases() as $name => $alias) {
|
||||
unset($this->routes[$name], $this->priorities[$name], $this->aliases[$name]);
|
||||
|
||||
$this->aliases[$name] = $alias;
|
||||
}
|
||||
|
||||
foreach ($collection->getResources() as $resource) {
|
||||
$this->addResource($resource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a prefix to the path of all child routes.
|
||||
*
|
||||
* @param string $prefix An optional prefix to add before each pattern of the route collection
|
||||
* @param array $defaults An array of default values
|
||||
* @param array $requirements An array of requirements
|
||||
*/
|
||||
public function addPrefix($prefix, array $defaults = array(), array $requirements = array())
|
||||
public function addPrefix(string $prefix, array $defaults = [], array $requirements = [])
|
||||
{
|
||||
$prefix = trim(trim($prefix), '/');
|
||||
|
||||
@@ -154,13 +196,37 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the host pattern on all routes.
|
||||
*
|
||||
* @param string $pattern The pattern
|
||||
* @param array $defaults An array of default values
|
||||
* @param array $requirements An array of requirements
|
||||
* Adds a prefix to the name of all the routes within in the collection.
|
||||
*/
|
||||
public function setHost($pattern, array $defaults = array(), array $requirements = array())
|
||||
public function addNamePrefix(string $prefix)
|
||||
{
|
||||
$prefixedRoutes = [];
|
||||
$prefixedPriorities = [];
|
||||
$prefixedAliases = [];
|
||||
|
||||
foreach ($this->routes as $name => $route) {
|
||||
$prefixedRoutes[$prefix.$name] = $route;
|
||||
if (null !== $canonicalName = $route->getDefault('_canonical_route')) {
|
||||
$route->setDefault('_canonical_route', $prefix.$canonicalName);
|
||||
}
|
||||
if (isset($this->priorities[$name])) {
|
||||
$prefixedPriorities[$prefix.$name] = $this->priorities[$name];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->aliases as $name => $alias) {
|
||||
$prefixedAliases[$prefix.$name] = $alias->withId($prefix.$alias->getId());
|
||||
}
|
||||
|
||||
$this->routes = $prefixedRoutes;
|
||||
$this->priorities = $prefixedPriorities;
|
||||
$this->aliases = $prefixedAliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the host pattern on all routes.
|
||||
*/
|
||||
public function setHost(?string $pattern, array $defaults = [], array $requirements = [])
|
||||
{
|
||||
foreach ($this->routes as $route) {
|
||||
$route->setHost($pattern);
|
||||
@@ -173,10 +239,8 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
||||
* Sets a condition on all routes.
|
||||
*
|
||||
* Existing conditions will be overridden.
|
||||
*
|
||||
* @param string $condition The condition
|
||||
*/
|
||||
public function setCondition($condition)
|
||||
public function setCondition(?string $condition)
|
||||
{
|
||||
foreach ($this->routes as $route) {
|
||||
$route->setCondition($condition);
|
||||
@@ -187,8 +251,6 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
||||
* Adds defaults to all routes.
|
||||
*
|
||||
* An existing default value under the same name in a route will be overridden.
|
||||
*
|
||||
* @param array $defaults An array of default values
|
||||
*/
|
||||
public function addDefaults(array $defaults)
|
||||
{
|
||||
@@ -203,8 +265,6 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
||||
* Adds requirements to all routes.
|
||||
*
|
||||
* An existing requirement under the same name in a route will be overridden.
|
||||
*
|
||||
* @param array $requirements An array of requirements
|
||||
*/
|
||||
public function addRequirements(array $requirements)
|
||||
{
|
||||
@@ -219,8 +279,6 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
||||
* Adds options to all routes.
|
||||
*
|
||||
* An existing option value under the same name in a route will be overridden.
|
||||
*
|
||||
* @param array $options An array of options
|
||||
*/
|
||||
public function addOptions(array $options)
|
||||
{
|
||||
@@ -234,9 +292,9 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Sets the schemes (e.g. 'https') all child routes are restricted to.
|
||||
*
|
||||
* @param string|array $schemes The scheme or an array of schemes
|
||||
* @param string|string[] $schemes The scheme or an array of schemes
|
||||
*/
|
||||
public function setSchemes($schemes)
|
||||
public function setSchemes(string|array $schemes)
|
||||
{
|
||||
foreach ($this->routes as $route) {
|
||||
$route->setSchemes($schemes);
|
||||
@@ -246,9 +304,9 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Sets the HTTP methods (e.g. 'POST') all child routes are restricted to.
|
||||
*
|
||||
* @param string|array $methods The method or an array of methods
|
||||
* @param string|string[] $methods The method or an array of methods
|
||||
*/
|
||||
public function setMethods($methods)
|
||||
public function setMethods(string|array $methods)
|
||||
{
|
||||
foreach ($this->routes as $route) {
|
||||
$route->setMethods($methods);
|
||||
@@ -258,20 +316,55 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns an array of resources loaded to build this collection.
|
||||
*
|
||||
* @return ResourceInterface[] An array of resources
|
||||
* @return ResourceInterface[]
|
||||
*/
|
||||
public function getResources()
|
||||
public function getResources(): array
|
||||
{
|
||||
return array_unique($this->resources);
|
||||
return array_values($this->resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a resource for this collection.
|
||||
*
|
||||
* @param ResourceInterface $resource A resource instance
|
||||
* Adds a resource for this collection. If the resource already exists
|
||||
* it is not added.
|
||||
*/
|
||||
public function addResource(ResourceInterface $resource)
|
||||
{
|
||||
$this->resources[] = $resource;
|
||||
$key = (string) $resource;
|
||||
|
||||
if (!isset($this->resources[$key])) {
|
||||
$this->resources[$key] = $resource;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an alias for an existing route.
|
||||
*
|
||||
* @param string $name The alias to create
|
||||
* @param string $alias The route to alias
|
||||
*
|
||||
* @throws InvalidArgumentException if the alias is for itself
|
||||
*/
|
||||
public function addAlias(string $name, string $alias): Alias
|
||||
{
|
||||
if ($name === $alias) {
|
||||
throw new InvalidArgumentException(sprintf('Route alias "%s" can not reference itself.', $name));
|
||||
}
|
||||
|
||||
unset($this->routes[$name], $this->priorities[$name]);
|
||||
|
||||
return $this->aliases[$name] = new Alias($alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, Alias>
|
||||
*/
|
||||
public function getAliases(): array
|
||||
{
|
||||
return $this->aliases;
|
||||
}
|
||||
|
||||
public function getAlias(string $name): ?Alias
|
||||
{
|
||||
return $this->aliases[$name] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
383
vendor/symfony/routing/RouteCollectionBuilder.php
vendored
383
vendor/symfony/routing/RouteCollectionBuilder.php
vendored
@@ -1,383 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing;
|
||||
|
||||
use Symfony\Component\Config\Exception\FileLoaderLoadException;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\Config\Resource\ResourceInterface;
|
||||
|
||||
/**
|
||||
* Helps add and import routes into a RouteCollection.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*/
|
||||
class RouteCollectionBuilder
|
||||
{
|
||||
/**
|
||||
* @var Route[]|RouteCollectionBuilder[]
|
||||
*/
|
||||
private $routes = array();
|
||||
|
||||
private $loader;
|
||||
private $defaults = array();
|
||||
private $prefix;
|
||||
private $host;
|
||||
private $condition;
|
||||
private $requirements = array();
|
||||
private $options = array();
|
||||
private $schemes;
|
||||
private $methods;
|
||||
private $resources = array();
|
||||
|
||||
/**
|
||||
* @param LoaderInterface $loader
|
||||
*/
|
||||
public function __construct(LoaderInterface $loader = null)
|
||||
{
|
||||
$this->loader = $loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import an external routing resource and returns the RouteCollectionBuilder.
|
||||
*
|
||||
* $routes->import('blog.yml', '/blog');
|
||||
*
|
||||
* @param mixed $resource
|
||||
* @param string|null $prefix
|
||||
* @param string $type
|
||||
*
|
||||
* @return self
|
||||
*
|
||||
* @throws FileLoaderLoadException
|
||||
*/
|
||||
public function import($resource, $prefix = '/', $type = null)
|
||||
{
|
||||
/** @var RouteCollection[] $collection */
|
||||
$collections = $this->load($resource, $type);
|
||||
|
||||
// create a builder from the RouteCollection
|
||||
$builder = $this->createBuilder();
|
||||
|
||||
foreach ($collections as $collection) {
|
||||
if (null === $collection) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($collection->all() as $name => $route) {
|
||||
$builder->addRoute($route, $name);
|
||||
}
|
||||
|
||||
foreach ($collection->getResources() as $resource) {
|
||||
$builder->addResource($resource);
|
||||
}
|
||||
|
||||
// mount into this builder
|
||||
$this->mount($prefix, $builder);
|
||||
}
|
||||
|
||||
return $builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a route and returns it for future modification.
|
||||
*
|
||||
* @param string $path The route path
|
||||
* @param string $controller The route's controller
|
||||
* @param string|null $name The name to give this route
|
||||
*
|
||||
* @return Route
|
||||
*/
|
||||
public function add($path, $controller, $name = null)
|
||||
{
|
||||
$route = new Route($path);
|
||||
$route->setDefault('_controller', $controller);
|
||||
$this->addRoute($route, $name);
|
||||
|
||||
return $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a RouteCollectionBuilder that can be configured and then added with mount().
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function createBuilder()
|
||||
{
|
||||
return new self($this->loader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a RouteCollectionBuilder.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @param RouteCollectionBuilder $builder
|
||||
*/
|
||||
public function mount($prefix, RouteCollectionBuilder $builder)
|
||||
{
|
||||
$builder->prefix = trim(trim($prefix), '/');
|
||||
$this->routes[] = $builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Route object to the builder.
|
||||
*
|
||||
* @param Route $route
|
||||
* @param string|null $name
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addRoute(Route $route, $name = null)
|
||||
{
|
||||
if (null === $name) {
|
||||
// used as a flag to know which routes will need a name later
|
||||
$name = '_unnamed_route_'.spl_object_hash($route);
|
||||
}
|
||||
|
||||
$this->routes[$name] = $route;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the host on all embedded routes (unless already set).
|
||||
*
|
||||
* @param string $pattern
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHost($pattern)
|
||||
{
|
||||
$this->host = $pattern;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a condition on all embedded routes (unless already set).
|
||||
*
|
||||
* @param string $condition
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCondition($condition)
|
||||
{
|
||||
$this->condition = $condition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a default value that will be added to all embedded routes (unless that
|
||||
* default value is already set).
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDefault($key, $value)
|
||||
{
|
||||
$this->defaults[$key] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a requirement that will be added to all embedded routes (unless that
|
||||
* requirement is already set).
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $regex
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRequirement($key, $regex)
|
||||
{
|
||||
$this->requirements[$key] = $regex;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an option that will be added to all embedded routes (unless that
|
||||
* option is already set).
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setOption($key, $value)
|
||||
{
|
||||
$this->options[$key] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the schemes on all embedded routes (unless already set).
|
||||
*
|
||||
* @param array|string $schemes
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSchemes($schemes)
|
||||
{
|
||||
$this->schemes = $schemes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the methods on all embedded routes (unless already set).
|
||||
*
|
||||
* @param array|string $methods
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMethods($methods)
|
||||
{
|
||||
$this->methods = $methods;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a resource for this collection.
|
||||
*
|
||||
* @param ResourceInterface $resource
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
private function addResource(ResourceInterface $resource)
|
||||
{
|
||||
$this->resources[] = $resource;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the final RouteCollection and returns it.
|
||||
*
|
||||
* @return RouteCollection
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$routeCollection = new RouteCollection();
|
||||
|
||||
foreach ($this->routes as $name => $route) {
|
||||
if ($route instanceof Route) {
|
||||
$route->setDefaults(array_merge($this->defaults, $route->getDefaults()));
|
||||
$route->setOptions(array_merge($this->options, $route->getOptions()));
|
||||
|
||||
foreach ($this->requirements as $key => $val) {
|
||||
if (!$route->hasRequirement($key)) {
|
||||
$route->setRequirement($key, $val);
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $this->prefix) {
|
||||
$route->setPath('/'.$this->prefix.$route->getPath());
|
||||
}
|
||||
|
||||
if (!$route->getHost()) {
|
||||
$route->setHost($this->host);
|
||||
}
|
||||
|
||||
if (!$route->getCondition()) {
|
||||
$route->setCondition($this->condition);
|
||||
}
|
||||
|
||||
if (!$route->getSchemes()) {
|
||||
$route->setSchemes($this->schemes);
|
||||
}
|
||||
|
||||
if (!$route->getMethods()) {
|
||||
$route->setMethods($this->methods);
|
||||
}
|
||||
|
||||
// auto-generate the route name if it's been marked
|
||||
if ('_unnamed_route_' === substr($name, 0, 15)) {
|
||||
$name = $this->generateRouteName($route);
|
||||
}
|
||||
|
||||
$routeCollection->add($name, $route);
|
||||
} else {
|
||||
/* @var self $route */
|
||||
$subCollection = $route->build();
|
||||
$subCollection->addPrefix($this->prefix);
|
||||
|
||||
$routeCollection->addCollection($subCollection);
|
||||
}
|
||||
|
||||
foreach ($this->resources as $resource) {
|
||||
$routeCollection->addResource($resource);
|
||||
}
|
||||
}
|
||||
|
||||
return $routeCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a route name based on details of this route.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function generateRouteName(Route $route)
|
||||
{
|
||||
$methods = implode('_', $route->getMethods()).'_';
|
||||
|
||||
$routeName = $methods.$route->getPath();
|
||||
$routeName = str_replace(array('/', ':', '|', '-'), '_', $routeName);
|
||||
$routeName = preg_replace('/[^a-z0-9A-Z_.]+/', '', $routeName);
|
||||
|
||||
// Collapse consecutive underscores down into a single underscore.
|
||||
$routeName = preg_replace('/_+/', '_', $routeName);
|
||||
|
||||
return $routeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a loader able to load an imported resource and loads it.
|
||||
*
|
||||
* @param mixed $resource A resource
|
||||
* @param string|null $type The resource type or null if unknown
|
||||
*
|
||||
* @return RouteCollection[]
|
||||
*
|
||||
* @throws FileLoaderLoadException If no loader is found
|
||||
*/
|
||||
private function load($resource, $type = null)
|
||||
{
|
||||
if (null === $this->loader) {
|
||||
throw new \BadMethodCallException('Cannot import other routing resources: you must pass a LoaderInterface when constructing RouteCollectionBuilder.');
|
||||
}
|
||||
|
||||
if ($this->loader->supports($resource, $type)) {
|
||||
$collections = $this->loader->load($resource, $type);
|
||||
|
||||
return is_array($collections) ? $collections : array($collections);
|
||||
}
|
||||
|
||||
if (null === $resolver = $this->loader->getResolver()) {
|
||||
throw new FileLoaderLoadException($resource, null, null, null, $type);
|
||||
}
|
||||
|
||||
if (false === $loader = $resolver->resolve($resource, $type)) {
|
||||
throw new FileLoaderLoadException($resource, null, null, null, $type);
|
||||
}
|
||||
|
||||
$collections = $loader->load($resource, $type);
|
||||
|
||||
return is_array($collections) ? $collections : array($collections);
|
||||
}
|
||||
}
|
||||
156
vendor/symfony/routing/RouteCompiler.php
vendored
156
vendor/symfony/routing/RouteCompiler.php
vendored
@@ -19,14 +19,12 @@ namespace Symfony\Component\Routing;
|
||||
*/
|
||||
class RouteCompiler implements RouteCompilerInterface
|
||||
{
|
||||
const REGEX_DELIMITER = '#';
|
||||
|
||||
/**
|
||||
* This string defines the characters that are automatically considered separators in front of
|
||||
* optional placeholders (with default and no static text following). Such a single separator
|
||||
* can be left out together with the optional placeholder from matching and generating URLs.
|
||||
*/
|
||||
const SEPARATORS = '/,;.:-_~+*=@|';
|
||||
public const SEPARATORS = '/,;.:-_~+*=@|';
|
||||
|
||||
/**
|
||||
* The maximum supported length of a PCRE subpattern name
|
||||
@@ -34,22 +32,22 @@ class RouteCompiler implements RouteCompilerInterface
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const VARIABLE_MAXIMUM_LENGTH = 32;
|
||||
public const VARIABLE_MAXIMUM_LENGTH = 32;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws \InvalidArgumentException If a path variable is named _fragment
|
||||
* @throws \LogicException If a variable is referenced more than once
|
||||
* @throws \DomainException If a variable name starts with a digit or if it is too long to be successfully used as
|
||||
* a PCRE subpattern.
|
||||
* @throws \InvalidArgumentException if a path variable is named _fragment
|
||||
* @throws \LogicException if a variable is referenced more than once
|
||||
* @throws \DomainException if a variable name starts with a digit or if it is too long to be successfully used as
|
||||
* a PCRE subpattern
|
||||
*/
|
||||
public static function compile(Route $route)
|
||||
public static function compile(Route $route): CompiledRoute
|
||||
{
|
||||
$hostVariables = array();
|
||||
$variables = array();
|
||||
$hostVariables = [];
|
||||
$variables = [];
|
||||
$hostRegex = null;
|
||||
$hostTokens = array();
|
||||
$hostTokens = [];
|
||||
|
||||
if ('' !== $host = $route->getHost()) {
|
||||
$result = self::compilePattern($route, $host, true);
|
||||
@@ -61,6 +59,14 @@ class RouteCompiler implements RouteCompilerInterface
|
||||
$hostRegex = $result['regex'];
|
||||
}
|
||||
|
||||
$locale = $route->getDefault('_locale');
|
||||
if (null !== $locale && null !== $route->getDefault('_canonical_route') && preg_quote($locale) === $route->getRequirement('_locale')) {
|
||||
$requirements = $route->getRequirements();
|
||||
unset($requirements['_locale']);
|
||||
$route->setRequirements($requirements);
|
||||
$route->setPath(str_replace('{_locale}', $locale, $route->getPath()));
|
||||
}
|
||||
|
||||
$path = $route->getPath();
|
||||
|
||||
$result = self::compilePattern($route, $path, false);
|
||||
@@ -92,19 +98,18 @@ class RouteCompiler implements RouteCompilerInterface
|
||||
);
|
||||
}
|
||||
|
||||
private static function compilePattern(Route $route, $pattern, $isHost)
|
||||
private static function compilePattern(Route $route, string $pattern, bool $isHost): array
|
||||
{
|
||||
$tokens = array();
|
||||
$variables = array();
|
||||
$matches = array();
|
||||
$tokens = [];
|
||||
$variables = [];
|
||||
$matches = [];
|
||||
$pos = 0;
|
||||
$defaultSeparator = $isHost ? '.' : '/';
|
||||
$useUtf8 = preg_match('//u', $pattern);
|
||||
$needsUtf8 = $route->getOption('utf8');
|
||||
|
||||
if (!$needsUtf8 && $useUtf8 && preg_match('/[\x80-\xFF]/', $pattern)) {
|
||||
$needsUtf8 = true;
|
||||
@trigger_error(sprintf('Using UTF-8 route patterns without setting the "utf8" option is deprecated since Symfony 3.2 and will throw a LogicException in 4.0. Turn on the "utf8" route option for pattern "%s".', $pattern), E_USER_DEPRECATED);
|
||||
throw new \LogicException(sprintf('Cannot use UTF-8 route patterns without setting the "utf8" option for route "%s".', $route->getPath()));
|
||||
}
|
||||
if (!$useUtf8 && $needsUtf8) {
|
||||
throw new \LogicException(sprintf('Cannot mix UTF-8 requirements with non-UTF-8 pattern "%s".', $pattern));
|
||||
@@ -112,14 +117,15 @@ class RouteCompiler implements RouteCompilerInterface
|
||||
|
||||
// Match all variables enclosed in "{}" and iterate over them. But we only want to match the innermost variable
|
||||
// in case of nested "{}", e.g. {foo{bar}}. This in ensured because \w does not match "{" or "}" itself.
|
||||
preg_match_all('#\{\w+\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
|
||||
preg_match_all('#\{(!)?(\w+)\}#', $pattern, $matches, \PREG_OFFSET_CAPTURE | \PREG_SET_ORDER);
|
||||
foreach ($matches as $match) {
|
||||
$varName = substr($match[0][0], 1, -1);
|
||||
$important = $match[1][1] >= 0;
|
||||
$varName = $match[2][0];
|
||||
// get all static text preceding the current variable
|
||||
$precedingText = substr($pattern, $pos, $match[0][1] - $pos);
|
||||
$pos = $match[0][1] + strlen($match[0][0]);
|
||||
$pos = $match[0][1] + \strlen($match[0][0]);
|
||||
|
||||
if (!strlen($precedingText)) {
|
||||
if (!\strlen($precedingText)) {
|
||||
$precedingChar = '';
|
||||
} elseif ($useUtf8) {
|
||||
preg_match('/.$/u', $precedingText, $precedingChar);
|
||||
@@ -127,25 +133,25 @@ class RouteCompiler implements RouteCompilerInterface
|
||||
} else {
|
||||
$precedingChar = substr($precedingText, -1);
|
||||
}
|
||||
$isSeparator = '' !== $precedingChar && false !== strpos(static::SEPARATORS, $precedingChar);
|
||||
$isSeparator = '' !== $precedingChar && str_contains(static::SEPARATORS, $precedingChar);
|
||||
|
||||
// A PCRE subpattern name must start with a non-digit. Also a PHP variable cannot start with a digit so the
|
||||
// variable would not be usable as a Controller action argument.
|
||||
if (preg_match('/^\d/', $varName)) {
|
||||
throw new \DomainException(sprintf('Variable name "%s" cannot start with a digit in route pattern "%s". Please use a different name.', $varName, $pattern));
|
||||
}
|
||||
if (in_array($varName, $variables)) {
|
||||
if (\in_array($varName, $variables)) {
|
||||
throw new \LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $pattern, $varName));
|
||||
}
|
||||
|
||||
if (strlen($varName) > self::VARIABLE_MAXIMUM_LENGTH) {
|
||||
throw new \DomainException(sprintf('Variable name "%s" cannot be longer than %s characters in route pattern "%s". Please use a shorter name.', $varName, self::VARIABLE_MAXIMUM_LENGTH, $pattern));
|
||||
if (\strlen($varName) > self::VARIABLE_MAXIMUM_LENGTH) {
|
||||
throw new \DomainException(sprintf('Variable name "%s" cannot be longer than %d characters in route pattern "%s". Please use a shorter name.', $varName, self::VARIABLE_MAXIMUM_LENGTH, $pattern));
|
||||
}
|
||||
|
||||
if ($isSeparator && $precedingText !== $precedingChar) {
|
||||
$tokens[] = array('text', substr($precedingText, 0, -strlen($precedingChar)));
|
||||
} elseif (!$isSeparator && strlen($precedingText) > 0) {
|
||||
$tokens[] = array('text', $precedingText);
|
||||
$tokens[] = ['text', substr($precedingText, 0, -\strlen($precedingChar))];
|
||||
} elseif (!$isSeparator && '' !== $precedingText) {
|
||||
$tokens[] = ['text', $precedingText];
|
||||
}
|
||||
|
||||
$regexp = $route->getRequirement($varName);
|
||||
@@ -154,15 +160,15 @@ class RouteCompiler implements RouteCompilerInterface
|
||||
// Find the next static character after the variable that functions as a separator. By default, this separator and '/'
|
||||
// are disallowed for the variable. This default requirement makes sure that optional variables can be matched at all
|
||||
// and that the generating-matching-combination of URLs unambiguous, i.e. the params used for generating the URL are
|
||||
// the same that will be matched. Example: new Route('/{page}.{_format}', array('_format' => 'html'))
|
||||
// the same that will be matched. Example: new Route('/{page}.{_format}', ['_format' => 'html'])
|
||||
// If {page} would also match the separating dot, {_format} would never match as {page} will eagerly consume everything.
|
||||
// Also even if {_format} was not optional the requirement prevents that {page} matches something that was originally
|
||||
// part of {_format} when generating the URL, e.g. _format = 'mobile.html'.
|
||||
$nextSeparator = self::findNextSeparator($followingPattern, $useUtf8);
|
||||
$regexp = sprintf(
|
||||
'[^%s%s]+',
|
||||
preg_quote($defaultSeparator, self::REGEX_DELIMITER),
|
||||
$defaultSeparator !== $nextSeparator && '' !== $nextSeparator ? preg_quote($nextSeparator, self::REGEX_DELIMITER) : ''
|
||||
preg_quote($defaultSeparator),
|
||||
$defaultSeparator !== $nextSeparator && '' !== $nextSeparator ? preg_quote($nextSeparator) : ''
|
||||
);
|
||||
if (('' !== $nextSeparator && !preg_match('#^\{\w+\}#', $followingPattern)) || '' === $followingPattern) {
|
||||
// When we have a separator, which is disallowed for the variable, we can optimize the regex with a possessive
|
||||
@@ -176,28 +182,35 @@ class RouteCompiler implements RouteCompilerInterface
|
||||
if (!preg_match('//u', $regexp)) {
|
||||
$useUtf8 = false;
|
||||
} elseif (!$needsUtf8 && preg_match('/[\x80-\xFF]|(?<!\\\\)\\\\(?:\\\\\\\\)*+(?-i:X|[pP][\{CLMNPSZ]|x\{[A-Fa-f0-9]{3})/', $regexp)) {
|
||||
$needsUtf8 = true;
|
||||
@trigger_error(sprintf('Using UTF-8 route requirements without setting the "utf8" option is deprecated since Symfony 3.2 and will throw a LogicException in 4.0. Turn on the "utf8" route option for variable "%s" in pattern "%s".', $varName, $pattern), E_USER_DEPRECATED);
|
||||
throw new \LogicException(sprintf('Cannot use UTF-8 route requirements without setting the "utf8" option for variable "%s" in pattern "%s".', $varName, $pattern));
|
||||
}
|
||||
if (!$useUtf8 && $needsUtf8) {
|
||||
throw new \LogicException(sprintf('Cannot mix UTF-8 requirement with non-UTF-8 charset for variable "%s" in pattern "%s".', $varName, $pattern));
|
||||
}
|
||||
$regexp = self::transformCapturingGroupsToNonCapturings($regexp);
|
||||
}
|
||||
|
||||
$tokens[] = array('variable', $isSeparator ? $precedingChar : '', $regexp, $varName);
|
||||
if ($important) {
|
||||
$token = ['variable', $isSeparator ? $precedingChar : '', $regexp, $varName, false, true];
|
||||
} else {
|
||||
$token = ['variable', $isSeparator ? $precedingChar : '', $regexp, $varName];
|
||||
}
|
||||
|
||||
$tokens[] = $token;
|
||||
$variables[] = $varName;
|
||||
}
|
||||
|
||||
if ($pos < strlen($pattern)) {
|
||||
$tokens[] = array('text', substr($pattern, $pos));
|
||||
if ($pos < \strlen($pattern)) {
|
||||
$tokens[] = ['text', substr($pattern, $pos)];
|
||||
}
|
||||
|
||||
// find the first optional token
|
||||
$firstOptional = PHP_INT_MAX;
|
||||
$firstOptional = \PHP_INT_MAX;
|
||||
if (!$isHost) {
|
||||
for ($i = count($tokens) - 1; $i >= 0; --$i) {
|
||||
for ($i = \count($tokens) - 1; $i >= 0; --$i) {
|
||||
$token = $tokens[$i];
|
||||
if ('variable' === $token[0] && $route->hasDefault($token[3])) {
|
||||
// variable is optional when it is not important and has a default value
|
||||
if ('variable' === $token[0] && !($token[5] ?? false) && $route->hasDefault($token[3])) {
|
||||
$firstOptional = $i;
|
||||
} else {
|
||||
break;
|
||||
@@ -207,38 +220,33 @@ class RouteCompiler implements RouteCompilerInterface
|
||||
|
||||
// compute the matching regexp
|
||||
$regexp = '';
|
||||
for ($i = 0, $nbToken = count($tokens); $i < $nbToken; ++$i) {
|
||||
for ($i = 0, $nbToken = \count($tokens); $i < $nbToken; ++$i) {
|
||||
$regexp .= self::computeRegexp($tokens, $i, $firstOptional);
|
||||
}
|
||||
$regexp = self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s'.($isHost ? 'i' : '');
|
||||
$regexp = '{^'.$regexp.'$}sD'.($isHost ? 'i' : '');
|
||||
|
||||
// enable Utf8 matching if really required
|
||||
if ($needsUtf8) {
|
||||
$regexp .= 'u';
|
||||
for ($i = 0, $nbToken = count($tokens); $i < $nbToken; ++$i) {
|
||||
for ($i = 0, $nbToken = \count($tokens); $i < $nbToken; ++$i) {
|
||||
if ('variable' === $tokens[$i][0]) {
|
||||
$tokens[$i][] = true;
|
||||
$tokens[$i][4] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
return [
|
||||
'staticPrefix' => self::determineStaticPrefix($route, $tokens),
|
||||
'regex' => $regexp,
|
||||
'tokens' => array_reverse($tokens),
|
||||
'variables' => $variables,
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the longest static prefix possible for a route.
|
||||
*
|
||||
* @param Route $route
|
||||
* @param array $tokens
|
||||
*
|
||||
* @return string The leading static part of a route's path
|
||||
*/
|
||||
private static function determineStaticPrefix(Route $route, array $tokens)
|
||||
private static function determineStaticPrefix(Route $route, array $tokens): string
|
||||
{
|
||||
if ('text' !== $tokens[0][0]) {
|
||||
return ($route->hasDefault($tokens[0][3]) || '/' === $tokens[0][1]) ? '' : $tokens[0][1];
|
||||
@@ -254,14 +262,9 @@ class RouteCompiler implements RouteCompilerInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next static character in the Route pattern that will serve as a separator.
|
||||
*
|
||||
* @param string $pattern The route pattern
|
||||
* @param bool $useUtf8 Whether the character is encoded in UTF-8 or not
|
||||
*
|
||||
* @return string The next static character that functions as separator (or empty string when none available)
|
||||
* Returns the next static character in the Route pattern that will serve as a separator (or the empty string when none available).
|
||||
*/
|
||||
private static function findNextSeparator($pattern, $useUtf8)
|
||||
private static function findNextSeparator(string $pattern, bool $useUtf8): string
|
||||
{
|
||||
if ('' == $pattern) {
|
||||
// return empty string if pattern is empty or false (false which can be returned by substr)
|
||||
@@ -275,7 +278,7 @@ class RouteCompiler implements RouteCompilerInterface
|
||||
preg_match('/^./u', $pattern, $pattern);
|
||||
}
|
||||
|
||||
return false !== strpos(static::SEPARATORS, $pattern[0]) ? $pattern[0] : '';
|
||||
return str_contains(static::SEPARATORS, $pattern[0]) ? $pattern[0] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,28 +287,26 @@ class RouteCompiler implements RouteCompilerInterface
|
||||
* @param array $tokens The route tokens
|
||||
* @param int $index The index of the current token
|
||||
* @param int $firstOptional The index of the first optional token
|
||||
*
|
||||
* @return string The regexp pattern for a single token
|
||||
*/
|
||||
private static function computeRegexp(array $tokens, $index, $firstOptional)
|
||||
private static function computeRegexp(array $tokens, int $index, int $firstOptional): string
|
||||
{
|
||||
$token = $tokens[$index];
|
||||
if ('text' === $token[0]) {
|
||||
// Text tokens
|
||||
return preg_quote($token[1], self::REGEX_DELIMITER);
|
||||
return preg_quote($token[1]);
|
||||
} else {
|
||||
// Variable tokens
|
||||
if (0 === $index && 0 === $firstOptional) {
|
||||
// When the only token is an optional variable token, the separator is required
|
||||
return sprintf('%s(?P<%s>%s)?', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]);
|
||||
return sprintf('%s(?P<%s>%s)?', preg_quote($token[1]), $token[3], $token[2]);
|
||||
} else {
|
||||
$regexp = sprintf('%s(?P<%s>%s)', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]);
|
||||
$regexp = sprintf('%s(?P<%s>%s)', preg_quote($token[1]), $token[3], $token[2]);
|
||||
if ($index >= $firstOptional) {
|
||||
// Enclose each optional token in a subpattern to make it optional.
|
||||
// "?:" means it is non-capturing, i.e. the portion of the subject string that
|
||||
// matched the optional subpattern is not passed back.
|
||||
$regexp = "(?:$regexp";
|
||||
$nbTokens = count($tokens);
|
||||
$nbTokens = \count($tokens);
|
||||
if ($nbTokens - 1 == $index) {
|
||||
// Close the optional subpatterns
|
||||
$regexp .= str_repeat(')?', $nbTokens - $firstOptional - (0 === $firstOptional ? 1 : 0));
|
||||
@@ -316,4 +317,25 @@ class RouteCompiler implements RouteCompilerInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function transformCapturingGroupsToNonCapturings(string $regexp): string
|
||||
{
|
||||
for ($i = 0; $i < \strlen($regexp); ++$i) {
|
||||
if ('\\' === $regexp[$i]) {
|
||||
++$i;
|
||||
continue;
|
||||
}
|
||||
if ('(' !== $regexp[$i] || !isset($regexp[$i + 2])) {
|
||||
continue;
|
||||
}
|
||||
if ('*' === $regexp[++$i] || '?' === $regexp[$i]) {
|
||||
++$i;
|
||||
continue;
|
||||
}
|
||||
$regexp = substr_replace($regexp, '?:', $i, 0);
|
||||
++$i;
|
||||
}
|
||||
|
||||
return $regexp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,8 @@ interface RouteCompilerInterface
|
||||
/**
|
||||
* Compiles the current route instance.
|
||||
*
|
||||
* @param Route $route A Route instance
|
||||
*
|
||||
* @return CompiledRoute A CompiledRoute instance
|
||||
*
|
||||
* @throws \LogicException If the Route cannot be compiled because the
|
||||
* path or host pattern is invalid
|
||||
*/
|
||||
public static function compile(Route $route);
|
||||
public static function compile(Route $route): CompiledRoute;
|
||||
}
|
||||
|
||||
181
vendor/symfony/routing/Router.php
vendored
181
vendor/symfony/routing/Router.php
vendored
@@ -11,19 +11,23 @@
|
||||
|
||||
namespace Symfony\Component\Routing;
|
||||
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\Config\ConfigCacheInterface;
|
||||
use Symfony\Component\Config\ConfigCacheFactoryInterface;
|
||||
use Symfony\Component\Config\ConfigCacheFactory;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Config\ConfigCacheFactory;
|
||||
use Symfony\Component\Config\ConfigCacheFactoryInterface;
|
||||
use Symfony\Component\Config\ConfigCacheInterface;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Generator\CompiledUrlGenerator;
|
||||
use Symfony\Component\Routing\Generator\ConfigurableRequirementsInterface;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Routing\Generator\Dumper\CompiledUrlGeneratorDumper;
|
||||
use Symfony\Component\Routing\Generator\Dumper\GeneratorDumperInterface;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Routing\Matcher\CompiledUrlMatcher;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface;
|
||||
use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
|
||||
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||
|
||||
/**
|
||||
* The Router class is an example of the integration of all pieces of the
|
||||
@@ -66,7 +70,7 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options = array();
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* @var LoggerInterface|null
|
||||
@@ -74,31 +78,27 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var ConfigCacheFactoryInterface|null
|
||||
* @var string|null
|
||||
*/
|
||||
protected $defaultLocale;
|
||||
|
||||
private $configCacheFactory;
|
||||
|
||||
/**
|
||||
* @var ExpressionFunctionProviderInterface[]
|
||||
*/
|
||||
private $expressionLanguageProviders = array();
|
||||
private array $expressionLanguageProviders = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param LoaderInterface $loader A LoaderInterface instance
|
||||
* @param mixed $resource The main resource to load
|
||||
* @param array $options An array of options
|
||||
* @param RequestContext $context The context
|
||||
* @param LoggerInterface $logger A logger instance
|
||||
*/
|
||||
public function __construct(LoaderInterface $loader, $resource, array $options = array(), RequestContext $context = null, LoggerInterface $logger = null)
|
||||
private static ?array $cache = [];
|
||||
|
||||
public function __construct(LoaderInterface $loader, mixed $resource, array $options = [], RequestContext $context = null, LoggerInterface $logger = null, string $defaultLocale = null)
|
||||
{
|
||||
$this->loader = $loader;
|
||||
$this->resource = $resource;
|
||||
$this->logger = $logger;
|
||||
$this->context = $context ?: new RequestContext();
|
||||
$this->context = $context ?? new RequestContext();
|
||||
$this->setOptions($options);
|
||||
$this->defaultLocale = $defaultLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,42 +109,32 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
* * cache_dir: The cache directory (or null to disable caching)
|
||||
* * debug: Whether to enable debugging or not (false by default)
|
||||
* * generator_class: The name of a UrlGeneratorInterface implementation
|
||||
* * generator_base_class: The base class for the dumped generator class
|
||||
* * generator_cache_class: The class name for the dumped generator class
|
||||
* * generator_dumper_class: The name of a GeneratorDumperInterface implementation
|
||||
* * matcher_class: The name of a UrlMatcherInterface implementation
|
||||
* * matcher_base_class: The base class for the dumped matcher class
|
||||
* * matcher_dumper_class: The class name for the dumped matcher class
|
||||
* * matcher_cache_class: The name of a MatcherDumperInterface implementation
|
||||
* * matcher_dumper_class: The name of a MatcherDumperInterface implementation
|
||||
* * resource_type: Type hint for the main resource (optional)
|
||||
* * strict_requirements: Configure strict requirement checking for generators
|
||||
* implementing ConfigurableRequirementsInterface (default is true)
|
||||
*
|
||||
* @param array $options An array of options
|
||||
*
|
||||
* @throws \InvalidArgumentException When unsupported option is provided
|
||||
*/
|
||||
public function setOptions(array $options)
|
||||
{
|
||||
$this->options = array(
|
||||
$this->options = [
|
||||
'cache_dir' => null,
|
||||
'debug' => false,
|
||||
'generator_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
|
||||
'generator_base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
|
||||
'generator_dumper_class' => 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper',
|
||||
'generator_cache_class' => 'ProjectUrlGenerator',
|
||||
'matcher_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
|
||||
'matcher_base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
|
||||
'matcher_dumper_class' => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper',
|
||||
'matcher_cache_class' => 'ProjectUrlMatcher',
|
||||
'generator_class' => CompiledUrlGenerator::class,
|
||||
'generator_dumper_class' => CompiledUrlGeneratorDumper::class,
|
||||
'matcher_class' => CompiledUrlMatcher::class,
|
||||
'matcher_dumper_class' => CompiledUrlMatcherDumper::class,
|
||||
'resource_type' => null,
|
||||
'strict_requirements' => true,
|
||||
);
|
||||
];
|
||||
|
||||
// check option names and live merge, if errors are encountered Exception will be thrown
|
||||
$invalid = array();
|
||||
$invalid = [];
|
||||
foreach ($options as $key => $value) {
|
||||
if (array_key_exists($key, $this->options)) {
|
||||
if (\array_key_exists($key, $this->options)) {
|
||||
$this->options[$key] = $value;
|
||||
} else {
|
||||
$invalid[] = $key;
|
||||
@@ -159,14 +149,11 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
/**
|
||||
* Sets an option.
|
||||
*
|
||||
* @param string $key The key
|
||||
* @param mixed $value The value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setOption($key, $value)
|
||||
public function setOption(string $key, mixed $value)
|
||||
{
|
||||
if (!array_key_exists($key, $this->options)) {
|
||||
if (!\array_key_exists($key, $this->options)) {
|
||||
throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
|
||||
}
|
||||
|
||||
@@ -176,15 +163,11 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
/**
|
||||
* Gets an option value.
|
||||
*
|
||||
* @param string $key The key
|
||||
*
|
||||
* @return mixed The value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getOption($key)
|
||||
public function getOption(string $key): mixed
|
||||
{
|
||||
if (!array_key_exists($key, $this->options)) {
|
||||
if (!\array_key_exists($key, $this->options)) {
|
||||
throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
|
||||
}
|
||||
|
||||
@@ -221,15 +204,13 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext()
|
||||
public function getContext(): RequestContext
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ConfigCache factory to use.
|
||||
*
|
||||
* @param ConfigCacheFactoryInterface $configCacheFactory The factory to use
|
||||
*/
|
||||
public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
|
||||
{
|
||||
@@ -239,7 +220,7 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
|
||||
public function generate(string $name, array $parameters = [], int $referenceType = self::ABSOLUTE_PATH): string
|
||||
{
|
||||
return $this->getGenerator()->generate($name, $parameters, $referenceType);
|
||||
}
|
||||
@@ -247,7 +228,7 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function match($pathinfo)
|
||||
public function match(string $pathinfo): array
|
||||
{
|
||||
return $this->getMatcher()->match($pathinfo);
|
||||
}
|
||||
@@ -255,7 +236,7 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function matchRequest(Request $request)
|
||||
public function matchRequest(Request $request): array
|
||||
{
|
||||
$matcher = $this->getMatcher();
|
||||
if (!$matcher instanceof RequestMatcherInterface) {
|
||||
@@ -267,18 +248,21 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UrlMatcher instance associated with this Router.
|
||||
*
|
||||
* @return UrlMatcherInterface A UrlMatcherInterface instance
|
||||
* Gets the UrlMatcher or RequestMatcher instance associated with this Router.
|
||||
*/
|
||||
public function getMatcher()
|
||||
public function getMatcher(): UrlMatcherInterface|RequestMatcherInterface
|
||||
{
|
||||
if (null !== $this->matcher) {
|
||||
return $this->matcher;
|
||||
}
|
||||
|
||||
if (null === $this->options['cache_dir'] || null === $this->options['matcher_cache_class']) {
|
||||
$this->matcher = new $this->options['matcher_class']($this->getRouteCollection(), $this->context);
|
||||
if (null === $this->options['cache_dir']) {
|
||||
$routes = $this->getRouteCollection();
|
||||
$compiled = is_a($this->options['matcher_class'], CompiledUrlMatcher::class, true);
|
||||
if ($compiled) {
|
||||
$routes = (new CompiledUrlMatcherDumper($routes))->getCompiledRoutes();
|
||||
}
|
||||
$this->matcher = new $this->options['matcher_class']($routes, $this->context);
|
||||
if (method_exists($this->matcher, 'addExpressionLanguageProvider')) {
|
||||
foreach ($this->expressionLanguageProviders as $provider) {
|
||||
$this->matcher->addExpressionLanguageProvider($provider);
|
||||
@@ -288,7 +272,7 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
return $this->matcher;
|
||||
}
|
||||
|
||||
$cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/'.$this->options['matcher_cache_class'].'.php',
|
||||
$cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/url_matching_routes.php',
|
||||
function (ConfigCacheInterface $cache) {
|
||||
$dumper = $this->getMatcherDumperInstance();
|
||||
if (method_exists($dumper, 'addExpressionLanguageProvider')) {
|
||||
@@ -297,50 +281,40 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
}
|
||||
}
|
||||
|
||||
$options = array(
|
||||
'class' => $this->options['matcher_cache_class'],
|
||||
'base_class' => $this->options['matcher_base_class'],
|
||||
);
|
||||
|
||||
$cache->write($dumper->dump($options), $this->getRouteCollection()->getResources());
|
||||
$cache->write($dumper->dump(), $this->getRouteCollection()->getResources());
|
||||
}
|
||||
);
|
||||
|
||||
require_once $cache->getPath();
|
||||
|
||||
return $this->matcher = new $this->options['matcher_cache_class']($this->context);
|
||||
return $this->matcher = new $this->options['matcher_class'](self::getCompiledRoutes($cache->getPath()), $this->context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UrlGenerator instance associated with this Router.
|
||||
*
|
||||
* @return UrlGeneratorInterface A UrlGeneratorInterface instance
|
||||
*/
|
||||
public function getGenerator()
|
||||
public function getGenerator(): UrlGeneratorInterface
|
||||
{
|
||||
if (null !== $this->generator) {
|
||||
return $this->generator;
|
||||
}
|
||||
|
||||
if (null === $this->options['cache_dir'] || null === $this->options['generator_cache_class']) {
|
||||
$this->generator = new $this->options['generator_class']($this->getRouteCollection(), $this->context, $this->logger);
|
||||
if (null === $this->options['cache_dir']) {
|
||||
$routes = $this->getRouteCollection();
|
||||
$compiled = is_a($this->options['generator_class'], CompiledUrlGenerator::class, true);
|
||||
if ($compiled) {
|
||||
$generatorDumper = new CompiledUrlGeneratorDumper($routes);
|
||||
$routes = array_merge($generatorDumper->getCompiledRoutes(), $generatorDumper->getCompiledAliases());
|
||||
}
|
||||
$this->generator = new $this->options['generator_class']($routes, $this->context, $this->logger, $this->defaultLocale);
|
||||
} else {
|
||||
$cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/'.$this->options['generator_cache_class'].'.php',
|
||||
$cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/url_generating_routes.php',
|
||||
function (ConfigCacheInterface $cache) {
|
||||
$dumper = $this->getGeneratorDumperInstance();
|
||||
|
||||
$options = array(
|
||||
'class' => $this->options['generator_cache_class'],
|
||||
'base_class' => $this->options['generator_base_class'],
|
||||
);
|
||||
|
||||
$cache->write($dumper->dump($options), $this->getRouteCollection()->getResources());
|
||||
$cache->write($dumper->dump(), $this->getRouteCollection()->getResources());
|
||||
}
|
||||
);
|
||||
|
||||
require_once $cache->getPath();
|
||||
|
||||
$this->generator = new $this->options['generator_cache_class']($this->context, $this->logger);
|
||||
$this->generator = new $this->options['generator_class'](self::getCompiledRoutes($cache->getPath()), $this->context, $this->logger, $this->defaultLocale);
|
||||
}
|
||||
|
||||
if ($this->generator instanceof ConfigurableRequirementsInterface) {
|
||||
@@ -355,18 +329,12 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
$this->expressionLanguageProviders[] = $provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return GeneratorDumperInterface
|
||||
*/
|
||||
protected function getGeneratorDumperInstance()
|
||||
protected function getGeneratorDumperInstance(): GeneratorDumperInterface
|
||||
{
|
||||
return new $this->options['generator_dumper_class']($this->getRouteCollection());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MatcherDumperInterface
|
||||
*/
|
||||
protected function getMatcherDumperInstance()
|
||||
protected function getMatcherDumperInstance(): MatcherDumperInterface
|
||||
{
|
||||
return new $this->options['matcher_dumper_class']($this->getRouteCollection());
|
||||
}
|
||||
@@ -374,15 +342,22 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
/**
|
||||
* Provides the ConfigCache factory implementation, falling back to a
|
||||
* default implementation if necessary.
|
||||
*
|
||||
* @return ConfigCacheFactoryInterface $configCacheFactory
|
||||
*/
|
||||
private function getConfigCacheFactory()
|
||||
private function getConfigCacheFactory(): ConfigCacheFactoryInterface
|
||||
{
|
||||
if (null === $this->configCacheFactory) {
|
||||
$this->configCacheFactory = new ConfigCacheFactory($this->options['debug']);
|
||||
return $this->configCacheFactory ??= new ConfigCacheFactory($this->options['debug']);
|
||||
}
|
||||
|
||||
private static function getCompiledRoutes(string $path): array
|
||||
{
|
||||
if ([] === self::$cache && \function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN))) {
|
||||
self::$cache = null;
|
||||
}
|
||||
|
||||
return $this->configCacheFactory;
|
||||
if (null === self::$cache) {
|
||||
return require $path;
|
||||
}
|
||||
|
||||
return self::$cache[$path] ??= require $path;
|
||||
}
|
||||
}
|
||||
|
||||
5
vendor/symfony/routing/RouterInterface.php
vendored
5
vendor/symfony/routing/RouterInterface.php
vendored
@@ -26,7 +26,10 @@ interface RouterInterface extends UrlMatcherInterface, UrlGeneratorInterface
|
||||
/**
|
||||
* Gets the RouteCollection instance associated with this Router.
|
||||
*
|
||||
* @return RouteCollection A RouteCollection instance
|
||||
* WARNING: This method should never be used at runtime as it is SLOW.
|
||||
* You might use it in a cache warmer though.
|
||||
*
|
||||
* @return RouteCollection
|
||||
*/
|
||||
public function getRouteCollection();
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Annotation;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class RouteTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException \BadMethodCallException
|
||||
*/
|
||||
public function testInvalidRouteParameter()
|
||||
{
|
||||
$route = new Route(array('foo' => 'bar'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidParameters
|
||||
*/
|
||||
public function testRouteParameters($parameter, $value, $getter)
|
||||
{
|
||||
$route = new Route(array($parameter => $value));
|
||||
$this->assertEquals($route->$getter(), $value);
|
||||
}
|
||||
|
||||
public function getValidParameters()
|
||||
{
|
||||
return array(
|
||||
array('value', '/Blog', 'getPath'),
|
||||
array('requirements', array('locale' => 'en'), 'getRequirements'),
|
||||
array('options', array('compiler_class' => 'RouteCompiler'), 'getOptions'),
|
||||
array('name', 'blog_index', 'getName'),
|
||||
array('defaults', array('_controller' => 'MyBlogBundle:Blog:index'), 'getDefaults'),
|
||||
array('schemes', array('https'), 'getSchemes'),
|
||||
array('methods', array('GET', 'POST'), 'getMethods'),
|
||||
array('host', '{locale}.example.com', 'getHost'),
|
||||
array('condition', 'context.getMethod() == "GET"', 'getCondition'),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Routing\CompiledRoute;
|
||||
|
||||
class CompiledRouteTest extends TestCase
|
||||
{
|
||||
public function testAccessors()
|
||||
{
|
||||
$compiled = new CompiledRoute('prefix', 'regex', array('tokens'), array(), array(), array(), array(), array('variables'));
|
||||
$this->assertEquals('prefix', $compiled->getStaticPrefix(), '__construct() takes a static prefix as its second argument');
|
||||
$this->assertEquals('regex', $compiled->getRegex(), '__construct() takes a regexp as its third argument');
|
||||
$this->assertEquals(array('tokens'), $compiled->getTokens(), '__construct() takes an array of tokens as its fourth argument');
|
||||
$this->assertEquals(array('variables'), $compiled->getVariables(), '__construct() takes an array of variables as its ninth argument');
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\DependencyInjection;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Config\Loader\LoaderResolver;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass;
|
||||
|
||||
class RoutingResolverPassTest extends TestCase
|
||||
{
|
||||
public function testProcess()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('routing.resolver', LoaderResolver::class);
|
||||
$container->register('loader1')->addTag('routing.loader');
|
||||
$container->register('loader2')->addTag('routing.loader');
|
||||
|
||||
(new RoutingResolverPass())->process($container);
|
||||
|
||||
$this->assertEquals(
|
||||
array(array('addLoader', array(new Reference('loader1'))), array('addLoader', array(new Reference('loader2')))),
|
||||
$container->getDefinition('routing.resolver')->getMethodCalls()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses;
|
||||
|
||||
class BarClass
|
||||
{
|
||||
public function routeAction($arg1, $arg2 = 'defaultValue2', $arg3 = 'defaultValue3')
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses;
|
||||
|
||||
class FooClass
|
||||
{
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses;
|
||||
|
||||
trait FooTrait
|
||||
{
|
||||
public function doBar()
|
||||
{
|
||||
$baz = self::class;
|
||||
if (true) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCompiler;
|
||||
|
||||
class CustomRouteCompiler extends RouteCompiler
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function compile(Route $route)
|
||||
{
|
||||
return new CustomCompiledRoute('', '', array(), array());
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\Routing\Loader\XmlFileLoader;
|
||||
use Symfony\Component\Config\Util\XmlUtils;
|
||||
|
||||
/**
|
||||
* XmlFileLoader with schema validation turned off.
|
||||
*/
|
||||
class CustomXmlFileLoader extends XmlFileLoader
|
||||
{
|
||||
protected function loadFile($file)
|
||||
{
|
||||
return XmlUtils::loadFile($file, function () { return true; });
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
class NoStartTagClass
|
||||
{
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Fixtures\OtherAnnotatedClasses;
|
||||
|
||||
class VariadicClass
|
||||
{
|
||||
public function routeAction(...$params)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Fixtures;
|
||||
|
||||
use Symfony\Component\Routing\Matcher\UrlMatcher;
|
||||
use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface
|
||||
{
|
||||
public function redirect($path, $route, $scheme = null)
|
||||
{
|
||||
return array(
|
||||
'_controller' => 'Some controller reference...',
|
||||
'path' => $path,
|
||||
'scheme' => $scheme,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
blog_show:
|
||||
path: /blog/{slug}
|
||||
defaults: { _controller: "MyBundle:Blog:show" }
|
||||
@@ -1,2 +0,0 @@
|
||||
route1:
|
||||
path: /route/1
|
||||
@@ -1,2 +0,0 @@
|
||||
route2:
|
||||
path: /route/2
|
||||
@@ -1,2 +0,0 @@
|
||||
route3:
|
||||
path: /route/3
|
||||
@@ -1,3 +0,0 @@
|
||||
_directory:
|
||||
resource: "../directory"
|
||||
type: directory
|
||||
@@ -1,163 +0,0 @@
|
||||
# skip "real" requests
|
||||
RewriteCond %{REQUEST_FILENAME} -f
|
||||
RewriteRule .* - [QSA,L]
|
||||
|
||||
# foo
|
||||
RewriteCond %{REQUEST_URI} ^/foo/(baz|symfony)$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:foo,E=_ROUTING_param_bar:%1,E=_ROUTING_default_def:test]
|
||||
|
||||
# foobar
|
||||
RewriteCond %{REQUEST_URI} ^/foo(?:/([^/]++))?$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:foobar,E=_ROUTING_param_bar:%1,E=_ROUTING_default_bar:toto]
|
||||
|
||||
# bar
|
||||
RewriteCond %{REQUEST_URI} ^/bar/([^/]++)$
|
||||
RewriteCond %{REQUEST_METHOD} !^(GET|HEAD)$ [NC]
|
||||
RewriteRule .* - [S=1,E=_ROUTING_allow_GET:1,E=_ROUTING_allow_HEAD:1]
|
||||
RewriteCond %{REQUEST_URI} ^/bar/([^/]++)$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:bar,E=_ROUTING_param_foo:%1]
|
||||
|
||||
# baragain
|
||||
RewriteCond %{REQUEST_URI} ^/baragain/([^/]++)$
|
||||
RewriteCond %{REQUEST_METHOD} !^(GET|POST|HEAD)$ [NC]
|
||||
RewriteRule .* - [S=1,E=_ROUTING_allow_GET:1,E=_ROUTING_allow_POST:1,E=_ROUTING_allow_HEAD:1]
|
||||
RewriteCond %{REQUEST_URI} ^/baragain/([^/]++)$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baragain,E=_ROUTING_param_foo:%1]
|
||||
|
||||
# baz
|
||||
RewriteCond %{REQUEST_URI} ^/test/baz$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz]
|
||||
|
||||
# baz2
|
||||
RewriteCond %{REQUEST_URI} ^/test/baz\.html$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz2]
|
||||
|
||||
# baz3
|
||||
RewriteCond %{REQUEST_URI} ^/test/baz3$
|
||||
RewriteRule .* $0/ [QSA,L,R=301]
|
||||
RewriteCond %{REQUEST_URI} ^/test/baz3/$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz3]
|
||||
|
||||
# baz4
|
||||
RewriteCond %{REQUEST_URI} ^/test/([^/]++)$
|
||||
RewriteRule .* $0/ [QSA,L,R=301]
|
||||
RewriteCond %{REQUEST_URI} ^/test/([^/]++)/$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz4,E=_ROUTING_param_foo:%1]
|
||||
|
||||
# baz5
|
||||
RewriteCond %{REQUEST_URI} ^/test/([^/]++)/$
|
||||
RewriteCond %{REQUEST_METHOD} !^(GET|HEAD)$ [NC]
|
||||
RewriteRule .* - [S=2,E=_ROUTING_allow_GET:1,E=_ROUTING_allow_HEAD:1]
|
||||
RewriteCond %{REQUEST_URI} ^/test/([^/]++)$
|
||||
RewriteRule .* $0/ [QSA,L,R=301]
|
||||
RewriteCond %{REQUEST_URI} ^/test/([^/]++)/$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz5,E=_ROUTING_param_foo:%1]
|
||||
|
||||
# baz5unsafe
|
||||
RewriteCond %{REQUEST_URI} ^/testunsafe/([^/]++)/$
|
||||
RewriteCond %{REQUEST_METHOD} !^(POST)$ [NC]
|
||||
RewriteRule .* - [S=1,E=_ROUTING_allow_POST:1]
|
||||
RewriteCond %{REQUEST_URI} ^/testunsafe/([^/]++)/$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz5unsafe,E=_ROUTING_param_foo:%1]
|
||||
|
||||
# baz6
|
||||
RewriteCond %{REQUEST_URI} ^/test/baz$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz6,E=_ROUTING_default_foo:bar\ baz]
|
||||
|
||||
# baz7
|
||||
RewriteCond %{REQUEST_URI} ^/te\ st/baz$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz7]
|
||||
|
||||
# baz8
|
||||
RewriteCond %{REQUEST_URI} ^/te\\\ st/baz$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz8]
|
||||
|
||||
# baz9
|
||||
RewriteCond %{REQUEST_URI} ^/test/(te\\\ st)$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz9,E=_ROUTING_param_baz:%1]
|
||||
|
||||
RewriteCond %{HTTP:Host} ^a\.example\.com$
|
||||
RewriteRule .? - [E=__ROUTING_host_1:1]
|
||||
|
||||
# route1
|
||||
RewriteCond %{ENV:__ROUTING_host_1} =1
|
||||
RewriteCond %{REQUEST_URI} ^/route1$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route1]
|
||||
|
||||
# route2
|
||||
RewriteCond %{ENV:__ROUTING_host_1} =1
|
||||
RewriteCond %{REQUEST_URI} ^/c2/route2$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route2]
|
||||
|
||||
RewriteCond %{HTTP:Host} ^b\.example\.com$
|
||||
RewriteRule .? - [E=__ROUTING_host_2:1]
|
||||
|
||||
# route3
|
||||
RewriteCond %{ENV:__ROUTING_host_2} =1
|
||||
RewriteCond %{REQUEST_URI} ^/c2/route3$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route3]
|
||||
|
||||
RewriteCond %{HTTP:Host} ^a\.example\.com$
|
||||
RewriteRule .? - [E=__ROUTING_host_3:1]
|
||||
|
||||
# route4
|
||||
RewriteCond %{ENV:__ROUTING_host_3} =1
|
||||
RewriteCond %{REQUEST_URI} ^/route4$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route4]
|
||||
|
||||
RewriteCond %{HTTP:Host} ^c\.example\.com$
|
||||
RewriteRule .? - [E=__ROUTING_host_4:1]
|
||||
|
||||
# route5
|
||||
RewriteCond %{ENV:__ROUTING_host_4} =1
|
||||
RewriteCond %{REQUEST_URI} ^/route5$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route5]
|
||||
|
||||
# route6
|
||||
RewriteCond %{REQUEST_URI} ^/route6$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route6]
|
||||
|
||||
RewriteCond %{HTTP:Host} ^([^\.]++)\.example\.com$
|
||||
RewriteRule .? - [E=__ROUTING_host_5:1,E=__ROUTING_host_5_var1:%1]
|
||||
|
||||
# route11
|
||||
RewriteCond %{ENV:__ROUTING_host_5} =1
|
||||
RewriteCond %{REQUEST_URI} ^/route11$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route11,E=_ROUTING_param_var1:%{ENV:__ROUTING_host_5_var1}]
|
||||
|
||||
# route12
|
||||
RewriteCond %{ENV:__ROUTING_host_5} =1
|
||||
RewriteCond %{REQUEST_URI} ^/route12$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route12,E=_ROUTING_param_var1:%{ENV:__ROUTING_host_5_var1},E=_ROUTING_default_var1:val]
|
||||
|
||||
# route13
|
||||
RewriteCond %{ENV:__ROUTING_host_5} =1
|
||||
RewriteCond %{REQUEST_URI} ^/route13/([^/]++)$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route13,E=_ROUTING_param_var1:%{ENV:__ROUTING_host_5_var1},E=_ROUTING_param_name:%1]
|
||||
|
||||
# route14
|
||||
RewriteCond %{ENV:__ROUTING_host_5} =1
|
||||
RewriteCond %{REQUEST_URI} ^/route14/([^/]++)$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route14,E=_ROUTING_param_var1:%{ENV:__ROUTING_host_5_var1},E=_ROUTING_param_name:%1,E=_ROUTING_default_var1:val]
|
||||
|
||||
RewriteCond %{HTTP:Host} ^c\.example\.com$
|
||||
RewriteRule .? - [E=__ROUTING_host_6:1]
|
||||
|
||||
# route15
|
||||
RewriteCond %{ENV:__ROUTING_host_6} =1
|
||||
RewriteCond %{REQUEST_URI} ^/route15/([^/]++)$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route15,E=_ROUTING_param_name:%1]
|
||||
|
||||
# route16
|
||||
RewriteCond %{REQUEST_URI} ^/route16/([^/]++)$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route16,E=_ROUTING_param_name:%1,E=_ROUTING_default_var1:val]
|
||||
|
||||
# route17
|
||||
RewriteCond %{REQUEST_URI} ^/route17$
|
||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route17]
|
||||
|
||||
# 405 Method Not Allowed
|
||||
RewriteCond %{ENV:_ROUTING__allow_GET} =1 [OR]
|
||||
RewriteCond %{ENV:_ROUTING__allow_HEAD} =1 [OR]
|
||||
RewriteCond %{ENV:_ROUTING__allow_POST} =1
|
||||
RewriteRule .* app.php [QSA,L]
|
||||
@@ -1,317 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* ProjectUrlMatcher.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function match($pathinfo)
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($pathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$request = $this->request;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
$scheme = $context->getScheme();
|
||||
|
||||
if ('HEAD' === $requestMethod) {
|
||||
$canonicalMethod = 'GET';
|
||||
}
|
||||
|
||||
|
||||
if (0 === strpos($pathinfo, '/foo')) {
|
||||
// foo
|
||||
if (preg_match('#^/foo/(?P<bar>baz|symfony)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array ( 'def' => 'test',));
|
||||
}
|
||||
|
||||
// foofoo
|
||||
if ('/foofoo' === $pathinfo) {
|
||||
return array ( 'def' => 'test', '_route' => 'foofoo',);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
elseif (0 === strpos($pathinfo, '/bar')) {
|
||||
// bar
|
||||
if (preg_match('#^/bar/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow[] = 'GET';
|
||||
goto not_bar;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar')), array ());
|
||||
}
|
||||
not_bar:
|
||||
|
||||
// barhead
|
||||
if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow[] = 'GET';
|
||||
goto not_barhead;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'barhead')), array ());
|
||||
}
|
||||
not_barhead:
|
||||
|
||||
}
|
||||
|
||||
elseif (0 === strpos($pathinfo, '/test')) {
|
||||
if (0 === strpos($pathinfo, '/test/baz')) {
|
||||
// baz
|
||||
if ('/test/baz' === $pathinfo) {
|
||||
return array('_route' => 'baz');
|
||||
}
|
||||
|
||||
// baz2
|
||||
if ('/test/baz.html' === $pathinfo) {
|
||||
return array('_route' => 'baz2');
|
||||
}
|
||||
|
||||
// baz3
|
||||
if ('/test/baz3/' === $pathinfo) {
|
||||
return array('_route' => 'baz3');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// baz4
|
||||
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ());
|
||||
}
|
||||
|
||||
// baz5
|
||||
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
|
||||
if ('POST' !== $canonicalMethod) {
|
||||
$allow[] = 'POST';
|
||||
goto not_baz5;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array ());
|
||||
}
|
||||
not_baz5:
|
||||
|
||||
// baz.baz6
|
||||
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
|
||||
if ('PUT' !== $canonicalMethod) {
|
||||
$allow[] = 'PUT';
|
||||
goto not_bazbaz6;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array ());
|
||||
}
|
||||
not_bazbaz6:
|
||||
|
||||
}
|
||||
|
||||
// quoter
|
||||
if (preg_match('#^/(?P<quoter>[\']+)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'quoter')), array ());
|
||||
}
|
||||
|
||||
// space
|
||||
if ('/spa ce' === $pathinfo) {
|
||||
return array('_route' => 'space');
|
||||
}
|
||||
|
||||
if (0 === strpos($pathinfo, '/a')) {
|
||||
if (0 === strpos($pathinfo, '/a/b\'b')) {
|
||||
// foo1
|
||||
if (preg_match('#^/a/b\'b/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ());
|
||||
}
|
||||
|
||||
// bar1
|
||||
if (preg_match('#^/a/b\'b/(?P<bar>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// overridden
|
||||
if (preg_match('#^/a/(?P<var>.*)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ());
|
||||
}
|
||||
|
||||
if (0 === strpos($pathinfo, '/a/b\'b')) {
|
||||
// foo2
|
||||
if (preg_match('#^/a/b\'b/(?P<foo1>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ());
|
||||
}
|
||||
|
||||
// bar2
|
||||
if (preg_match('#^/a/b\'b/(?P<bar1>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar2')), array ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
elseif (0 === strpos($pathinfo, '/multi')) {
|
||||
// helloWorld
|
||||
if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P<who>[^/]++))?$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'helloWorld')), array ( 'who' => 'World!',));
|
||||
}
|
||||
|
||||
// hey
|
||||
if ('/multi/hey/' === $pathinfo) {
|
||||
return array('_route' => 'hey');
|
||||
}
|
||||
|
||||
// overridden2
|
||||
if ('/multi/new' === $pathinfo) {
|
||||
return array('_route' => 'overridden2');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// foo3
|
||||
if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ());
|
||||
}
|
||||
|
||||
// bar3
|
||||
if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<bar>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ());
|
||||
}
|
||||
|
||||
if (0 === strpos($pathinfo, '/aba')) {
|
||||
// ababa
|
||||
if ('/ababa' === $pathinfo) {
|
||||
return array('_route' => 'ababa');
|
||||
}
|
||||
|
||||
// foo4
|
||||
if (preg_match('#^/aba/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo4')), array ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$host = $context->getHost();
|
||||
|
||||
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) {
|
||||
// route1
|
||||
if ('/route1' === $pathinfo) {
|
||||
return array('_route' => 'route1');
|
||||
}
|
||||
|
||||
// route2
|
||||
if ('/c2/route2' === $pathinfo) {
|
||||
return array('_route' => 'route2');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (preg_match('#^b\\.example\\.com$#si', $host, $hostMatches)) {
|
||||
// route3
|
||||
if ('/c2/route3' === $pathinfo) {
|
||||
return array('_route' => 'route3');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) {
|
||||
// route4
|
||||
if ('/route4' === $pathinfo) {
|
||||
return array('_route' => 'route4');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) {
|
||||
// route5
|
||||
if ('/route5' === $pathinfo) {
|
||||
return array('_route' => 'route5');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// route6
|
||||
if ('/route6' === $pathinfo) {
|
||||
return array('_route' => 'route6');
|
||||
}
|
||||
|
||||
if (preg_match('#^(?P<var1>[^\\.]++)\\.example\\.com$#si', $host, $hostMatches)) {
|
||||
if (0 === strpos($pathinfo, '/route1')) {
|
||||
// route11
|
||||
if ('/route11' === $pathinfo) {
|
||||
return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route11')), array ());
|
||||
}
|
||||
|
||||
// route12
|
||||
if ('/route12' === $pathinfo) {
|
||||
return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route12')), array ( 'var1' => 'val',));
|
||||
}
|
||||
|
||||
// route13
|
||||
if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route13')), array ());
|
||||
}
|
||||
|
||||
// route14
|
||||
if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route14')), array ( 'var1' => 'val',));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) {
|
||||
// route15
|
||||
if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// route16
|
||||
if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route16')), array ( 'var1' => 'val',));
|
||||
}
|
||||
|
||||
// route17
|
||||
if ('/route17' === $pathinfo) {
|
||||
return array('_route' => 'route17');
|
||||
}
|
||||
|
||||
// a
|
||||
if ('/a/a...' === $pathinfo) {
|
||||
return array('_route' => 'a');
|
||||
}
|
||||
|
||||
if (0 === strpos($pathinfo, '/a/b')) {
|
||||
// b
|
||||
if (preg_match('#^/a/b/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ());
|
||||
}
|
||||
|
||||
// c
|
||||
if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'c')), array ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
# skip "real" requests
|
||||
RewriteCond %{REQUEST_FILENAME} -f
|
||||
RewriteRule .* - [QSA,L]
|
||||
|
||||
# foo
|
||||
RewriteCond %{REQUEST_URI} ^/foo$
|
||||
RewriteRule .* ap\ p_d\ ev.php [QSA,L,E=_ROUTING_route:foo]
|
||||
@@ -1,349 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* ProjectUrlMatcher.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function match($pathinfo)
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($pathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$request = $this->request;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
$scheme = $context->getScheme();
|
||||
|
||||
if ('HEAD' === $requestMethod) {
|
||||
$canonicalMethod = 'GET';
|
||||
}
|
||||
|
||||
|
||||
if (0 === strpos($pathinfo, '/foo')) {
|
||||
// foo
|
||||
if (preg_match('#^/foo/(?P<bar>baz|symfony)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array ( 'def' => 'test',));
|
||||
}
|
||||
|
||||
// foofoo
|
||||
if ('/foofoo' === $pathinfo) {
|
||||
return array ( 'def' => 'test', '_route' => 'foofoo',);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
elseif (0 === strpos($pathinfo, '/bar')) {
|
||||
// bar
|
||||
if (preg_match('#^/bar/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow[] = 'GET';
|
||||
goto not_bar;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar')), array ());
|
||||
}
|
||||
not_bar:
|
||||
|
||||
// barhead
|
||||
if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow[] = 'GET';
|
||||
goto not_barhead;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'barhead')), array ());
|
||||
}
|
||||
not_barhead:
|
||||
|
||||
}
|
||||
|
||||
elseif (0 === strpos($pathinfo, '/test')) {
|
||||
if (0 === strpos($pathinfo, '/test/baz')) {
|
||||
// baz
|
||||
if ('/test/baz' === $pathinfo) {
|
||||
return array('_route' => 'baz');
|
||||
}
|
||||
|
||||
// baz2
|
||||
if ('/test/baz.html' === $pathinfo) {
|
||||
return array('_route' => 'baz2');
|
||||
}
|
||||
|
||||
// baz3
|
||||
if ('/test/baz3' === $trimmedPathinfo) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'baz3');
|
||||
}
|
||||
|
||||
return array('_route' => 'baz3');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// baz4
|
||||
if (preg_match('#^/test/(?P<foo>[^/]++)/?$#s', $pathinfo, $matches)) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'baz4');
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ());
|
||||
}
|
||||
|
||||
// baz5
|
||||
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
|
||||
if ('POST' !== $canonicalMethod) {
|
||||
$allow[] = 'POST';
|
||||
goto not_baz5;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array ());
|
||||
}
|
||||
not_baz5:
|
||||
|
||||
// baz.baz6
|
||||
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
|
||||
if ('PUT' !== $canonicalMethod) {
|
||||
$allow[] = 'PUT';
|
||||
goto not_bazbaz6;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array ());
|
||||
}
|
||||
not_bazbaz6:
|
||||
|
||||
}
|
||||
|
||||
// quoter
|
||||
if (preg_match('#^/(?P<quoter>[\']+)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'quoter')), array ());
|
||||
}
|
||||
|
||||
// space
|
||||
if ('/spa ce' === $pathinfo) {
|
||||
return array('_route' => 'space');
|
||||
}
|
||||
|
||||
if (0 === strpos($pathinfo, '/a')) {
|
||||
if (0 === strpos($pathinfo, '/a/b\'b')) {
|
||||
// foo1
|
||||
if (preg_match('#^/a/b\'b/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ());
|
||||
}
|
||||
|
||||
// bar1
|
||||
if (preg_match('#^/a/b\'b/(?P<bar>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// overridden
|
||||
if (preg_match('#^/a/(?P<var>.*)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ());
|
||||
}
|
||||
|
||||
if (0 === strpos($pathinfo, '/a/b\'b')) {
|
||||
// foo2
|
||||
if (preg_match('#^/a/b\'b/(?P<foo1>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ());
|
||||
}
|
||||
|
||||
// bar2
|
||||
if (preg_match('#^/a/b\'b/(?P<bar1>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar2')), array ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
elseif (0 === strpos($pathinfo, '/multi')) {
|
||||
// helloWorld
|
||||
if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P<who>[^/]++))?$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'helloWorld')), array ( 'who' => 'World!',));
|
||||
}
|
||||
|
||||
// hey
|
||||
if ('/multi/hey' === $trimmedPathinfo) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'hey');
|
||||
}
|
||||
|
||||
return array('_route' => 'hey');
|
||||
}
|
||||
|
||||
// overridden2
|
||||
if ('/multi/new' === $pathinfo) {
|
||||
return array('_route' => 'overridden2');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// foo3
|
||||
if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ());
|
||||
}
|
||||
|
||||
// bar3
|
||||
if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<bar>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ());
|
||||
}
|
||||
|
||||
if (0 === strpos($pathinfo, '/aba')) {
|
||||
// ababa
|
||||
if ('/ababa' === $pathinfo) {
|
||||
return array('_route' => 'ababa');
|
||||
}
|
||||
|
||||
// foo4
|
||||
if (preg_match('#^/aba/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo4')), array ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$host = $context->getHost();
|
||||
|
||||
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) {
|
||||
// route1
|
||||
if ('/route1' === $pathinfo) {
|
||||
return array('_route' => 'route1');
|
||||
}
|
||||
|
||||
// route2
|
||||
if ('/c2/route2' === $pathinfo) {
|
||||
return array('_route' => 'route2');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (preg_match('#^b\\.example\\.com$#si', $host, $hostMatches)) {
|
||||
// route3
|
||||
if ('/c2/route3' === $pathinfo) {
|
||||
return array('_route' => 'route3');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) {
|
||||
// route4
|
||||
if ('/route4' === $pathinfo) {
|
||||
return array('_route' => 'route4');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) {
|
||||
// route5
|
||||
if ('/route5' === $pathinfo) {
|
||||
return array('_route' => 'route5');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// route6
|
||||
if ('/route6' === $pathinfo) {
|
||||
return array('_route' => 'route6');
|
||||
}
|
||||
|
||||
if (preg_match('#^(?P<var1>[^\\.]++)\\.example\\.com$#si', $host, $hostMatches)) {
|
||||
if (0 === strpos($pathinfo, '/route1')) {
|
||||
// route11
|
||||
if ('/route11' === $pathinfo) {
|
||||
return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route11')), array ());
|
||||
}
|
||||
|
||||
// route12
|
||||
if ('/route12' === $pathinfo) {
|
||||
return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route12')), array ( 'var1' => 'val',));
|
||||
}
|
||||
|
||||
// route13
|
||||
if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route13')), array ());
|
||||
}
|
||||
|
||||
// route14
|
||||
if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route14')), array ( 'var1' => 'val',));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) {
|
||||
// route15
|
||||
if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// route16
|
||||
if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route16')), array ( 'var1' => 'val',));
|
||||
}
|
||||
|
||||
// route17
|
||||
if ('/route17' === $pathinfo) {
|
||||
return array('_route' => 'route17');
|
||||
}
|
||||
|
||||
// a
|
||||
if ('/a/a...' === $pathinfo) {
|
||||
return array('_route' => 'a');
|
||||
}
|
||||
|
||||
if (0 === strpos($pathinfo, '/a/b')) {
|
||||
// b
|
||||
if (preg_match('#^/a/b/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ());
|
||||
}
|
||||
|
||||
// c
|
||||
if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'c')), array ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// secure
|
||||
if ('/secure' === $pathinfo) {
|
||||
$requiredSchemes = array ( 'https' => 0,);
|
||||
if (!isset($requiredSchemes[$scheme])) {
|
||||
return $this->redirect($pathinfo, 'secure', key($requiredSchemes));
|
||||
}
|
||||
|
||||
return array('_route' => 'secure');
|
||||
}
|
||||
|
||||
// nonsecure
|
||||
if ('/nonsecure' === $pathinfo) {
|
||||
$requiredSchemes = array ( 'http' => 0,);
|
||||
if (!isset($requiredSchemes[$scheme])) {
|
||||
return $this->redirect($pathinfo, 'nonsecure', key($requiredSchemes));
|
||||
}
|
||||
|
||||
return array('_route' => 'nonsecure');
|
||||
}
|
||||
|
||||
throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* ProjectUrlMatcher.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function match($pathinfo)
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($pathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$request = $this->request;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
$scheme = $context->getScheme();
|
||||
|
||||
if ('HEAD' === $requestMethod) {
|
||||
$canonicalMethod = 'GET';
|
||||
}
|
||||
|
||||
|
||||
if (0 === strpos($pathinfo, '/rootprefix')) {
|
||||
// static
|
||||
if ('/rootprefix/test' === $pathinfo) {
|
||||
return array('_route' => 'static');
|
||||
}
|
||||
|
||||
// dynamic
|
||||
if (preg_match('#^/rootprefix/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'dynamic')), array ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// with-condition
|
||||
if ('/with-condition' === $pathinfo && ($context->getMethod() == "GET")) {
|
||||
return array('_route' => 'with-condition');
|
||||
}
|
||||
|
||||
throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* ProjectUrlMatcher.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function match($pathinfo)
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($pathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$request = $this->request;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
$scheme = $context->getScheme();
|
||||
|
||||
if ('HEAD' === $requestMethod) {
|
||||
$canonicalMethod = 'GET';
|
||||
}
|
||||
|
||||
|
||||
// just_head
|
||||
if ('/just_head' === $pathinfo) {
|
||||
if ('HEAD' !== $requestMethod) {
|
||||
$allow[] = 'HEAD';
|
||||
goto not_just_head;
|
||||
}
|
||||
|
||||
return array('_route' => 'just_head');
|
||||
}
|
||||
not_just_head:
|
||||
|
||||
// head_and_get
|
||||
if ('/head_and_get' === $pathinfo) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow[] = 'GET';
|
||||
goto not_head_and_get;
|
||||
}
|
||||
|
||||
return array('_route' => 'head_and_get');
|
||||
}
|
||||
not_head_and_get:
|
||||
|
||||
// post_and_head
|
||||
if ('/post_and_get' === $pathinfo) {
|
||||
if (!in_array($requestMethod, array('POST', 'HEAD'))) {
|
||||
$allow = array_merge($allow, array('POST', 'HEAD'));
|
||||
goto not_post_and_head;
|
||||
}
|
||||
|
||||
return array('_route' => 'post_and_head');
|
||||
}
|
||||
not_post_and_head:
|
||||
|
||||
if (0 === strpos($pathinfo, '/put_and_post')) {
|
||||
// put_and_post
|
||||
if ('/put_and_post' === $pathinfo) {
|
||||
if (!in_array($requestMethod, array('PUT', 'POST'))) {
|
||||
$allow = array_merge($allow, array('PUT', 'POST'));
|
||||
goto not_put_and_post;
|
||||
}
|
||||
|
||||
return array('_route' => 'put_and_post');
|
||||
}
|
||||
not_put_and_post:
|
||||
|
||||
// put_and_get_and_head
|
||||
if ('/put_and_post' === $pathinfo) {
|
||||
if (!in_array($canonicalMethod, array('PUT', 'GET'))) {
|
||||
$allow = array_merge($allow, array('PUT', 'GET'));
|
||||
goto not_put_and_get_and_head;
|
||||
}
|
||||
|
||||
return array('_route' => 'put_and_get_and_head');
|
||||
}
|
||||
not_put_and_get_and_head:
|
||||
|
||||
}
|
||||
|
||||
throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* ProjectUrlMatcher.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function match($pathinfo)
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($pathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$request = $this->request;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
$scheme = $context->getScheme();
|
||||
|
||||
if ('HEAD' === $requestMethod) {
|
||||
$canonicalMethod = 'GET';
|
||||
}
|
||||
|
||||
|
||||
if (0 === strpos($pathinfo, '/a')) {
|
||||
// a_first
|
||||
if ('/a/11' === $pathinfo) {
|
||||
return array('_route' => 'a_first');
|
||||
}
|
||||
|
||||
// a_second
|
||||
if ('/a/22' === $pathinfo) {
|
||||
return array('_route' => 'a_second');
|
||||
}
|
||||
|
||||
// a_third
|
||||
if ('/a/333' === $pathinfo) {
|
||||
return array('_route' => 'a_third');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// a_wildcard
|
||||
if (preg_match('#^/(?P<param>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'a_wildcard')), array ());
|
||||
}
|
||||
|
||||
if (0 === strpos($pathinfo, '/a')) {
|
||||
// a_fourth
|
||||
if ('/a/44' === $trimmedPathinfo) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'a_fourth');
|
||||
}
|
||||
|
||||
return array('_route' => 'a_fourth');
|
||||
}
|
||||
|
||||
// a_fifth
|
||||
if ('/a/55' === $trimmedPathinfo) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'a_fifth');
|
||||
}
|
||||
|
||||
return array('_route' => 'a_fifth');
|
||||
}
|
||||
|
||||
// a_sixth
|
||||
if ('/a/66' === $trimmedPathinfo) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'a_sixth');
|
||||
}
|
||||
|
||||
return array('_route' => 'a_sixth');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// nested_wildcard
|
||||
if (0 === strpos($pathinfo, '/nested') && preg_match('#^/nested/(?P<param>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'nested_wildcard')), array ());
|
||||
}
|
||||
|
||||
if (0 === strpos($pathinfo, '/nested/group')) {
|
||||
// nested_a
|
||||
if ('/nested/group/a' === $trimmedPathinfo) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'nested_a');
|
||||
}
|
||||
|
||||
return array('_route' => 'nested_a');
|
||||
}
|
||||
|
||||
// nested_b
|
||||
if ('/nested/group/b' === $trimmedPathinfo) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'nested_b');
|
||||
}
|
||||
|
||||
return array('_route' => 'nested_b');
|
||||
}
|
||||
|
||||
// nested_c
|
||||
if ('/nested/group/c' === $trimmedPathinfo) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'nested_c');
|
||||
}
|
||||
|
||||
return array('_route' => 'nested_c');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
elseif (0 === strpos($pathinfo, '/slashed/group')) {
|
||||
// slashed_a
|
||||
if ('/slashed/group' === $trimmedPathinfo) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'slashed_a');
|
||||
}
|
||||
|
||||
return array('_route' => 'slashed_a');
|
||||
}
|
||||
|
||||
// slashed_b
|
||||
if ('/slashed/group/b' === $trimmedPathinfo) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'slashed_b');
|
||||
}
|
||||
|
||||
return array('_route' => 'slashed_b');
|
||||
}
|
||||
|
||||
// slashed_c
|
||||
if ('/slashed/group/c' === $trimmedPathinfo) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'slashed_c');
|
||||
}
|
||||
|
||||
return array('_route' => 'slashed_c');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* ProjectUrlMatcher.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function match($pathinfo)
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($pathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$request = $this->request;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
$scheme = $context->getScheme();
|
||||
|
||||
if ('HEAD' === $requestMethod) {
|
||||
$canonicalMethod = 'GET';
|
||||
}
|
||||
|
||||
|
||||
if (0 === strpos($pathinfo, '/trailing/simple')) {
|
||||
// simple_trailing_slash_no_methods
|
||||
if ('/trailing/simple/no-methods/' === $pathinfo) {
|
||||
return array('_route' => 'simple_trailing_slash_no_methods');
|
||||
}
|
||||
|
||||
// simple_trailing_slash_GET_method
|
||||
if ('/trailing/simple/get-method/' === $pathinfo) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow[] = 'GET';
|
||||
goto not_simple_trailing_slash_GET_method;
|
||||
}
|
||||
|
||||
return array('_route' => 'simple_trailing_slash_GET_method');
|
||||
}
|
||||
not_simple_trailing_slash_GET_method:
|
||||
|
||||
// simple_trailing_slash_HEAD_method
|
||||
if ('/trailing/simple/head-method/' === $pathinfo) {
|
||||
if ('HEAD' !== $requestMethod) {
|
||||
$allow[] = 'HEAD';
|
||||
goto not_simple_trailing_slash_HEAD_method;
|
||||
}
|
||||
|
||||
return array('_route' => 'simple_trailing_slash_HEAD_method');
|
||||
}
|
||||
not_simple_trailing_slash_HEAD_method:
|
||||
|
||||
// simple_trailing_slash_POST_method
|
||||
if ('/trailing/simple/post-method/' === $pathinfo) {
|
||||
if ('POST' !== $canonicalMethod) {
|
||||
$allow[] = 'POST';
|
||||
goto not_simple_trailing_slash_POST_method;
|
||||
}
|
||||
|
||||
return array('_route' => 'simple_trailing_slash_POST_method');
|
||||
}
|
||||
not_simple_trailing_slash_POST_method:
|
||||
|
||||
}
|
||||
|
||||
elseif (0 === strpos($pathinfo, '/trailing/regex')) {
|
||||
// regex_trailing_slash_no_methods
|
||||
if (0 === strpos($pathinfo, '/trailing/regex/no-methods') && preg_match('#^/trailing/regex/no\\-methods/(?P<param>[^/]++)/$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_no_methods')), array ());
|
||||
}
|
||||
|
||||
// regex_trailing_slash_GET_method
|
||||
if (0 === strpos($pathinfo, '/trailing/regex/get-method') && preg_match('#^/trailing/regex/get\\-method/(?P<param>[^/]++)/$#s', $pathinfo, $matches)) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow[] = 'GET';
|
||||
goto not_regex_trailing_slash_GET_method;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_GET_method')), array ());
|
||||
}
|
||||
not_regex_trailing_slash_GET_method:
|
||||
|
||||
// regex_trailing_slash_HEAD_method
|
||||
if (0 === strpos($pathinfo, '/trailing/regex/head-method') && preg_match('#^/trailing/regex/head\\-method/(?P<param>[^/]++)/$#s', $pathinfo, $matches)) {
|
||||
if ('HEAD' !== $requestMethod) {
|
||||
$allow[] = 'HEAD';
|
||||
goto not_regex_trailing_slash_HEAD_method;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_HEAD_method')), array ());
|
||||
}
|
||||
not_regex_trailing_slash_HEAD_method:
|
||||
|
||||
// regex_trailing_slash_POST_method
|
||||
if (0 === strpos($pathinfo, '/trailing/regex/post-method') && preg_match('#^/trailing/regex/post\\-method/(?P<param>[^/]++)/$#s', $pathinfo, $matches)) {
|
||||
if ('POST' !== $canonicalMethod) {
|
||||
$allow[] = 'POST';
|
||||
goto not_regex_trailing_slash_POST_method;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_POST_method')), array ());
|
||||
}
|
||||
not_regex_trailing_slash_POST_method:
|
||||
|
||||
}
|
||||
|
||||
elseif (0 === strpos($pathinfo, '/not-trailing/simple')) {
|
||||
// simple_not_trailing_slash_no_methods
|
||||
if ('/not-trailing/simple/no-methods' === $pathinfo) {
|
||||
return array('_route' => 'simple_not_trailing_slash_no_methods');
|
||||
}
|
||||
|
||||
// simple_not_trailing_slash_GET_method
|
||||
if ('/not-trailing/simple/get-method' === $pathinfo) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow[] = 'GET';
|
||||
goto not_simple_not_trailing_slash_GET_method;
|
||||
}
|
||||
|
||||
return array('_route' => 'simple_not_trailing_slash_GET_method');
|
||||
}
|
||||
not_simple_not_trailing_slash_GET_method:
|
||||
|
||||
// simple_not_trailing_slash_HEAD_method
|
||||
if ('/not-trailing/simple/head-method' === $pathinfo) {
|
||||
if ('HEAD' !== $requestMethod) {
|
||||
$allow[] = 'HEAD';
|
||||
goto not_simple_not_trailing_slash_HEAD_method;
|
||||
}
|
||||
|
||||
return array('_route' => 'simple_not_trailing_slash_HEAD_method');
|
||||
}
|
||||
not_simple_not_trailing_slash_HEAD_method:
|
||||
|
||||
// simple_not_trailing_slash_POST_method
|
||||
if ('/not-trailing/simple/post-method' === $pathinfo) {
|
||||
if ('POST' !== $canonicalMethod) {
|
||||
$allow[] = 'POST';
|
||||
goto not_simple_not_trailing_slash_POST_method;
|
||||
}
|
||||
|
||||
return array('_route' => 'simple_not_trailing_slash_POST_method');
|
||||
}
|
||||
not_simple_not_trailing_slash_POST_method:
|
||||
|
||||
}
|
||||
|
||||
elseif (0 === strpos($pathinfo, '/not-trailing/regex')) {
|
||||
// regex_not_trailing_slash_no_methods
|
||||
if (0 === strpos($pathinfo, '/not-trailing/regex/no-methods') && preg_match('#^/not\\-trailing/regex/no\\-methods/(?P<param>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_no_methods')), array ());
|
||||
}
|
||||
|
||||
// regex_not_trailing_slash_GET_method
|
||||
if (0 === strpos($pathinfo, '/not-trailing/regex/get-method') && preg_match('#^/not\\-trailing/regex/get\\-method/(?P<param>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow[] = 'GET';
|
||||
goto not_regex_not_trailing_slash_GET_method;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_GET_method')), array ());
|
||||
}
|
||||
not_regex_not_trailing_slash_GET_method:
|
||||
|
||||
// regex_not_trailing_slash_HEAD_method
|
||||
if (0 === strpos($pathinfo, '/not-trailing/regex/head-method') && preg_match('#^/not\\-trailing/regex/head\\-method/(?P<param>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
if ('HEAD' !== $requestMethod) {
|
||||
$allow[] = 'HEAD';
|
||||
goto not_regex_not_trailing_slash_HEAD_method;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_HEAD_method')), array ());
|
||||
}
|
||||
not_regex_not_trailing_slash_HEAD_method:
|
||||
|
||||
// regex_not_trailing_slash_POST_method
|
||||
if (0 === strpos($pathinfo, '/not-trailing/regex/post-method') && preg_match('#^/not\\-trailing/regex/post\\-method/(?P<param>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
if ('POST' !== $canonicalMethod) {
|
||||
$allow[] = 'POST';
|
||||
goto not_regex_not_trailing_slash_POST_method;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_POST_method')), array ());
|
||||
}
|
||||
not_regex_not_trailing_slash_POST_method:
|
||||
|
||||
}
|
||||
|
||||
throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* ProjectUrlMatcher.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function match($pathinfo)
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($pathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$request = $this->request;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
$scheme = $context->getScheme();
|
||||
|
||||
if ('HEAD' === $requestMethod) {
|
||||
$canonicalMethod = 'GET';
|
||||
}
|
||||
|
||||
|
||||
if (0 === strpos($pathinfo, '/trailing/simple')) {
|
||||
// simple_trailing_slash_no_methods
|
||||
if ('/trailing/simple/no-methods' === $trimmedPathinfo) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'simple_trailing_slash_no_methods');
|
||||
}
|
||||
|
||||
return array('_route' => 'simple_trailing_slash_no_methods');
|
||||
}
|
||||
|
||||
// simple_trailing_slash_GET_method
|
||||
if ('/trailing/simple/get-method' === $trimmedPathinfo) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow[] = 'GET';
|
||||
goto not_simple_trailing_slash_GET_method;
|
||||
}
|
||||
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'simple_trailing_slash_GET_method');
|
||||
}
|
||||
|
||||
return array('_route' => 'simple_trailing_slash_GET_method');
|
||||
}
|
||||
not_simple_trailing_slash_GET_method:
|
||||
|
||||
// simple_trailing_slash_HEAD_method
|
||||
if ('/trailing/simple/head-method' === $trimmedPathinfo) {
|
||||
if ('HEAD' !== $requestMethod) {
|
||||
$allow[] = 'HEAD';
|
||||
goto not_simple_trailing_slash_HEAD_method;
|
||||
}
|
||||
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'simple_trailing_slash_HEAD_method');
|
||||
}
|
||||
|
||||
return array('_route' => 'simple_trailing_slash_HEAD_method');
|
||||
}
|
||||
not_simple_trailing_slash_HEAD_method:
|
||||
|
||||
// simple_trailing_slash_POST_method
|
||||
if ('/trailing/simple/post-method/' === $pathinfo) {
|
||||
if ('POST' !== $canonicalMethod) {
|
||||
$allow[] = 'POST';
|
||||
goto not_simple_trailing_slash_POST_method;
|
||||
}
|
||||
|
||||
return array('_route' => 'simple_trailing_slash_POST_method');
|
||||
}
|
||||
not_simple_trailing_slash_POST_method:
|
||||
|
||||
}
|
||||
|
||||
elseif (0 === strpos($pathinfo, '/trailing/regex')) {
|
||||
// regex_trailing_slash_no_methods
|
||||
if (0 === strpos($pathinfo, '/trailing/regex/no-methods') && preg_match('#^/trailing/regex/no\\-methods/(?P<param>[^/]++)/?$#s', $pathinfo, $matches)) {
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'regex_trailing_slash_no_methods');
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_no_methods')), array ());
|
||||
}
|
||||
|
||||
// regex_trailing_slash_GET_method
|
||||
if (0 === strpos($pathinfo, '/trailing/regex/get-method') && preg_match('#^/trailing/regex/get\\-method/(?P<param>[^/]++)/?$#s', $pathinfo, $matches)) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow[] = 'GET';
|
||||
goto not_regex_trailing_slash_GET_method;
|
||||
}
|
||||
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'regex_trailing_slash_GET_method');
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_GET_method')), array ());
|
||||
}
|
||||
not_regex_trailing_slash_GET_method:
|
||||
|
||||
// regex_trailing_slash_HEAD_method
|
||||
if (0 === strpos($pathinfo, '/trailing/regex/head-method') && preg_match('#^/trailing/regex/head\\-method/(?P<param>[^/]++)/?$#s', $pathinfo, $matches)) {
|
||||
if ('HEAD' !== $requestMethod) {
|
||||
$allow[] = 'HEAD';
|
||||
goto not_regex_trailing_slash_HEAD_method;
|
||||
}
|
||||
|
||||
if (substr($pathinfo, -1) !== '/') {
|
||||
return $this->redirect($pathinfo.'/', 'regex_trailing_slash_HEAD_method');
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_HEAD_method')), array ());
|
||||
}
|
||||
not_regex_trailing_slash_HEAD_method:
|
||||
|
||||
// regex_trailing_slash_POST_method
|
||||
if (0 === strpos($pathinfo, '/trailing/regex/post-method') && preg_match('#^/trailing/regex/post\\-method/(?P<param>[^/]++)/$#s', $pathinfo, $matches)) {
|
||||
if ('POST' !== $canonicalMethod) {
|
||||
$allow[] = 'POST';
|
||||
goto not_regex_trailing_slash_POST_method;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_POST_method')), array ());
|
||||
}
|
||||
not_regex_trailing_slash_POST_method:
|
||||
|
||||
}
|
||||
|
||||
elseif (0 === strpos($pathinfo, '/not-trailing/simple')) {
|
||||
// simple_not_trailing_slash_no_methods
|
||||
if ('/not-trailing/simple/no-methods' === $pathinfo) {
|
||||
return array('_route' => 'simple_not_trailing_slash_no_methods');
|
||||
}
|
||||
|
||||
// simple_not_trailing_slash_GET_method
|
||||
if ('/not-trailing/simple/get-method' === $pathinfo) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow[] = 'GET';
|
||||
goto not_simple_not_trailing_slash_GET_method;
|
||||
}
|
||||
|
||||
return array('_route' => 'simple_not_trailing_slash_GET_method');
|
||||
}
|
||||
not_simple_not_trailing_slash_GET_method:
|
||||
|
||||
// simple_not_trailing_slash_HEAD_method
|
||||
if ('/not-trailing/simple/head-method' === $pathinfo) {
|
||||
if ('HEAD' !== $requestMethod) {
|
||||
$allow[] = 'HEAD';
|
||||
goto not_simple_not_trailing_slash_HEAD_method;
|
||||
}
|
||||
|
||||
return array('_route' => 'simple_not_trailing_slash_HEAD_method');
|
||||
}
|
||||
not_simple_not_trailing_slash_HEAD_method:
|
||||
|
||||
// simple_not_trailing_slash_POST_method
|
||||
if ('/not-trailing/simple/post-method' === $pathinfo) {
|
||||
if ('POST' !== $canonicalMethod) {
|
||||
$allow[] = 'POST';
|
||||
goto not_simple_not_trailing_slash_POST_method;
|
||||
}
|
||||
|
||||
return array('_route' => 'simple_not_trailing_slash_POST_method');
|
||||
}
|
||||
not_simple_not_trailing_slash_POST_method:
|
||||
|
||||
}
|
||||
|
||||
elseif (0 === strpos($pathinfo, '/not-trailing/regex')) {
|
||||
// regex_not_trailing_slash_no_methods
|
||||
if (0 === strpos($pathinfo, '/not-trailing/regex/no-methods') && preg_match('#^/not\\-trailing/regex/no\\-methods/(?P<param>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_no_methods')), array ());
|
||||
}
|
||||
|
||||
// regex_not_trailing_slash_GET_method
|
||||
if (0 === strpos($pathinfo, '/not-trailing/regex/get-method') && preg_match('#^/not\\-trailing/regex/get\\-method/(?P<param>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow[] = 'GET';
|
||||
goto not_regex_not_trailing_slash_GET_method;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_GET_method')), array ());
|
||||
}
|
||||
not_regex_not_trailing_slash_GET_method:
|
||||
|
||||
// regex_not_trailing_slash_HEAD_method
|
||||
if (0 === strpos($pathinfo, '/not-trailing/regex/head-method') && preg_match('#^/not\\-trailing/regex/head\\-method/(?P<param>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
if ('HEAD' !== $requestMethod) {
|
||||
$allow[] = 'HEAD';
|
||||
goto not_regex_not_trailing_slash_HEAD_method;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_HEAD_method')), array ());
|
||||
}
|
||||
not_regex_not_trailing_slash_HEAD_method:
|
||||
|
||||
// regex_not_trailing_slash_POST_method
|
||||
if (0 === strpos($pathinfo, '/not-trailing/regex/post-method') && preg_match('#^/not\\-trailing/regex/post\\-method/(?P<param>[^/]++)$#s', $pathinfo, $matches)) {
|
||||
if ('POST' !== $canonicalMethod) {
|
||||
$allow[] = 'POST';
|
||||
goto not_regex_not_trailing_slash_POST_method;
|
||||
}
|
||||
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_POST_method')), array ());
|
||||
}
|
||||
not_regex_not_trailing_slash_POST_method:
|
||||
|
||||
}
|
||||
|
||||
throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user