Update to laravel 7

This commit is contained in:
KodeStar
2022-03-10 11:54:29 +00:00
parent 61a5a1a8b0
commit f9a19fce91
7170 changed files with 274189 additions and 283773 deletions

View File

@@ -26,6 +26,6 @@ class CompiledUrlMatcher extends UrlMatcher
public function __construct(array $compiledRoutes, RequestContext $context)
{
$this->context = $context;
list($this->matchHost, $this->staticRoutes, $this->regexpList, $this->dynamicRoutes, $this->checkCondition) = $compiledRoutes;
[$this->matchHost, $this->staticRoutes, $this->regexpList, $this->dynamicRoutes, $this->checkCondition] = $compiledRoutes;
}
}

View File

@@ -83,7 +83,7 @@ EOF;
$routes = $this->getRoutes();
}
list($staticRoutes, $dynamicRoutes) = $this->groupStaticRoutes($routes);
[$staticRoutes, $dynamicRoutes] = $this->groupStaticRoutes($routes);
$conditions = [null];
$compiledRoutes[] = $this->compileStaticRoutes($staticRoutes, $conditions);
@@ -91,7 +91,7 @@ EOF;
while (true) {
try {
$this->signalingException = new \RuntimeException('preg_match(): Compilation failed: regular expression is too large');
$this->signalingException = new \RuntimeException('Compilation failed: regular expression is too large');
$compiledRoutes = array_merge($compiledRoutes, $this->compileDynamicRoutes($dynamicRoutes, $matchHost, $chunkLimit, $conditions));
break;
@@ -121,7 +121,7 @@ EOF;
}
}
EOF;
$compiledRoutes[4] = $forDump ? $checkConditionCode .= ",\n" : eval('return '.$checkConditionCode.';');
$compiledRoutes[4] = $forDump ? $checkConditionCode.",\n" : eval('return '.$checkConditionCode.';');
} else {
$compiledRoutes[4] = $forDump ? " null, // \$checkCondition\n" : null;
}
@@ -131,7 +131,7 @@ EOF;
private function generateCompiledRoutes(): string
{
list($matchHost, $staticRoutes, $regexpCode, $dynamicRoutes, $checkConditionCode) = $this->getCompiledRoutes(true);
[$matchHost, $staticRoutes, $regexpCode, $dynamicRoutes, $checkConditionCode] = $this->getCompiledRoutes(true);
$code = self::export($matchHost).', // $matchHost'."\n";
@@ -186,8 +186,8 @@ EOF;
if ($hasTrailingSlash) {
$url = substr($url, 0, -1);
}
foreach ($dynamicRegex as list($hostRx, $rx, $prefix)) {
if (('' === $prefix || 0 === strpos($url, $prefix)) && preg_match($rx, $url) && (!$host || !$hostRx || preg_match($hostRx, $host))) {
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;
@@ -221,7 +221,7 @@ EOF;
foreach ($staticRoutes as $url => $routes) {
$compiledRoutes[$url] = [];
foreach ($routes as $name => list($route, $hasTrailingSlash)) {
foreach ($routes as $name => [$route, $hasTrailingSlash]) {
$compiledRoutes[$url][] = $this->compileRoute($route, $name, (!$route->compile()->getHostVariables() ? $route->getHost() : $route->compile()->getHostRegex()) ?: null, $hasTrailingSlash, false, $conditions);
}
}
@@ -242,9 +242,9 @@ EOF;
* 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 possibe to mix unicode/non-unicode patterns in a single regexp, several of them can be generated.
* - 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 blacklisted by replacing its name by "(*F)", which forces a failure-to-match.
* 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
@@ -287,7 +287,7 @@ EOF;
$routes->add($name, $route);
}
foreach ($perModifiers as list($modifiers, $routes)) {
foreach ($perModifiers as [$modifiers, $routes]) {
$prev = false;
$perHost = [];
foreach ($routes->all() as $name => $route) {
@@ -306,7 +306,7 @@ EOF;
$state->mark += \strlen($rx);
$state->regex = $rx;
foreach ($perHost as list($hostRegex, $routes)) {
foreach ($perHost as [$hostRegex, $routes]) {
if ($matchHost) {
if ($hostRegex) {
preg_match('#^.\^(.*)\$.[a-zA-Z]*$#', $hostRegex, $rx);
@@ -349,7 +349,7 @@ EOF;
$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 0 === strpos($message, $this->signalingException->getMessage()) ? $this->signalingException : new \ErrorException($message); });
set_error_handler(function ($type, $message) { throw str_contains($message, $this->signalingException->getMessage()) ? $this->signalingException : new \ErrorException($message); });
try {
preg_match($state->regex, '');
} finally {
@@ -391,7 +391,7 @@ EOF;
continue;
}
list($name, $regex, $vars, $route, $hasTrailingSlash, $hasTrailingVar) = $route;
[$name, $regex, $vars, $route, $hasTrailingSlash, $hasTrailingVar] = $route;
$compiledRoute = $route->compile();
$vars = array_merge($state->hostVars, $vars);
@@ -427,7 +427,7 @@ EOF;
if ($condition = $route->getCondition()) {
$condition = $this->getExpressionLanguage()->compile($condition, ['context', 'request']);
$condition = $conditions[$condition] ?? $conditions[$condition] = (false !== strpos($condition, '$request') ? 1 : -1) * \count($conditions);
$condition = $conditions[$condition] ?? $conditions[$condition] = (str_contains($condition, '$request') ? 1 : -1) * \count($conditions);
} else {
$condition = null;
}
@@ -443,10 +443,10 @@ EOF;
];
}
private function getExpressionLanguage()
private function getExpressionLanguage(): ExpressionLanguage
{
if (null === $this->expressionLanguage) {
if (!class_exists('Symfony\Component\ExpressionLanguage\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);
@@ -455,7 +455,7 @@ EOF;
return $this->expressionLanguage;
}
private function indent($code, $level = 1)
private function indent(string $code, int $level = 1): string
{
return preg_replace('/^./m', str_repeat(' ', $level).'$0', $code);
}

View File

@@ -30,9 +30,13 @@ trait CompiledUrlMatcherTrait
private $staticRoutes = [];
private $regexpList = [];
private $dynamicRoutes = [];
/**
* @var callable|null
*/
private $checkCondition;
public function match($pathinfo)
public function match(string $pathinfo): array
{
$allow = $allowSchemes = [];
if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) {
@@ -87,16 +91,16 @@ trait CompiledUrlMatcherTrait
}
$supportsRedirections = 'GET' === $canonicalMethod && $this instanceof RedirectableUrlMatcherInterface;
foreach ($this->staticRoutes[$trimmedPathinfo] ?? [] as list($ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash, , $condition)) {
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)) {
if ('{' !== $requiredHost[0] ? $requiredHost !== $host : !preg_match($requiredHost, $host, $hostMatches)) {
continue;
}
if ('#' === $requiredHost[0] && $hostMatches) {
if ('{' === $requiredHost[0] && $hostMatches) {
$hostMatches['_route'] = $ret['_route'];
$ret = $this->mergeDefaults($hostMatches, $ret);
}
@@ -127,7 +131,7 @@ trait CompiledUrlMatcherTrait
foreach ($this->regexpList as $offset => $regex) {
while (preg_match($regex, $matchedPathinfo, $matches)) {
foreach ($this->dynamicRoutes[$m = (int) $matches['MARK']] as list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash, $hasTrailingVar, $condition)) {
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;

View File

@@ -24,16 +24,14 @@ 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
* @return string
*/
public function dump(array $options = []);
/**
* Gets the routes to dump.
*
* @return RouteCollection A RouteCollection instance
* @return RouteCollection
*/
public function getRoutes();
}

View File

@@ -1,75 +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;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.2, use "CompiledUrlMatcherDumper" instead.', PhpMatcherDumper::class), E_USER_DEPRECATED);
/**
* 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>
* @author Nicolas Grekas <p@tchwork.com>
*
* @deprecated since Symfony 4.2, use CompiledUrlMatcherDumper instead.
*/
class PhpMatcherDumper extends CompiledUrlMatcherDumper
{
/**
* 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 = [])
{
$options = array_replace([
'class' => 'ProjectUrlMatcher',
'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
], $options);
$code = parent::dump();
$code = preg_replace('#\n ([^ ].*?) // \$(\w++)$#m', "\n \$this->$2 = $1", $code);
$code = str_replace(",\n $", ";\n $", $code);
$code = substr($code, strpos($code, '$this') - 4, -5).";\n";
$code = preg_replace('/^ \$this->\w++ = (?:null|false|\[\n \]);\n/m', '', $code);
$code = str_replace("\n ", "\n ", "\n".$code);
return <<<EOF
<?php
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
use Symfony\Component\Routing\RequestContext;
/**
* This class has been auto-generated
* by the Symfony Routing Component.
*/
class {$options['class']} extends {$options['base_class']}
{
use CompiledUrlMatcherTrait;
public function __construct(RequestContext \$context)
{
\$this->context = \$context;{$code} }
}
EOF;
}
}

View File

@@ -65,12 +65,12 @@ class StaticPrefixCollection
*/
public function addRoute(string $prefix, $route)
{
list($prefix, $staticPrefix) = $this->getCommonPrefix($prefix, $prefix);
[$prefix, $staticPrefix] = $this->getCommonPrefix($prefix, $prefix);
for ($i = \count($this->items) - 1; 0 <= $i; --$i) {
$item = $this->items[$i];
list($commonPrefix, $commonStaticPrefix) = $this->getCommonPrefix($prefix, $this->prefixes[$i]);
[$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
@@ -104,8 +104,8 @@ class StaticPrefixCollection
} else {
// the new route and a previous one have a common prefix, let's merge them
$child = new self($commonPrefix);
list($child->prefixes[0], $child->staticPrefixes[0]) = $child->getCommonPrefix($this->prefixes[$i], $this->prefixes[$i]);
list($child->prefixes[1], $child->staticPrefixes[1]) = $child->getCommonPrefix($prefix, $prefix);
[$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];
$this->staticPrefixes[$i] = $commonStaticPrefix;
@@ -151,40 +151,43 @@ class StaticPrefixCollection
$staticLength = null;
set_error_handler([__CLASS__, 'handleError']);
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;
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 ('(' === $prefix[$j]) {
++$n;
} elseif (')' === $prefix[$j]) {
--$n;
} elseif ('\\' === $prefix[$j] && (++$j === $end || $prefix[$j] !== $anotherPrefix[$j])) {
--$j;
if (0 < $n) {
break;
}
}
if (0 < $n) {
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;
}
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();
}
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
@@ -195,8 +198,8 @@ class StaticPrefixCollection
return [substr($prefix, 0, $i), substr($prefix, 0, $staticLength ?? $i)];
}
public static function handleError($type, $msg)
public static function handleError(int $type, string $msg)
{
return 0 === strpos($msg, 'preg_match(): Compilation failed: lookbehind assertion is not fixed length');
return str_contains($msg, 'Compilation failed: lookbehind assertion is not fixed length');
}
}

View 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()
{
$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);
}
}

View File

@@ -22,7 +22,7 @@ abstract class RedirectableUrlMatcher extends UrlMatcher implements Redirectable
/**
* {@inheritdoc}
*/
public function match($pathinfo)
public function match(string $pathinfo)
{
try {
return parent::match($pathinfo);

View File

@@ -19,13 +19,13 @@ 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
* @return array
*/
public function redirect($path, $route, $scheme = null);
public function redirect(string $path, string $route, string $scheme = null);
}

View File

@@ -26,10 +26,10 @@ 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.
*
* @return array An array of parameters
* @return array
*
* @throws NoConfigurationException If no routing configuration could be found
* @throws ResourceNotFoundException If no matching resource could be found

View File

@@ -23,13 +23,13 @@ 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 = [];
@@ -50,12 +50,32 @@ class TraceableUrlMatcher extends UrlMatcher
return $traces;
}
protected function matchCollection($pathinfo, RouteCollection $routes)
protected function matchCollection(string $pathinfo, RouteCollection $routes)
{
// 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(), [], $route->getOptions());
$cr = $r->compile();
@@ -79,57 +99,60 @@ class TraceableUrlMatcher extends UrlMatcher
continue;
}
// check host requirement
$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, ['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[] = [
'log' => $log,

View File

@@ -28,9 +28,9 @@ use Symfony\Component\Routing\RouteCollection;
*/
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 */
protected $context;
@@ -81,7 +81,7 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
/**
* {@inheritdoc}
*/
public function match($pathinfo)
public function match(string $pathinfo)
{
$this->allow = $this->allowSchemes = [];
@@ -93,9 +93,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));
}
/**
@@ -120,16 +118,15 @@ 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
* @param string $pathinfo The path info to be parsed
*
* @return array An array of parameters
* @return array
*
* @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)
{
// HEAD and GET are equivalent as per RFC
if ('HEAD' === $method = $this->context->getMethod()) {
@@ -144,7 +141,7 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
$requiredMethods = $route->getMethods();
// check the static prefix of the URL first. Only use the more expensive preg_match when it matches
if ('' !== $staticPrefix && 0 !== strpos($trimmedPathinfo, $staticPrefix)) {
if ('' !== $staticPrefix && !str_starts_with($trimmedPathinfo, $staticPrefix)) {
continue;
}
$regex = $compiledRoute->getRegex();
@@ -195,7 +192,7 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
continue;
}
return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, isset($status[1]) ? $status[1] : []));
return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, $status[1] ?? []));
}
return [];
@@ -208,13 +205,9 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
* 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
* @return array
*/
protected function getAttributes(Route $route, $name, array $attributes)
protected function getAttributes(Route $route, string $name, array $attributes)
{
$defaults = $route->getDefaults();
if (isset($defaults['_canonical_route'])) {
@@ -229,13 +222,9 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
/**
* 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)
{
// expression condition
if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), ['context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)])) {
@@ -248,12 +237,9 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
/**
* Get merged default parameters.
*
* @param array $params The parameters
* @param array $defaults The defaults
*
* @return array Merged default parameters
* @return array
*/
protected function mergeDefaults($params, $defaults)
protected function mergeDefaults(array $params, array $defaults)
{
foreach ($params as $key => $value) {
if (!\is_int($key) && null !== $value) {
@@ -267,7 +253,7 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
protected function getExpressionLanguage()
{
if (null === $this->expressionLanguage) {
if (!class_exists('Symfony\Component\ExpressionLanguage\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);
@@ -279,9 +265,9 @@ 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;
}

View File

@@ -26,16 +26,16 @@ 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
* @return array
*
* @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);
}