update patch 1

This commit is contained in:
fiqhpratama
2024-12-02 01:18:34 +07:00
parent 453d9bb470
commit 25026b0a85
3380 changed files with 103529 additions and 363623 deletions
+3 -4
View File
@@ -15,12 +15,11 @@ use Symfony\Component\Routing\Exception\InvalidArgumentException;
class Alias
{
private string $id;
private array $deprecation = [];
public function __construct(string $id)
{
$this->id = $id;
public function __construct(
private string $id,
) {
}
public function withId(string $id): static
+6
View File
@@ -1,6 +1,12 @@
CHANGELOG
=========
7.2
---
* Add the `Requirement::UID_RFC9562` constant to validate UUIDs in the RFC 9562 format
* Deprecate the `AttributeClassLoader::$routeAnnotationClass` property
7.1
---
@@ -29,7 +29,7 @@ class MissingMandatoryParametersException extends \InvalidArgumentException impl
{
$this->routeName = $routeName;
$this->missingParameters = $missingParameters;
$message = sprintf('Some mandatory parameters are missing ("%s") to generate a URL for route "%s".', implode('", "', $missingParameters), $routeName);
$message = \sprintf('Some mandatory parameters are missing ("%s") to generate a URL for route "%s".', implode('", "', $missingParameters), $routeName);
parent::__construct($message, $code, $previous);
}
@@ -15,6 +15,6 @@ 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)));
parent::__construct(\sprintf('Circular reference detected for route "%s", path: "%s".', $routeId, implode(' -> ', $path)));
}
}
+1 -1
View File
@@ -49,7 +49,7 @@ class CompiledUrlGenerator extends UrlGenerator
}
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));
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 => []];
@@ -69,7 +69,7 @@ class CompiledUrlGeneratorDumper extends GeneratorDumper
}
if (null === $target = $routes->get($currentId)) {
throw new RouteNotFoundException(sprintf('Target route "%s" for alias "%s" does not exist.', $currentId, $name));
throw new RouteNotFoundException(\sprintf('Target route "%s" for alias "%s" does not exist.', $currentId, $name));
}
$compiledTarget = $target->compile();
@@ -109,11 +109,11 @@ EOF;
{
$routes = '';
foreach ($this->getCompiledRoutes() as $name => $properties) {
$routes .= sprintf("\n '%s' => %s,", $name, CompiledUrlMatcherDumper::export($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));
$routes .= \sprintf("\n '%s' => %s,", $alias, CompiledUrlMatcherDumper::export($properties));
}
return $routes;
+2 -2
View File
@@ -115,7 +115,7 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
}
if (null === $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));
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
@@ -266,7 +266,7 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
if ($vars = get_object_vars($v)) {
array_walk_recursive($vars, $caster);
$v = $vars;
} elseif (method_exists($v, '__toString')) {
} elseif ($v instanceof \Stringable) {
$v = (string) $v;
}
}
+76 -55
View File
@@ -14,7 +14,7 @@ namespace Symfony\Component\Routing\Loader;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolverInterface;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Routing\Attribute\Route as RouteAnnotation;
use Symfony\Component\Routing\Attribute\Route as RouteAttribute;
use Symfony\Component\Routing\Exception\LogicException;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
@@ -53,7 +53,11 @@ use Symfony\Component\Routing\RouteCollection;
*/
abstract class AttributeClassLoader implements LoaderInterface
{
protected string $routeAnnotationClass = RouteAnnotation::class;
/**
* @deprecated since Symfony 7.2, use "setRouteAttributeClass()" instead.
*/
protected string $routeAnnotationClass = RouteAttribute::class;
private string $routeAttributeClass = RouteAttribute::class;
protected int $defaultRouteIndex = 0;
public function __construct(
@@ -62,11 +66,24 @@ abstract class AttributeClassLoader implements LoaderInterface
}
/**
* @deprecated since Symfony 7.2, use "setRouteAttributeClass(string $class)" instead
*
* Sets the annotation class to read route properties from.
*/
public function setRouteAnnotationClass(string $class): void
{
trigger_deprecation('symfony/routing', '7.2', 'The "%s()" method is deprecated, use "%s::setRouteAttributeClass()" instead.', __METHOD__, self::class);
$this->setRouteAttributeClass($class);
}
/**
* Sets the attribute class to read route properties from.
*/
public function setRouteAttributeClass(string $class): void
{
$this->routeAnnotationClass = $class;
$this->routeAttributeClass = $class;
}
/**
@@ -75,12 +92,12 @@ abstract class AttributeClassLoader implements LoaderInterface
public function load(mixed $class, ?string $type = null): RouteCollection
{
if (!class_exists($class)) {
throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
throw new \InvalidArgumentException(\sprintf('Class "%s" does not exist.', $class));
}
$class = new \ReflectionClass($class);
if ($class->isAbstract()) {
throw new \InvalidArgumentException(sprintf('Attributes from class "%s" cannot be read as it is abstract.', $class->getName()));
throw new \InvalidArgumentException(\sprintf('Attributes from class "%s" cannot be read as it is abstract.', $class->getName()));
}
$globals = $this->getGlobals($class);
@@ -93,8 +110,8 @@ abstract class AttributeClassLoader implements LoaderInterface
foreach ($class->getMethods() as $method) {
$this->defaultRouteIndex = 0;
$routeNamesBefore = array_keys($collection->all());
foreach ($this->getAnnotations($method) as $annot) {
$this->addRoute($collection, $annot, $globals, $class, $method);
foreach ($this->getAttributes($method) as $attr) {
$this->addRoute($collection, $attr, $globals, $class, $method);
if ('__invoke' === $method->name) {
$fqcnAlias = true;
}
@@ -102,15 +119,15 @@ abstract class AttributeClassLoader implements LoaderInterface
if (1 === $collection->count() - \count($routeNamesBefore)) {
$newRouteName = current(array_diff(array_keys($collection->all()), $routeNamesBefore));
if ($newRouteName !== $aliasName = sprintf('%s::%s', $class->name, $method->name)) {
if ($newRouteName !== $aliasName = \sprintf('%s::%s', $class->name, $method->name)) {
$collection->addAlias($aliasName, $newRouteName);
}
}
}
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'));
foreach ($this->getAttributes($class) as $attr) {
$this->addRoute($collection, $attr, $globals, $class, $class->getMethod('__invoke'));
$fqcnAlias = true;
}
}
@@ -120,7 +137,7 @@ abstract class AttributeClassLoader implements LoaderInterface
$collection->addAlias($class->name, $invokeRouteName);
}
if ($invokeRouteName !== $aliasName = sprintf('%s::__invoke', $class->name)) {
if ($invokeRouteName !== $aliasName = \sprintf('%s::__invoke', $class->name)) {
$collection->addAlias($aliasName, $invokeRouteName);
}
}
@@ -129,36 +146,36 @@ abstract class AttributeClassLoader implements LoaderInterface
}
/**
* @param RouteAnnotation $annot or an object that exposes a similar interface
* @param RouteAttribute $attr or an object that exposes a similar interface
*/
protected function addRoute(RouteCollection $collection, object $annot, array $globals, \ReflectionClass $class, \ReflectionMethod $method): void
protected function addRoute(RouteCollection $collection, object $attr, array $globals, \ReflectionClass $class, \ReflectionMethod $method): void
{
if ($annot->getEnv() && $annot->getEnv() !== $this->env) {
if ($attr->getEnv() && $attr->getEnv() !== $this->env) {
return;
}
$name = $annot->getName() ?? $this->getDefaultRouteName($class, $method);
$name = $attr->getName() ?? $this->getDefaultRouteName($class, $method);
$name = $globals['name'].$name;
$requirements = $annot->getRequirements();
$requirements = $attr->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()));
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()));
}
}
$defaults = array_replace($globals['defaults'], $annot->getDefaults());
$defaults = array_replace($globals['defaults'], $attr->getDefaults());
$requirements = array_replace($globals['requirements'], $requirements);
$options = array_replace($globals['options'], $annot->getOptions());
$schemes = array_unique(array_merge($globals['schemes'], $annot->getSchemes()));
$methods = array_unique(array_merge($globals['methods'], $annot->getMethods()));
$options = array_replace($globals['options'], $attr->getOptions());
$schemes = array_unique(array_merge($globals['schemes'], $attr->getSchemes()));
$methods = array_unique(array_merge($globals['methods'], $attr->getMethods()));
$host = $annot->getHost() ?? $globals['host'];
$condition = $annot->getCondition() ?? $globals['condition'];
$priority = $annot->getPriority() ?? $globals['priority'];
$host = $attr->getHost() ?? $globals['host'];
$condition = $attr->getCondition() ?? $globals['condition'];
$priority = $attr->getPriority() ?? $globals['priority'];
$path = $annot->getLocalizedPaths() ?: $annot->getPath();
$path = $attr->getLocalizedPaths() ?: $attr->getPath();
$prefix = $globals['localized_paths'] ?: $globals['path'];
$paths = [];
@@ -168,11 +185,11 @@ abstract class AttributeClassLoader implements LoaderInterface
$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))));
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));
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;
@@ -191,7 +208,7 @@ abstract class AttributeClassLoader implements LoaderInterface
continue;
}
foreach ($paths as $locale => $path) {
if (preg_match(sprintf('/\{%s(?:<.*?>)?\}/', preg_quote($param->name)), $path)) {
if (preg_match(\sprintf('/\{%s(?:<.*?>)?\}/', preg_quote($param->name)), $path)) {
if (\is_scalar($defaultValue = $param->getDefaultValue()) || null === $defaultValue) {
$defaults[$param->name] = $defaultValue;
} elseif ($defaultValue instanceof \BackedEnum) {
@@ -204,7 +221,7 @@ abstract class AttributeClassLoader implements LoaderInterface
foreach ($paths as $locale => $path) {
$route = $this->createRoute($path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
$this->configureRoute($route, $class, $method, $annot);
$this->configureRoute($route, $class, $method, $attr);
if (0 !== $locale) {
$route->setDefault('_locale', $locale);
$route->setRequirement('_locale', preg_quote($locale));
@@ -227,7 +244,7 @@ abstract class AttributeClassLoader implements LoaderInterface
public function getResolver(): LoaderResolverInterface
{
throw new LogicException(sprintf('The "%s()" method must not be called.', __METHOD__));
throw new LogicException(\sprintf('The "%s()" method must not be called.', __METHOD__));
}
/**
@@ -254,53 +271,54 @@ abstract class AttributeClassLoader implements LoaderInterface
{
$globals = $this->resetGlobals();
// to be replaced in Symfony 8.0 by $this->routeAttributeClass
if ($attribute = $class->getAttributes($this->routeAnnotationClass, \ReflectionAttribute::IS_INSTANCEOF)[0] ?? null) {
$annot = $attribute->newInstance();
$attr = $attribute->newInstance();
if (null !== $annot->getName()) {
$globals['name'] = $annot->getName();
if (null !== $attr->getName()) {
$globals['name'] = $attr->getName();
}
if (null !== $annot->getPath()) {
$globals['path'] = $annot->getPath();
if (null !== $attr->getPath()) {
$globals['path'] = $attr->getPath();
}
$globals['localized_paths'] = $annot->getLocalizedPaths();
$globals['localized_paths'] = $attr->getLocalizedPaths();
if (null !== $annot->getRequirements()) {
$globals['requirements'] = $annot->getRequirements();
if (null !== $attr->getRequirements()) {
$globals['requirements'] = $attr->getRequirements();
}
if (null !== $annot->getOptions()) {
$globals['options'] = $annot->getOptions();
if (null !== $attr->getOptions()) {
$globals['options'] = $attr->getOptions();
}
if (null !== $annot->getDefaults()) {
$globals['defaults'] = $annot->getDefaults();
if (null !== $attr->getDefaults()) {
$globals['defaults'] = $attr->getDefaults();
}
if (null !== $annot->getSchemes()) {
$globals['schemes'] = $annot->getSchemes();
if (null !== $attr->getSchemes()) {
$globals['schemes'] = $attr->getSchemes();
}
if (null !== $annot->getMethods()) {
$globals['methods'] = $annot->getMethods();
if (null !== $attr->getMethods()) {
$globals['methods'] = $attr->getMethods();
}
if (null !== $annot->getHost()) {
$globals['host'] = $annot->getHost();
if (null !== $attr->getHost()) {
$globals['host'] = $attr->getHost();
}
if (null !== $annot->getCondition()) {
$globals['condition'] = $annot->getCondition();
if (null !== $attr->getCondition()) {
$globals['condition'] = $attr->getCondition();
}
$globals['priority'] = $annot->getPriority() ?? 0;
$globals['env'] = $annot->getEnv();
$globals['priority'] = $attr->getPriority() ?? 0;
$globals['env'] = $attr->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()));
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()));
}
}
}
@@ -332,15 +350,18 @@ abstract class AttributeClassLoader implements LoaderInterface
}
/**
* @param RouteAttribute $attr or an object that exposes a similar interface
*
* @return void
*/
abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot);
abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $attr);
/**
* @return iterable<int, RouteAnnotation>
* @return iterable<int, RouteAttribute>
*/
private function getAnnotations(\ReflectionClass|\ReflectionMethod $reflection): iterable
private function getAttributes(\ReflectionClass|\ReflectionMethod $reflection): iterable
{
// to be replaced in Symfony 8.0 by $this->routeAttributeClass
foreach ($reflection->getAttributes($this->routeAnnotationClass, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
yield $attribute->newInstance();
}
+1 -1
View File
@@ -76,7 +76,7 @@ class AttributeFileLoader extends FileLoader
$tokens = token_get_all(file_get_contents($file));
if (1 === \count($tokens) && \T_INLINE_HTML === $tokens[0][0]) {
throw new \InvalidArgumentException(sprintf('The file "%s" does not contain PHP code. Did you forget to add the "<?php" start tag at the beginning of the file?', $file));
throw new \InvalidArgumentException(\sprintf('The file "%s" does not contain PHP code. Did you forget to add the "<?php" start tag at the beginning of the file?', $file));
}
$nsTokens = [\T_NS_SEPARATOR => true, \T_STRING => true];
@@ -79,11 +79,11 @@ class CollectionConfigurator
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))));
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));
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;
@@ -28,6 +28,7 @@ trait HostTrait
foreach ($routes->all() as $name => $route) {
if (null === $locale = $route->getDefault('_locale')) {
$priority = $routes->getPriority($name) ?? 0;
$routes->remove($name);
foreach ($hosts as $locale => $host) {
$localizedRoute = clone $route;
@@ -35,14 +36,14 @@ trait HostTrait
$localizedRoute->setRequirement('_locale', preg_quote($locale));
$localizedRoute->setDefault('_canonical_route', $name);
$localizedRoute->setHost($host);
$routes->add($name.'.'.$locale, $localizedRoute);
$routes->add($name.'.'.$locale, $localizedRoute, $priority);
}
} elseif (!isset($hosts[$locale])) {
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding host in its parent collection.', $name, $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);
$routes->add($name, $route, $routes->getPriority($name) ?? 0);
}
}
}
@@ -37,11 +37,11 @@ trait LocalizedRouteTrait
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))));
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));
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;
@@ -40,7 +40,7 @@ trait PrefixTrait
$routes->add($name.'.'.$locale, $localizedRoute, $priority);
}
} elseif (!isset($prefix[$locale])) {
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $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, $routes->getPriority($name) ?? 0);
+3 -7
View File
@@ -36,7 +36,7 @@ abstract class ObjectLoader extends Loader
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'));
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);
@@ -44,12 +44,8 @@ abstract class ObjectLoader extends Loader
$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));
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);
@@ -57,7 +53,7 @@ abstract class ObjectLoader extends Loader
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));
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
+15 -15
View File
@@ -88,7 +88,7 @@ class XmlFileLoader extends FileLoader
}
break;
default:
throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "route" or "import".', $node->localName, $path));
throw new \InvalidArgumentException(\sprintf('Unknown tag "%s" used in file "%s". Expected "route" or "import".', $node->localName, $path));
}
}
@@ -105,7 +105,7 @@ class XmlFileLoader extends FileLoader
protected function parseRoute(RouteCollection $collection, \DOMElement $node, string $path): void
{
if ('' === $id = $node->getAttribute('id')) {
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" attribute.', $path));
throw new \InvalidArgumentException(\sprintf('The <route> element in file "%s" must have an "id" attribute.', $path));
}
if ('' !== $alias = $node->getAttribute('alias')) {
@@ -124,11 +124,11 @@ class XmlFileLoader extends FileLoader
[$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));
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));
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'));
@@ -161,7 +161,7 @@ class XmlFileLoader extends FileLoader
}
if (!$resource) {
throw new \InvalidArgumentException(sprintf('The <import> element in file "%s" must have a "resource" attribute or element.', $path));
throw new \InvalidArgumentException(\sprintf('The <import> element in file "%s" must have a "resource" attribute or element.', $path));
}
$type = $node->getAttribute('type');
@@ -174,7 +174,7 @@ class XmlFileLoader extends FileLoader
[$defaults, $requirements, $options, $condition, /* $paths */, $prefixes, $hosts] = $this->parseConfigs($node, $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));
throw new \InvalidArgumentException(\sprintf('The <route> element in file "%s" must not have both a "prefix" attribute and <prefix> child nodes.', $path));
}
$exclude = [];
@@ -288,15 +288,15 @@ class XmlFileLoader extends FileLoader
case 'resource':
break;
default:
throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "default", "requirement", "option" or "condition".', $n->localName, $path));
throw new \InvalidArgumentException(\sprintf('Unknown tag "%s" used in file "%s". Expected "default", "requirement", "option" or "condition".', $n->localName, $path));
}
}
if ($controller = $node->getAttribute('controller')) {
if (isset($defaults['_controller'])) {
$name = $node->hasAttribute('id') ? sprintf('"%s".', $node->getAttribute('id')) : sprintf('the "%s" tag.', $node->tagName);
$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);
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;
@@ -312,9 +312,9 @@ class XmlFileLoader extends FileLoader
}
if ($stateless = $node->getAttribute('stateless')) {
if (isset($defaults['_stateless'])) {
$name = $node->hasAttribute('id') ? sprintf('"%s".', $node->getAttribute('id')) : sprintf('the "%s" tag.', $node->tagName);
$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);
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);
@@ -410,7 +410,7 @@ class XmlFileLoader extends FileLoader
return $map;
default:
throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "bool", "int", "float", "string", "list", or "map".', $node->localName, $path));
throw new \InvalidArgumentException(\sprintf('Unknown tag "%s" used in file "%s". Expected "bool", "int", "float", "string", "list", or "map".', $node->localName, $path));
}
}
@@ -438,7 +438,7 @@ class XmlFileLoader extends FileLoader
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));
throw new \InvalidArgumentException(\sprintf('Invalid child element "%s" defined for alias "%s" in "%s".', $child->localName, $node->getAttribute('id'), $path));
}
$deprecatedNode = $child;
@@ -449,10 +449,10 @@ class XmlFileLoader extends FileLoader
}
if (!$deprecatedNode->hasAttribute('package')) {
throw new \InvalidArgumentException(sprintf('The <deprecated> element in file "%s" must have a "package" attribute.', $path));
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));
throw new \InvalidArgumentException(\sprintf('The <deprecated> element in file "%s" must have a "version" attribute.', $path));
}
return [
+15 -15
View File
@@ -46,11 +46,11 @@ class YamlFileLoader extends FileLoader
$path = $this->locator->locate($file);
if (!stream_is_local($path)) {
throw new \InvalidArgumentException(sprintf('This is not a local file "%s".', $path));
throw new \InvalidArgumentException(\sprintf('This is not a local file "%s".', $path));
}
if (!file_exists($path)) {
throw new \InvalidArgumentException(sprintf('File "%s" not found.', $path));
throw new \InvalidArgumentException(\sprintf('File "%s" not found.', $path));
}
$this->yamlParser ??= new YamlParser();
@@ -58,7 +58,7 @@ class YamlFileLoader extends FileLoader
try {
$parsedConfig = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT);
} catch (ParseException $e) {
throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: ', $path).$e->getMessage(), 0, $e);
throw new \InvalidArgumentException(\sprintf('The file "%s" does not contain valid YAML: ', $path).$e->getMessage(), 0, $e);
}
$collection = new RouteCollection();
@@ -71,7 +71,7 @@ class YamlFileLoader extends FileLoader
// not an array
if (!\is_array($parsedConfig)) {
throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $path));
throw new \InvalidArgumentException(\sprintf('The file "%s" must contain a YAML array.', $path));
}
foreach ($parsedConfig as $name => $config) {
@@ -135,7 +135,7 @@ class YamlFileLoader extends FileLoader
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));
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));
}
}
@@ -244,7 +244,7 @@ class YamlFileLoader extends FileLoader
protected function validate(mixed $config, string $name, string $path): void
{
if (!\is_array($config)) {
throw new \InvalidArgumentException(sprintf('The definition of "%s" in "%s" must be a YAML array.', $name, $path));
throw new \InvalidArgumentException(\sprintf('The definition of "%s" in "%s" must be a YAML array.', $name, $path));
}
if (isset($config['alias'])) {
$this->validateAlias($config, $name, $path);
@@ -252,22 +252,22 @@ class YamlFileLoader extends FileLoader
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)));
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));
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));
throw new \InvalidArgumentException(\sprintf('The routing file "%s" must not specify both the "stateless" key and the defaults key "_stateless" for "%s".', $path, $name));
}
}
@@ -279,16 +279,16 @@ class YamlFileLoader extends FileLoader
{
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));
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));
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));
throw new \InvalidArgumentException(\sprintf('The routing file "%s" must specify the attribute "version" of the "deprecated" option for "%s".', $path, $name));
}
}
}
@@ -134,7 +134,7 @@ EOF;
$code .= '[ // $staticRoutes'."\n";
foreach ($staticRoutes as $path => $routes) {
$code .= sprintf(" %s => [\n", self::export($path));
$code .= \sprintf(" %s => [\n", self::export($path));
foreach ($routes as $route) {
$code .= vsprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", array_map([__CLASS__, 'export'], $route));
}
@@ -142,11 +142,11 @@ EOF;
}
$code .= "],\n";
$code .= sprintf("[ // \$regexpList%s\n],\n", $regexpCode);
$code .= \sprintf("[ // \$regexpList%s\n],\n", $regexpCode);
$code .= '[ // $dynamicRoutes'."\n";
foreach ($dynamicRoutes as $path => $routes) {
$code .= sprintf(" %s => [\n", self::export($path));
$code .= \sprintf(" %s => [\n", self::export($path));
foreach ($routes as $route) {
$code .= vsprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", array_map([__CLASS__, 'export'], $route));
}
@@ -399,7 +399,7 @@ EOF;
$state->mark += 3 + $state->markTail + \strlen($regex) - $prefixLen;
$state->markTail = 2 + \strlen($state->mark);
$rx = sprintf('|%s(*:%s)', substr($regex, $prefixLen), $state->mark);
$rx = \sprintf('|%s(*:%s)', substr($regex, $prefixLen), $state->mark);
$code .= "\n .".self::export($rx);
$state->regex .= $rx;
@@ -42,7 +42,7 @@ trait CompiledUrlMatcherTrait
throw new MethodNotAllowedException(array_keys($allow));
}
if (!$this instanceof RedirectableUrlMatcherInterface) {
throw new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo));
throw new ResourceNotFoundException(\sprintf('No routes found for "%s".', $pathinfo));
}
if (!\in_array($this->context->getMethod(), ['HEAD', 'GET'], true)) {
// no-op
@@ -67,7 +67,7 @@ trait CompiledUrlMatcherTrait
}
}
throw new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo));
throw new ResourceNotFoundException(\sprintf('No routes found for "%s".', $pathinfo));
}
private function doMatch(string $pathinfo, array &$allow = [], array &$allowSchemes = []): array
@@ -23,8 +23,6 @@ use Symfony\Component\Routing\RouteCollection;
*/
class StaticPrefixCollection
{
private string $prefix;
/**
* @var string[]
*/
@@ -40,9 +38,9 @@ class StaticPrefixCollection
*/
private array $items = [];
public function __construct(string $prefix = '/')
{
$this->prefix = $prefix;
public function __construct(
private string $prefix = '/',
) {
}
public function getPrefix(): string
@@ -34,7 +34,7 @@ class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface
foreach ($this->functions->getProvidedServices() as $function => $type) {
$functions[] = new ExpressionFunction(
$function,
static fn (...$args) => sprintf('($context->getParameter(\'_functions\')->get(%s)(%s))', var_export($function, true), implode(', ', $args)),
static fn (...$args) => \sprintf('($context->getParameter(\'_functions\')->get(%s)(%s))', var_export($function, true), implode(', ', $args)),
fn ($values, ...$args) => $values['context']->getParameter('_functions')->get($function)(...$args)
);
}
+8 -8
View File
@@ -66,7 +66,7 @@ class TraceableUrlMatcher extends UrlMatcher
// 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);
$this->addTrace(\sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route);
continue;
}
$regex = $compiledRoute->getRegex();
@@ -80,7 +80,7 @@ class TraceableUrlMatcher extends UrlMatcher
$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);
$this->addTrace(\sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route);
continue;
}
@@ -90,7 +90,7 @@ class TraceableUrlMatcher extends UrlMatcher
$cr = $r->compile();
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);
$this->addTrace(\sprintf('Requirement for "%s" does not match (%s)', $n, $regex), self::ROUTE_ALMOST_MATCHES, $name, $route);
continue 2;
}
@@ -111,7 +111,7 @@ class TraceableUrlMatcher extends UrlMatcher
$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);
$this->addTrace(\sprintf('Host "%s" does not match the requirement ("%s")', $this->context->getHost(), $route->getHost()), self::ROUTE_ALMOST_MATCHES, $name, $route);
continue;
}
@@ -120,7 +120,7 @@ class TraceableUrlMatcher extends UrlMatcher
$status = $this->handleRouteRequirements($pathinfo, $name, $route, $attributes);
if (self::REQUIREMENT_MISMATCH === $status[0]) {
$this->addTrace(sprintf('Condition "%s" does not evaluate to "true"', $route->getCondition()), self::ROUTE_ALMOST_MATCHES, $name, $route);
$this->addTrace(\sprintf('Condition "%s" does not evaluate to "true"', $route->getCondition()), self::ROUTE_ALMOST_MATCHES, $name, $route);
continue;
}
@@ -130,19 +130,19 @@ class TraceableUrlMatcher extends UrlMatcher
return $this->allow = $this->allowSchemes = [];
}
$this->addTrace(sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route);
$this->addTrace(\sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route);
continue;
}
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);
$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 ($requiredMethods && !\in_array($method, $requiredMethods, true)) {
$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);
$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;
}
+1 -1
View File
@@ -79,7 +79,7 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
throw new NoConfigurationException();
}
throw 0 < \count($this->allow) ? new MethodNotAllowedException(array_unique($this->allow)) : new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo));
throw 0 < \count($this->allow) ? new MethodNotAllowedException(array_unique($this->allow)) : new ResourceNotFoundException(\sprintf('No routes found for "%s".', $pathinfo));
}
public function matchRequest(Request $request): array
+7
View File
@@ -47,6 +47,13 @@ class RequestContext
public static function fromUri(string $uri, string $host = 'localhost', string $scheme = 'http', int $httpPort = 80, int $httpsPort = 443): self
{
if (false !== ($i = strpos($uri, '\\')) && $i < strcspn($uri, '?#')) {
$uri = '';
}
if ('' !== $uri && (\ord($uri[0]) <= 32 || \ord($uri[-1]) <= 32 || \strlen($uri) !== strcspn($uri, "\r\n\t"))) {
$uri = '';
}
$uri = parse_url($uri);
$scheme = $uri['scheme'] ?? $scheme;
$host = $uri['host'] ?? $host;
+3 -3
View File
@@ -26,7 +26,7 @@ final class EnumRequirement implements \Stringable
{
if (\is_string($cases)) {
if (!is_subclass_of($cases, \BackedEnum::class, true)) {
throw new InvalidArgumentException(sprintf('"%s" is not a "BackedEnum" class.', $cases));
throw new InvalidArgumentException(\sprintf('"%s" is not a "BackedEnum" class.', $cases));
}
$cases = $cases::cases();
@@ -35,13 +35,13 @@ final class EnumRequirement implements \Stringable
foreach ($cases as $case) {
if (!$case instanceof \BackedEnum) {
throw new InvalidArgumentException(sprintf('Case must be a "BackedEnum" instance, "%s" given.', get_debug_type($case)));
throw new InvalidArgumentException(\sprintf('Case must be a "BackedEnum" instance, "%s" given.', get_debug_type($case)));
}
$class ??= $case::class;
if (!$case instanceof $class) {
throw new InvalidArgumentException(sprintf('"%s::%s" is not a case of "%s".', get_debug_type($case), $case->name, $class));
throw new InvalidArgumentException(\sprintf('"%s::%s" is not a case of "%s".', get_debug_type($case), $case->name, $class));
}
}
}
+2 -1
View File
@@ -23,7 +23,8 @@ enum Requirement
public const POSITIVE_INT = '[1-9][0-9]*';
public const UID_BASE32 = '[0-9A-HJKMNP-TV-Z]{26}';
public const UID_BASE58 = '[1-9A-HJ-NP-Za-km-z]{22}';
public const UID_RFC4122 = '[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}'; // RFC 9562 obsoleted RFC 4122 but the format is the same
public const UID_RFC4122 = '[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}';
public const UID_RFC9562 = self::UID_RFC4122;
public const ULID = '[0-7][0-9A-HJKMNP-TV-Z]{25}';
public const UUID = '[0-9a-f]{8}-[0-9a-f]{4}-[13-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}';
public const UUID_V1 = '[0-9a-f]{8}-[0-9a-f]{4}-1[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}';
+1 -1
View File
@@ -456,7 +456,7 @@ class Route implements \Serializable
}
if ('' === $regex) {
throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" cannot be empty.', $key));
throw new \InvalidArgumentException(\sprintf('Routing requirement for "%s" cannot be empty.', $key));
}
return $regex;
+1 -1
View File
@@ -360,7 +360,7 @@ class RouteCollection implements \IteratorAggregate, \Countable
public function addAlias(string $name, string $alias): Alias
{
if ($name === $alias) {
throw new InvalidArgumentException(sprintf('Route alias "%s" can not reference itself.', $name));
throw new InvalidArgumentException(\sprintf('Route alias "%s" can not reference itself.', $name));
}
unset($this->routes[$name], $this->priorities[$name]);
+29 -29
View File
@@ -75,7 +75,7 @@ class RouteCompiler implements RouteCompilerInterface
foreach ($pathVariables as $pathParam) {
if ('_fragment' === $pathParam) {
throw new \InvalidArgumentException(sprintf('Route pattern "%s" cannot contain "_fragment" as a path parameter.', $route->getPath()));
throw new \InvalidArgumentException(\sprintf('Route pattern "%s" cannot contain "_fragment" as a path parameter.', $route->getPath()));
}
}
@@ -107,10 +107,10 @@ class RouteCompiler implements RouteCompilerInterface
$needsUtf8 = $route->getOption('utf8');
if (!$needsUtf8 && $useUtf8 && preg_match('/[\x80-\xFF]/', $pattern)) {
throw new \LogicException(sprintf('Cannot use UTF-8 route patterns without setting the "utf8" option for route "%s".', $route->getPath()));
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));
throw new \LogicException(\sprintf('Cannot mix UTF-8 requirements with non-UTF-8 pattern "%s".', $pattern));
}
// Match all variables enclosed in "{}" and iterate over them. But we only want to match the innermost variable
@@ -136,14 +136,14 @@ class RouteCompiler implements RouteCompilerInterface
// 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));
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)) {
throw new \LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $pattern, $varName));
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 %d characters in route pattern "%s". Please use a shorter name.', $varName, self::VARIABLE_MAXIMUM_LENGTH, $pattern));
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) {
@@ -154,7 +154,7 @@ class RouteCompiler implements RouteCompilerInterface
$regexp = $route->getRequirement($varName);
if (null === $regexp) {
$followingPattern = (string) substr($pattern, $pos);
$followingPattern = substr($pattern, $pos);
// 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
@@ -163,7 +163,7 @@ class RouteCompiler implements RouteCompilerInterface
// 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(
$regexp = \sprintf(
'[^%s%s]+',
preg_quote($defaultSeparator),
$defaultSeparator !== $nextSeparator && '' !== $nextSeparator ? preg_quote($nextSeparator) : ''
@@ -180,10 +180,10 @@ 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)) {
throw new \LogicException(sprintf('Cannot use UTF-8 route requirements without setting the "utf8" option for variable "%s" in pattern "%s".', $varName, $pattern));
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));
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);
}
@@ -292,28 +292,28 @@ class RouteCompiler implements RouteCompilerInterface
if ('text' === $token[0]) {
// Text tokens
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]), $token[3], $token[2]);
} else {
$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);
if ($nbTokens - 1 == $index) {
// Close the optional subpatterns
$regexp .= str_repeat(')?', $nbTokens - $firstOptional - (0 === $firstOptional ? 1 : 0));
}
}
}
return $regexp;
// 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]), $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);
if ($nbTokens - 1 == $index) {
// Close the optional subpatterns
$regexp .= str_repeat(')?', $nbTokens - $firstOptional - (0 === $firstOptional ? 1 : 0));
}
}
return $regexp;
}
private static function transformCapturingGroupsToNonCapturings(string $regexp): string
+11 -13
View File
@@ -40,12 +40,8 @@ class Router implements RouterInterface, RequestMatcherInterface
protected UrlMatcherInterface|RequestMatcherInterface $matcher;
protected UrlGeneratorInterface $generator;
protected RequestContext $context;
protected LoaderInterface $loader;
protected RouteCollection $collection;
protected mixed $resource;
protected array $options = [];
protected ?LoggerInterface $logger;
protected ?string $defaultLocale;
private ConfigCacheFactoryInterface $configCacheFactory;
@@ -56,14 +52,16 @@ class Router implements RouterInterface, RequestMatcherInterface
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;
public function __construct(
protected LoaderInterface $loader,
protected mixed $resource,
array $options = [],
?RequestContext $context = null,
protected ?LoggerInterface $logger = null,
protected ?string $defaultLocale = null,
) {
$this->context = $context ?? new RequestContext();
$this->setOptions($options);
$this->defaultLocale = $defaultLocale;
}
/**
@@ -107,7 +105,7 @@ class Router implements RouterInterface, RequestMatcherInterface
}
if ($invalid) {
throw new \InvalidArgumentException(sprintf('The Router does not support the following options: "%s".', implode('", "', $invalid)));
throw new \InvalidArgumentException(\sprintf('The Router does not support the following options: "%s".', implode('", "', $invalid)));
}
}
@@ -119,7 +117,7 @@ class Router implements RouterInterface, RequestMatcherInterface
public function setOption(string $key, mixed $value): void
{
if (!\array_key_exists($key, $this->options)) {
throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
throw new \InvalidArgumentException(\sprintf('The Router does not support the "%s" option.', $key));
}
$this->options[$key] = $value;
@@ -133,7 +131,7 @@ class Router implements RouterInterface, RequestMatcherInterface
public function getOption(string $key): mixed
{
if (!\array_key_exists($key, $this->options)) {
throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
throw new \InvalidArgumentException(\sprintf('The Router does not support the "%s" option.', $key));
}
return $this->options[$key];