[Routing] Redirect from trailing slash to no-slash when possible
This commit is contained in:
parent
a38cbd08ce
commit
69a4e94130
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher\Dumper;
|
||||
|
||||
use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
@ -28,6 +29,7 @@ class PhpMatcherDumper extends MatcherDumper
|
||||
{
|
||||
private $expressionLanguage;
|
||||
private $signalingException;
|
||||
private $supportsRedirections;
|
||||
|
||||
/**
|
||||
* @var ExpressionFunctionProviderInterface[]
|
||||
@ -55,7 +57,7 @@ class PhpMatcherDumper extends MatcherDumper
|
||||
|
||||
// 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']);
|
||||
$this->supportsRedirections = isset($interfaces[RedirectableUrlMatcherInterface::class]);
|
||||
|
||||
return <<<EOF
|
||||
<?php
|
||||
@ -75,7 +77,7 @@ class {$options['class']} extends {$options['base_class']}
|
||||
\$this->context = \$context;
|
||||
}
|
||||
|
||||
{$this->generateMatchMethod($supportsRedirections)}
|
||||
{$this->generateMatchMethod()}
|
||||
}
|
||||
|
||||
EOF;
|
||||
@ -89,7 +91,7 @@ EOF;
|
||||
/**
|
||||
* Generates the code for the match method implementing UrlMatcherInterface.
|
||||
*/
|
||||
private function generateMatchMethod(bool $supportsRedirections): string
|
||||
private function generateMatchMethod(): string
|
||||
{
|
||||
// Group hosts by same-suffix, re-order when possible
|
||||
$matchHost = false;
|
||||
@ -104,15 +106,13 @@ EOF;
|
||||
}
|
||||
$routes = $matchHost ? $routes->populateCollection(new RouteCollection()) : $this->getRoutes();
|
||||
|
||||
$code = rtrim($this->compileRoutes($routes, $supportsRedirections, $matchHost), "\n");
|
||||
$code = rtrim($this->compileRoutes($routes, $matchHost), "\n");
|
||||
$fetchHost = $matchHost ? " \$host = strtolower(\$context->getHost());\n" : '';
|
||||
|
||||
return <<<EOF
|
||||
public function match(\$rawPathinfo)
|
||||
$code = <<<EOF
|
||||
{
|
||||
\$allow = array();
|
||||
\$pathinfo = rawurldecode(\$rawPathinfo);
|
||||
\$trimmedPathinfo = rtrim(\$pathinfo, '/');
|
||||
\$context = \$this->context;
|
||||
\$requestMethod = \$canonicalMethod = \$context->getMethod();
|
||||
{$fetchHost}
|
||||
@ -122,25 +122,49 @@ EOF;
|
||||
|
||||
$code
|
||||
|
||||
throw \$allow ? new MethodNotAllowedException(array_keys(\$allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
EOF;
|
||||
|
||||
if ($this->supportsRedirections) {
|
||||
return <<<'EOF'
|
||||
public function match($pathinfo)
|
||||
{
|
||||
$allow = array();
|
||||
if ($ret = $this->doMatch($pathinfo, $allow)) {
|
||||
return $ret;
|
||||
}
|
||||
if ('/' !== $pathinfo && in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
|
||||
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
|
||||
if ($ret = $this->doMatch($pathinfo)) {
|
||||
return $this->redirect($pathinfo, $ret['_route']) + $ret;
|
||||
}
|
||||
}
|
||||
|
||||
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
private function doMatch(string $rawPathinfo, array &$allow = array()): ?array
|
||||
|
||||
EOF
|
||||
.$code."\n return null;\n }";
|
||||
}
|
||||
|
||||
return " public function match(\$rawPathinfo)\n".$code."\n throw \$allow ? new MethodNotAllowedException(array_keys(\$allow)) : new ResourceNotFoundException();\n }";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates PHP code to match a RouteCollection with all its routes.
|
||||
*/
|
||||
private function compileRoutes(RouteCollection $routes, bool $supportsRedirections, bool $matchHost): string
|
||||
private function compileRoutes(RouteCollection $routes, bool $matchHost): string
|
||||
{
|
||||
list($staticRoutes, $dynamicRoutes) = $this->groupStaticRoutes($routes, $supportsRedirections);
|
||||
list($staticRoutes, $dynamicRoutes) = $this->groupStaticRoutes($routes);
|
||||
|
||||
$code = $this->compileStaticRoutes($staticRoutes, $supportsRedirections, $matchHost);
|
||||
$code = $this->compileStaticRoutes($staticRoutes, $matchHost);
|
||||
$chunkLimit = count($dynamicRoutes);
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
$this->signalingException = new \RuntimeException('preg_match(): Compilation failed: regular expression is too large');
|
||||
$code .= $this->compileDynamicRoutes($dynamicRoutes, $supportsRedirections, $matchHost, $chunkLimit);
|
||||
$code .= $this->compileDynamicRoutes($dynamicRoutes, $matchHost, $chunkLimit);
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
if (1 < $chunkLimit && $this->signalingException === $e) {
|
||||
@ -163,7 +187,7 @@ EOF;
|
||||
/**
|
||||
* Splits static routes from dynamic routes, so that they can be matched first, using a simple switch.
|
||||
*/
|
||||
private function groupStaticRoutes(RouteCollection $collection, bool $supportsRedirections): array
|
||||
private function groupStaticRoutes(RouteCollection $collection): array
|
||||
{
|
||||
$staticRoutes = $dynamicRegex = array();
|
||||
$dynamicRoutes = new RouteCollection();
|
||||
@ -172,15 +196,9 @@ EOF;
|
||||
$compiledRoute = $route->compile();
|
||||
$hostRegex = $compiledRoute->getHostRegex();
|
||||
$regex = $compiledRoute->getRegex();
|
||||
if ($hasTrailingSlash = $supportsRedirections && $pos = strpos($regex, '/$')) {
|
||||
$regex = substr_replace($regex, '/?$', $pos, 2);
|
||||
}
|
||||
if (!$compiledRoute->getPathVariables()) {
|
||||
$host = !$compiledRoute->getHostVariables() ? $route->getHost() : '';
|
||||
$url = $route->getPath();
|
||||
if ($hasTrailingSlash) {
|
||||
$url = rtrim($url, '/');
|
||||
}
|
||||
foreach ($dynamicRegex as list($hostRx, $rx)) {
|
||||
if (preg_match($rx, $url) && (!$host || !$hostRx || preg_match($hostRx, $host))) {
|
||||
$dynamicRegex[] = array($hostRegex, $regex);
|
||||
@ -189,7 +207,7 @@ EOF;
|
||||
}
|
||||
}
|
||||
|
||||
$staticRoutes[$url][$name] = array($hasTrailingSlash, $route);
|
||||
$staticRoutes[$url][$name] = $route;
|
||||
} else {
|
||||
$dynamicRegex[] = array($hostRegex, $regex);
|
||||
$dynamicRoutes->add($name, $route);
|
||||
@ -207,60 +225,56 @@ EOF;
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
private function compileStaticRoutes(array $staticRoutes, bool $supportsRedirections, bool $matchHost): string
|
||||
private function compileStaticRoutes(array $staticRoutes, bool $matchHost): string
|
||||
{
|
||||
if (!$staticRoutes) {
|
||||
return '';
|
||||
}
|
||||
$code = $default = '';
|
||||
$checkTrailingSlash = false;
|
||||
|
||||
foreach ($staticRoutes as $url => $routes) {
|
||||
if (1 === count($routes)) {
|
||||
foreach ($routes as $name => list($hasTrailingSlash, $route)) {
|
||||
foreach ($routes as $name => $route) {
|
||||
}
|
||||
|
||||
if (!$route->getCondition()) {
|
||||
if (!$supportsRedirections && $route->getSchemes()) {
|
||||
if (!$this->supportsRedirections && $route->getSchemes()) {
|
||||
throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
|
||||
}
|
||||
$checkTrailingSlash = $checkTrailingSlash || $hasTrailingSlash;
|
||||
$default .= sprintf(
|
||||
"%s => array(%s, %s, %s, %s),\n",
|
||||
self::export($url),
|
||||
self::export(array('_route' => $name) + $route->getDefaults()),
|
||||
self::export(!$route->compile()->getHostVariables() ? $route->getHost() : $route->compile()->getHostRegex() ?: null),
|
||||
self::export(array_flip($route->getMethods()) ?: null),
|
||||
self::export(array_flip($route->getSchemes()) ?: null).($hasTrailingSlash ? ', true' : '')
|
||||
self::export(array_flip($route->getSchemes()) ?: null)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$code .= sprintf(" case %s:\n", self::export($url));
|
||||
foreach ($routes as $name => list($hasTrailingSlash, $route)) {
|
||||
$code .= $this->compileRoute($route, $name, $supportsRedirections, $hasTrailingSlash, true);
|
||||
foreach ($routes as $name => $route) {
|
||||
$code .= $this->compileRoute($route, $name, true);
|
||||
}
|
||||
$code .= " break;\n";
|
||||
}
|
||||
|
||||
$matchedPathinfo = $supportsRedirections ? '$trimmedPathinfo' : '$pathinfo';
|
||||
|
||||
if ($default) {
|
||||
$code .= <<<EOF
|
||||
default:
|
||||
\$routes = array(
|
||||
{$this->indent($default, 4)} );
|
||||
|
||||
if (!isset(\$routes[{$matchedPathinfo}])) {
|
||||
if (!isset(\$routes[\$pathinfo])) {
|
||||
break;
|
||||
}
|
||||
list(\$ret, \$requiredHost, \$requiredMethods, \$requiredSchemes) = \$routes[{$matchedPathinfo}];
|
||||
{$this->compileSwitchDefault(false, $matchedPathinfo, $matchHost, $supportsRedirections, $checkTrailingSlash)}
|
||||
list(\$ret, \$requiredHost, \$requiredMethods, \$requiredSchemes) = \$routes[\$pathinfo];
|
||||
{$this->compileSwitchDefault(false, $matchHost)}
|
||||
EOF;
|
||||
}
|
||||
|
||||
return sprintf(" switch (%s) {\n%s }\n\n", $matchedPathinfo, $this->indent($code));
|
||||
return sprintf(" switch (\$pathinfo) {\n%s }\n\n", $this->indent($code));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -281,7 +295,7 @@ EOF;
|
||||
* matching-but-failing subpattern is blacklisted 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 $supportsRedirections, bool $matchHost, int $chunkLimit): string
|
||||
private function compileDynamicRoutes(RouteCollection $collection, bool $matchHost, int $chunkLimit): string
|
||||
{
|
||||
if (!$collection->all()) {
|
||||
return '';
|
||||
@ -293,8 +307,6 @@ EOF;
|
||||
'default' => '',
|
||||
'mark' => 0,
|
||||
'markTail' => 0,
|
||||
'supportsRedirections' => $supportsRedirections,
|
||||
'checkTrailingSlash' => false,
|
||||
'hostVars' => array(),
|
||||
'vars' => array(),
|
||||
);
|
||||
@ -392,7 +404,7 @@ EOF;
|
||||
{$this->indent($state->default, 4)} );
|
||||
|
||||
list(\$ret, \$vars, \$requiredMethods, \$requiredSchemes) = \$routes[\$m];
|
||||
{$this->compileSwitchDefault(true, '$m', $matchHost, $supportsRedirections, $state->checkTrailingSlash)}
|
||||
{$this->compileSwitchDefault(true, $matchHost)}
|
||||
EOF;
|
||||
}
|
||||
|
||||
@ -448,32 +460,28 @@ EOF;
|
||||
|
||||
list($name, $regex, $vars, $route) = $route;
|
||||
$compiledRoute = $route->compile();
|
||||
$hasTrailingSlash = $state->supportsRedirections && '' !== $regex && '/' === $regex[-1];
|
||||
|
||||
if ($compiledRoute->getRegex() === $prevRegex) {
|
||||
$state->switch = substr_replace($state->switch, $this->compileRoute($route, $name, $state->supportsRedirections, $hasTrailingSlash, false)."\n", -19, 0);
|
||||
$state->switch = substr_replace($state->switch, $this->compileRoute($route, $name, false)."\n", -19, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
$methods = array_flip($route->getMethods());
|
||||
$hasTrailingSlash = $hasTrailingSlash && (!$methods || isset($methods['GET']));
|
||||
$state->mark += 3 + $state->markTail + $hasTrailingSlash + strlen($regex) - $prefixLen;
|
||||
$state->mark += 3 + $state->markTail + strlen($regex) - $prefixLen;
|
||||
$state->markTail = 2 + strlen($state->mark);
|
||||
$rx = sprintf('|%s(*:%s)', substr($regex, $prefixLen).($hasTrailingSlash ? '?' : ''), $state->mark);
|
||||
$rx = sprintf('|%s(*:%s)', substr($regex, $prefixLen), $state->mark);
|
||||
$code .= "\n .".self::export($rx);
|
||||
$state->regex .= $rx;
|
||||
$vars = array_merge($state->hostVars, $vars);
|
||||
|
||||
if (!$route->getCondition() && (!is_array($next = $routes[1 + $i] ?? null) || $regex !== $next[1])) {
|
||||
$prevRegex = null;
|
||||
$state->checkTrailingSlash = $state->checkTrailingSlash || $hasTrailingSlash;
|
||||
$state->default .= sprintf(
|
||||
"%s => array(%s, %s, %s, %s),\n",
|
||||
$state->mark,
|
||||
self::export(array('_route' => $name) + $route->getDefaults()),
|
||||
self::export($vars),
|
||||
self::export($methods ?: null),
|
||||
self::export(array_flip($route->getSchemes()) ?: null).($hasTrailingSlash ? ', true' : '')
|
||||
self::export(array_flip($route->getMethods()) ?: null),
|
||||
self::export(array_flip($route->getSchemes()) ?: null)
|
||||
);
|
||||
} else {
|
||||
$prevRegex = $compiledRoute->getRegex();
|
||||
@ -485,7 +493,7 @@ EOF;
|
||||
|
||||
$state->switch .= <<<EOF
|
||||
case {$state->mark}:
|
||||
{$combine}{$this->compileRoute($route, $name, $state->supportsRedirections, $hasTrailingSlash, false)}
|
||||
{$combine}{$this->compileRoute($route, $name, false)}
|
||||
break;
|
||||
|
||||
EOF;
|
||||
@ -498,7 +506,7 @@ EOF;
|
||||
/**
|
||||
* A simple helper to compiles the switch's "default" for both static and dynamic routes.
|
||||
*/
|
||||
private function compileSwitchDefault(bool $hasVars, string $routesKey, bool $matchHost, bool $supportsRedirections, bool $checkTrailingSlash): string
|
||||
private function compileSwitchDefault(bool $hasVars, bool $matchHost): string
|
||||
{
|
||||
if ($hasVars) {
|
||||
$code = <<<EOF
|
||||
@ -527,21 +535,7 @@ EOF;
|
||||
} else {
|
||||
$code = '';
|
||||
}
|
||||
if ($supportsRedirections && $checkTrailingSlash) {
|
||||
$code .= <<<EOF
|
||||
|
||||
if (empty(\$routes[{$routesKey}][4]) || '/' === \$pathinfo[-1]) {
|
||||
// no-op
|
||||
} elseif ('GET' !== \$canonicalMethod) {
|
||||
\$allow['GET'] = 'GET';
|
||||
break;
|
||||
} else {
|
||||
return array_replace(\$ret, \$this->redirect(\$rawPathinfo.'/', \$ret['_route']));
|
||||
}
|
||||
|
||||
EOF;
|
||||
}
|
||||
if ($supportsRedirections) {
|
||||
if ($this->supportsRedirections) {
|
||||
$code .= <<<EOF
|
||||
|
||||
if (\$requiredSchemes && !isset(\$requiredSchemes[\$context->getScheme()])) {
|
||||
@ -550,7 +544,7 @@ EOF;
|
||||
break;
|
||||
}
|
||||
|
||||
return array_replace(\$ret, \$this->redirect(\$rawPathinfo, \$ret['_route'], key(\$requiredSchemes)));
|
||||
return \$this->redirect(\$rawPathinfo, \$ret['_route'], key(\$requiredSchemes)) + \$ret;
|
||||
}
|
||||
|
||||
EOF;
|
||||
@ -574,7 +568,7 @@ EOF;
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
private function compileRoute(Route $route, string $name, bool $supportsRedirections, bool $hasTrailingSlash, bool $checkHost): string
|
||||
private function compileRoute(Route $route, string $name, bool $checkHost): string
|
||||
{
|
||||
$code = '';
|
||||
$compiledRoute = $route->compile();
|
||||
@ -582,12 +576,6 @@ EOF;
|
||||
$matches = (bool) $compiledRoute->getPathVariables();
|
||||
$hostMatches = (bool) $compiledRoute->getHostVariables();
|
||||
$methods = array_flip($route->getMethods());
|
||||
$supportsTrailingSlash = $supportsRedirections && (!$methods || isset($methods['GET']));
|
||||
|
||||
if ($hasTrailingSlash && !$supportsTrailingSlash) {
|
||||
$hasTrailingSlash = false;
|
||||
$conditions[] = "'/' === \$pathinfo[-1]";
|
||||
}
|
||||
|
||||
if ($route->getCondition()) {
|
||||
$expression = $this->getExpressionLanguage()->compile($route->getCondition(), array('context', 'request'));
|
||||
@ -625,18 +613,17 @@ EOF;
|
||||
|
||||
// optimize parameters array
|
||||
if ($matches || $hostMatches) {
|
||||
$vars = array();
|
||||
if ($hostMatches && $checkHost) {
|
||||
$vars[] = '$hostMatches';
|
||||
}
|
||||
$vars = array("array('_route' => '$name')");
|
||||
if ($matches || ($hostMatches && !$checkHost)) {
|
||||
$vars[] = '$matches';
|
||||
}
|
||||
$vars[] = "array('_route' => '$name')";
|
||||
if ($hostMatches && $checkHost) {
|
||||
$vars[] = '$hostMatches';
|
||||
}
|
||||
|
||||
$code .= sprintf(
|
||||
" \$ret = \$this->mergeDefaults(array_replace(%s), %s);\n",
|
||||
implode(', ', $vars),
|
||||
" \$ret = \$this->mergeDefaults(%s, %s);\n",
|
||||
implode(' + ', $vars),
|
||||
self::export($route->getDefaults())
|
||||
);
|
||||
} elseif ($route->getDefaults()) {
|
||||
@ -645,23 +632,8 @@ EOF;
|
||||
$code .= sprintf(" \$ret = array('_route' => '%s');\n", $name);
|
||||
}
|
||||
|
||||
if ($hasTrailingSlash) {
|
||||
$code .= <<<EOF
|
||||
if ('/' === \$pathinfo[-1]) {
|
||||
// no-op
|
||||
} elseif ('GET' !== \$canonicalMethod) {
|
||||
\$allow['GET'] = 'GET';
|
||||
goto $gotoname;
|
||||
} else {
|
||||
return array_replace(\$ret, \$this->redirect(\$rawPathinfo.'/', '$name'));
|
||||
}
|
||||
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
if ($schemes = $route->getSchemes()) {
|
||||
if (!$supportsRedirections) {
|
||||
if (!$this->supportsRedirections) {
|
||||
throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
|
||||
}
|
||||
$schemes = self::export(array_flip($schemes));
|
||||
@ -673,7 +645,7 @@ EOF;
|
||||
goto $gotoname;
|
||||
}
|
||||
|
||||
return array_replace(\$ret, \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes)));
|
||||
return \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes)) + \$ret;
|
||||
}
|
||||
|
||||
|
||||
@ -694,18 +666,18 @@ EOF;
|
||||
EOF;
|
||||
}
|
||||
|
||||
if ($hasTrailingSlash || $schemes || $methods) {
|
||||
if ($schemes || $methods) {
|
||||
$code .= " return \$ret;\n";
|
||||
} else {
|
||||
$code = substr_replace($code, 'return', $retOffset, 6);
|
||||
}
|
||||
if ($conditions) {
|
||||
$code .= " }\n";
|
||||
} elseif ($hasTrailingSlash || $schemes || $methods) {
|
||||
} elseif ($schemes || $methods) {
|
||||
$code .= ' ';
|
||||
}
|
||||
|
||||
if ($hasTrailingSlash || $schemes || $methods) {
|
||||
if ($schemes || $methods) {
|
||||
$code .= " $gotoname:\n";
|
||||
}
|
||||
|
||||
|
@ -180,12 +180,6 @@ class StaticPrefixCollection
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (1 < $i && '/' === $prefix[$i - 1]) {
|
||||
--$i;
|
||||
}
|
||||
if (null !== $staticLength && 1 < $staticLength && '/' === $prefix[$staticLength - 1]) {
|
||||
--$staticLength;
|
||||
}
|
||||
|
||||
return array(substr($prefix, 0, $i), substr($prefix, 0, $staticLength ?? $i));
|
||||
}
|
||||
|
@ -25,22 +25,21 @@ abstract class RedirectableUrlMatcher extends UrlMatcher implements Redirectable
|
||||
public function match($pathinfo)
|
||||
{
|
||||
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 ('/' === $pathinfo || !\in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
try {
|
||||
$parameters = parent::match($pathinfo.'/');
|
||||
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
|
||||
$ret = parent::match($pathinfo);
|
||||
|
||||
return array_replace($parameters, $this->redirect($pathinfo.'/', isset($parameters['_route']) ? $parameters['_route'] : null));
|
||||
return $this->redirect($pathinfo, $ret['_route'] ?? null) + $ret;
|
||||
} catch (ResourceNotFoundException $e2) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,7 +19,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
|
||||
|
@ -19,7 +19,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
$host = strtolower($context->getHost());
|
||||
@ -82,41 +81,41 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
.'|/([^/]++)(*:57)'
|
||||
.'|head/([^/]++)(*:77)'
|
||||
.')'
|
||||
.'|/test/([^/]++)(?'
|
||||
.'|/(*:103)'
|
||||
.'|/test/([^/]++)/(?'
|
||||
.'|(*:103)'
|
||||
.')'
|
||||
.'|/([\']+)(*:119)'
|
||||
.'|/a(?'
|
||||
.'|/b\'b/([^/]++)(?'
|
||||
.'|/a/(?'
|
||||
.'|b\'b/([^/]++)(?'
|
||||
.'|(*:148)'
|
||||
.'|(*:156)'
|
||||
.')'
|
||||
.'|/(.*)(*:170)'
|
||||
.'|/b\'b/([^/]++)(?'
|
||||
.'|(*:194)'
|
||||
.'|(*:202)'
|
||||
.'|(.*)(*:169)'
|
||||
.'|b\'b/([^/]++)(?'
|
||||
.'|(*:192)'
|
||||
.'|(*:200)'
|
||||
.')'
|
||||
.')'
|
||||
.'|/multi/hello(?:/([^/]++))?(*:238)'
|
||||
.'|/multi/hello(?:/([^/]++))?(*:236)'
|
||||
.'|/([^/]++)/b/([^/]++)(?'
|
||||
.'|(*:269)'
|
||||
.'|(*:277)'
|
||||
.'|(*:267)'
|
||||
.'|(*:275)'
|
||||
.')'
|
||||
.'|/aba/([^/]++)(*:299)'
|
||||
.'|/aba/([^/]++)(*:297)'
|
||||
.')|(?i:([^\\.]++)\\.example\\.com)(?'
|
||||
.'|/route1(?'
|
||||
.'|3/([^/]++)(*:359)'
|
||||
.'|4/([^/]++)(*:377)'
|
||||
.'|3/([^/]++)(*:357)'
|
||||
.'|4/([^/]++)(*:375)'
|
||||
.')'
|
||||
.')|(?i:c\\.example\\.com)(?'
|
||||
.'|/route15/([^/]++)(*:427)'
|
||||
.'|/route15/([^/]++)(*:425)'
|
||||
.')|[^/]*+(?'
|
||||
.'|/route16/([^/]++)(*:462)'
|
||||
.'|/a(?'
|
||||
.'|/a\\.\\.\\.(*:483)'
|
||||
.'|/b(?'
|
||||
.'|/([^/]++)(*:505)'
|
||||
.'|/c/([^/]++)(*:524)'
|
||||
.'|/route16/([^/]++)(*:460)'
|
||||
.'|/a/(?'
|
||||
.'|a\\.\\.\\.(*:481)'
|
||||
.'|b/(?'
|
||||
.'|([^/]++)(*:502)'
|
||||
.'|c/([^/]++)(*:520)'
|
||||
.')'
|
||||
.')'
|
||||
.')'
|
||||
@ -130,10 +129,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
$matches = array('foo' => $matches[1] ?? null);
|
||||
|
||||
// baz4
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array());
|
||||
return $this->mergeDefaults(array('_route' => 'baz4') + $matches, array());
|
||||
|
||||
// baz5
|
||||
$ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array());
|
||||
$ret = $this->mergeDefaults(array('_route' => 'baz5') + $matches, array());
|
||||
if (!isset(($a = array('POST' => 0))[$requestMethod])) {
|
||||
$allow += $a;
|
||||
goto not_baz5;
|
||||
@ -143,7 +142,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
not_baz5:
|
||||
|
||||
// baz.baz6
|
||||
$ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array());
|
||||
$ret = $this->mergeDefaults(array('_route' => 'baz.baz6') + $matches, array());
|
||||
if (!isset(($a = array('PUT' => 0))[$requestMethod])) {
|
||||
$allow += $a;
|
||||
goto not_bazbaz6;
|
||||
@ -157,7 +156,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
$matches = array('foo' => $matches[1] ?? null);
|
||||
|
||||
// foo1
|
||||
$ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array());
|
||||
$ret = $this->mergeDefaults(array('_route' => 'foo1') + $matches, array());
|
||||
if (!isset(($a = array('PUT' => 0))[$requestMethod])) {
|
||||
$allow += $a;
|
||||
goto not_foo1;
|
||||
@ -167,18 +166,18 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
not_foo1:
|
||||
|
||||
break;
|
||||
case 194:
|
||||
case 192:
|
||||
$matches = array('foo1' => $matches[1] ?? null);
|
||||
|
||||
// foo2
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array());
|
||||
return $this->mergeDefaults(array('_route' => 'foo2') + $matches, array());
|
||||
|
||||
break;
|
||||
case 269:
|
||||
case 267:
|
||||
$matches = array('_locale' => $matches[1] ?? null, 'foo' => $matches[2] ?? null);
|
||||
|
||||
// foo3
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array());
|
||||
return $this->mergeDefaults(array('_route' => 'foo3') + $matches, array());
|
||||
|
||||
break;
|
||||
default:
|
||||
@ -188,18 +187,18 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
77 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null),
|
||||
119 => array(array('_route' => 'quoter'), array('quoter'), null, null),
|
||||
156 => array(array('_route' => 'bar1'), array('bar'), null, null),
|
||||
170 => array(array('_route' => 'overridden'), array('var'), null, null),
|
||||
202 => array(array('_route' => 'bar2'), array('bar1'), null, null),
|
||||
238 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null),
|
||||
277 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null),
|
||||
299 => array(array('_route' => 'foo4'), array('foo'), null, null),
|
||||
359 => array(array('_route' => 'route13'), array('var1', 'name'), null, null),
|
||||
377 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null),
|
||||
427 => array(array('_route' => 'route15'), array('name'), null, null),
|
||||
462 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null),
|
||||
483 => array(array('_route' => 'a'), array(), null, null),
|
||||
505 => array(array('_route' => 'b'), array('var'), null, null),
|
||||
524 => array(array('_route' => 'c'), array('var'), null, null),
|
||||
169 => array(array('_route' => 'overridden'), array('var'), null, null),
|
||||
200 => array(array('_route' => 'bar2'), array('bar1'), null, null),
|
||||
236 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null),
|
||||
275 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null),
|
||||
297 => array(array('_route' => 'foo4'), array('foo'), null, null),
|
||||
357 => array(array('_route' => 'route13'), array('var1', 'name'), null, null),
|
||||
375 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null),
|
||||
425 => array(array('_route' => 'route15'), array('name'), null, null),
|
||||
460 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null),
|
||||
481 => array(array('_route' => 'a'), array(), null, null),
|
||||
502 => array(array('_route' => 'b'), array('var'), null, null),
|
||||
520 => array(array('_route' => 'c'), array('var'), null, null),
|
||||
);
|
||||
|
||||
list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m];
|
||||
@ -218,7 +217,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if (524 === $m) {
|
||||
if (520 === $m) {
|
||||
break;
|
||||
}
|
||||
$regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m));
|
||||
|
@ -19,7 +19,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
|
||||
|
@ -15,11 +15,26 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function match($rawPathinfo)
|
||||
public function match($pathinfo)
|
||||
{
|
||||
$allow = array();
|
||||
if ($ret = $this->doMatch($pathinfo, $allow)) {
|
||||
return $ret;
|
||||
}
|
||||
if ('/' !== $pathinfo && in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
|
||||
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
|
||||
if ($ret = $this->doMatch($pathinfo)) {
|
||||
return $this->redirect($pathinfo, $ret['_route']) + $ret;
|
||||
}
|
||||
}
|
||||
|
||||
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
private function doMatch(string $rawPathinfo, array &$allow = array()): ?array
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
|
||||
@ -30,32 +45,34 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
$matchedPathinfo = $pathinfo;
|
||||
$regexList = array(
|
||||
0 => '{^(?'
|
||||
.'|/(en|fr)(?'
|
||||
.'|/admin/post(?'
|
||||
.'|/?(*:34)'
|
||||
.'|/new(*:45)'
|
||||
.'|/(\\d+)(?'
|
||||
.'|(*:61)'
|
||||
.'|/edit(*:73)'
|
||||
.'|/delete(*:87)'
|
||||
.'|/(en|fr)/(?'
|
||||
.'|admin/post/(?'
|
||||
.'|(*:33)'
|
||||
.'|new(*:43)'
|
||||
.'|(\\d+)(?'
|
||||
.'|(*:58)'
|
||||
.'|/(?'
|
||||
.'|edit(*:73)'
|
||||
.'|delete(*:86)'
|
||||
.')'
|
||||
.')'
|
||||
.')'
|
||||
.'|/blog(?'
|
||||
.'|/?(*:106)'
|
||||
.'|/rss\\.xml(*:123)'
|
||||
.'|/p(?'
|
||||
.'|age/([^/]++)(*:148)'
|
||||
.'|osts/([^/]++)(*:169)'
|
||||
.'|blog/(?'
|
||||
.'|(*:104)'
|
||||
.'|rss\\.xml(*:120)'
|
||||
.'|p(?'
|
||||
.'|age/([^/]++)(*:144)'
|
||||
.'|osts/([^/]++)(*:165)'
|
||||
.')'
|
||||
.'|/comments/(\\d+)/new(*:197)'
|
||||
.'|/search(*:212)'
|
||||
.'|comments/(\\d+)/new(*:192)'
|
||||
.'|search(*:206)'
|
||||
.')'
|
||||
.'|/log(?'
|
||||
.'|in(*:230)'
|
||||
.'|out(*:241)'
|
||||
.'|log(?'
|
||||
.'|in(*:223)'
|
||||
.'|out(*:234)'
|
||||
.')'
|
||||
.')'
|
||||
.'|/(en|fr)?(*:260)'
|
||||
.'|/(en|fr)?(*:253)'
|
||||
.')$}sD',
|
||||
);
|
||||
|
||||
@ -64,20 +81,20 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
switch ($m = (int) $matches['MARK']) {
|
||||
default:
|
||||
$routes = array(
|
||||
34 => array(array('_route' => 'a', '_locale' => 'en'), array('_locale'), null, null, true),
|
||||
45 => array(array('_route' => 'b', '_locale' => 'en'), array('_locale'), null, null),
|
||||
61 => array(array('_route' => 'c', '_locale' => 'en'), array('_locale', 'id'), null, null),
|
||||
33 => array(array('_route' => 'a', '_locale' => 'en'), array('_locale'), null, null),
|
||||
43 => array(array('_route' => 'b', '_locale' => 'en'), array('_locale'), null, null),
|
||||
58 => array(array('_route' => 'c', '_locale' => 'en'), array('_locale', 'id'), null, null),
|
||||
73 => array(array('_route' => 'd', '_locale' => 'en'), array('_locale', 'id'), null, null),
|
||||
87 => array(array('_route' => 'e', '_locale' => 'en'), array('_locale', 'id'), null, null),
|
||||
106 => array(array('_route' => 'f', '_locale' => 'en'), array('_locale'), null, null, true),
|
||||
123 => array(array('_route' => 'g', '_locale' => 'en'), array('_locale'), null, null),
|
||||
148 => array(array('_route' => 'h', '_locale' => 'en'), array('_locale', 'page'), null, null),
|
||||
169 => array(array('_route' => 'i', '_locale' => 'en'), array('_locale', 'page'), null, null),
|
||||
197 => array(array('_route' => 'j', '_locale' => 'en'), array('_locale', 'id'), null, null),
|
||||
212 => array(array('_route' => 'k', '_locale' => 'en'), array('_locale'), null, null),
|
||||
230 => array(array('_route' => 'l', '_locale' => 'en'), array('_locale'), null, null),
|
||||
241 => array(array('_route' => 'm', '_locale' => 'en'), array('_locale'), null, null),
|
||||
260 => array(array('_route' => 'n', '_locale' => 'en'), array('_locale'), null, null),
|
||||
86 => array(array('_route' => 'e', '_locale' => 'en'), array('_locale', 'id'), null, null),
|
||||
104 => array(array('_route' => 'f', '_locale' => 'en'), array('_locale'), null, null),
|
||||
120 => array(array('_route' => 'g', '_locale' => 'en'), array('_locale'), null, null),
|
||||
144 => array(array('_route' => 'h', '_locale' => 'en'), array('_locale', 'page'), null, null),
|
||||
165 => array(array('_route' => 'i', '_locale' => 'en'), array('_locale', 'page'), null, null),
|
||||
192 => array(array('_route' => 'j', '_locale' => 'en'), array('_locale', 'id'), null, null),
|
||||
206 => array(array('_route' => 'k', '_locale' => 'en'), array('_locale'), null, null),
|
||||
223 => array(array('_route' => 'l', '_locale' => 'en'), array('_locale'), null, null),
|
||||
234 => array(array('_route' => 'm', '_locale' => 'en'), array('_locale'), null, null),
|
||||
253 => array(array('_route' => 'n', '_locale' => 'en'), array('_locale'), null, null),
|
||||
);
|
||||
|
||||
list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m];
|
||||
@ -88,22 +105,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($routes[$m][4]) || '/' === $pathinfo[-1]) {
|
||||
// no-op
|
||||
} elseif ('GET' !== $canonicalMethod) {
|
||||
$allow['GET'] = 'GET';
|
||||
break;
|
||||
} else {
|
||||
return array_replace($ret, $this->redirect($rawPathinfo.'/', $ret['_route']));
|
||||
}
|
||||
|
||||
if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow['GET'] = 'GET';
|
||||
break;
|
||||
}
|
||||
|
||||
return array_replace($ret, $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)));
|
||||
return $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)) + $ret;
|
||||
}
|
||||
|
||||
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
|
||||
@ -114,7 +122,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if (260 === $m) {
|
||||
if (253 === $m) {
|
||||
break;
|
||||
}
|
||||
$regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m));
|
||||
@ -122,6 +130,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
}
|
||||
}
|
||||
|
||||
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
|
||||
@ -30,19 +29,19 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
$matchedPathinfo = $pathinfo;
|
||||
$regexList = array(
|
||||
0 => '{^(?'
|
||||
.'|/abc([^/]++)(?'
|
||||
.'|/1(?'
|
||||
.'|/abc([^/]++)/(?'
|
||||
.'|1(?'
|
||||
.'|(*:27)'
|
||||
.'|0(?'
|
||||
.'|(*:38)'
|
||||
.'|0(*:46)'
|
||||
.')'
|
||||
.')'
|
||||
.'|/2(?'
|
||||
.'|(*:60)'
|
||||
.'|2(?'
|
||||
.'|(*:59)'
|
||||
.'|0(?'
|
||||
.'|(*:71)'
|
||||
.'|0(*:79)'
|
||||
.'|(*:70)'
|
||||
.'|0(*:78)'
|
||||
.')'
|
||||
.')'
|
||||
.')'
|
||||
@ -57,9 +56,9 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
27 => array(array('_route' => 'r1'), array('foo'), null, null),
|
||||
38 => array(array('_route' => 'r10'), array('foo'), null, null),
|
||||
46 => array(array('_route' => 'r100'), array('foo'), null, null),
|
||||
60 => array(array('_route' => 'r2'), array('foo'), null, null),
|
||||
71 => array(array('_route' => 'r20'), array('foo'), null, null),
|
||||
79 => array(array('_route' => 'r200'), array('foo'), null, null),
|
||||
59 => array(array('_route' => 'r2'), array('foo'), null, null),
|
||||
70 => array(array('_route' => 'r20'), array('foo'), null, null),
|
||||
78 => array(array('_route' => 'r200'), array('foo'), null, null),
|
||||
);
|
||||
|
||||
list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m];
|
||||
@ -78,7 +77,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if (79 === $m) {
|
||||
if (78 === $m) {
|
||||
break;
|
||||
}
|
||||
$regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m));
|
||||
|
@ -19,7 +19,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
$host = strtolower($context->getHost());
|
||||
@ -46,10 +45,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
$matches = array('foo' => $matches[1] ?? null, 'foo' => $matches[2] ?? null);
|
||||
|
||||
// r1
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'r1')), array());
|
||||
return $this->mergeDefaults(array('_route' => 'r1') + $matches, array());
|
||||
|
||||
// r2
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'r2')), array());
|
||||
return $this->mergeDefaults(array('_route' => 'r2') + $matches, array());
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -15,11 +15,26 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function match($rawPathinfo)
|
||||
public function match($pathinfo)
|
||||
{
|
||||
$allow = array();
|
||||
if ($ret = $this->doMatch($pathinfo, $allow)) {
|
||||
return $ret;
|
||||
}
|
||||
if ('/' !== $pathinfo && in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
|
||||
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
|
||||
if ($ret = $this->doMatch($pathinfo)) {
|
||||
return $this->redirect($pathinfo, $ret['_route']) + $ret;
|
||||
}
|
||||
}
|
||||
|
||||
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
private function doMatch(string $rawPathinfo, array &$allow = array()): ?array
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
$host = strtolower($context->getHost());
|
||||
@ -28,16 +43,16 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
$canonicalMethod = 'GET';
|
||||
}
|
||||
|
||||
switch ($trimmedPathinfo) {
|
||||
switch ($pathinfo) {
|
||||
default:
|
||||
$routes = array(
|
||||
'/test/baz' => array(array('_route' => 'baz'), null, null, null),
|
||||
'/test/baz.html' => array(array('_route' => 'baz2'), null, null, null),
|
||||
'/test/baz3' => array(array('_route' => 'baz3'), null, null, null, true),
|
||||
'/test/baz3/' => array(array('_route' => 'baz3'), null, null, null),
|
||||
'/foofoo' => array(array('_route' => 'foofoo', 'def' => 'test'), null, null, null),
|
||||
'/spa ce' => array(array('_route' => 'space'), null, null, null),
|
||||
'/multi/new' => array(array('_route' => 'overridden2'), null, null, null),
|
||||
'/multi/hey' => array(array('_route' => 'hey'), null, null, null, true),
|
||||
'/multi/hey/' => array(array('_route' => 'hey'), null, null, null),
|
||||
'/ababa' => array(array('_route' => 'ababa'), null, null, null),
|
||||
'/route1' => array(array('_route' => 'route1'), 'a.example.com', null, null),
|
||||
'/c2/route2' => array(array('_route' => 'route2'), 'a.example.com', null, null),
|
||||
@ -52,10 +67,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
'/nonsecure' => array(array('_route' => 'nonsecure'), null, null, array('http' => 0)),
|
||||
);
|
||||
|
||||
if (!isset($routes[$trimmedPathinfo])) {
|
||||
if (!isset($routes[$pathinfo])) {
|
||||
break;
|
||||
}
|
||||
list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$trimmedPathinfo];
|
||||
list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo];
|
||||
|
||||
if ($requiredHost) {
|
||||
if ('#' !== $requiredHost[0] ? $requiredHost !== $host : !preg_match($requiredHost, $host, $hostMatches)) {
|
||||
@ -67,22 +82,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($routes[$trimmedPathinfo][4]) || '/' === $pathinfo[-1]) {
|
||||
// no-op
|
||||
} elseif ('GET' !== $canonicalMethod) {
|
||||
$allow['GET'] = 'GET';
|
||||
break;
|
||||
} else {
|
||||
return array_replace($ret, $this->redirect($rawPathinfo.'/', $ret['_route']));
|
||||
}
|
||||
|
||||
if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow['GET'] = 'GET';
|
||||
break;
|
||||
}
|
||||
|
||||
return array_replace($ret, $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)));
|
||||
return $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)) + $ret;
|
||||
}
|
||||
|
||||
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
|
||||
@ -102,41 +108,41 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
.'|/([^/]++)(*:57)'
|
||||
.'|head/([^/]++)(*:77)'
|
||||
.')'
|
||||
.'|/test/([^/]++)(?'
|
||||
.'|/?(*:104)'
|
||||
.'|/test/([^/]++)/(?'
|
||||
.'|(*:103)'
|
||||
.')'
|
||||
.'|/([\']+)(*:120)'
|
||||
.'|/a(?'
|
||||
.'|/b\'b/([^/]++)(?'
|
||||
.'|(*:149)'
|
||||
.'|(*:157)'
|
||||
.'|/([\']+)(*:119)'
|
||||
.'|/a/(?'
|
||||
.'|b\'b/([^/]++)(?'
|
||||
.'|(*:148)'
|
||||
.'|(*:156)'
|
||||
.')'
|
||||
.'|/(.*)(*:171)'
|
||||
.'|/b\'b/([^/]++)(?'
|
||||
.'|(*:195)'
|
||||
.'|(*:203)'
|
||||
.'|(.*)(*:169)'
|
||||
.'|b\'b/([^/]++)(?'
|
||||
.'|(*:192)'
|
||||
.'|(*:200)'
|
||||
.')'
|
||||
.')'
|
||||
.'|/multi/hello(?:/([^/]++))?(*:239)'
|
||||
.'|/multi/hello(?:/([^/]++))?(*:236)'
|
||||
.'|/([^/]++)/b/([^/]++)(?'
|
||||
.'|(*:270)'
|
||||
.'|(*:278)'
|
||||
.'|(*:267)'
|
||||
.'|(*:275)'
|
||||
.')'
|
||||
.'|/aba/([^/]++)(*:300)'
|
||||
.'|/aba/([^/]++)(*:297)'
|
||||
.')|(?i:([^\\.]++)\\.example\\.com)(?'
|
||||
.'|/route1(?'
|
||||
.'|3/([^/]++)(*:360)'
|
||||
.'|4/([^/]++)(*:378)'
|
||||
.'|3/([^/]++)(*:357)'
|
||||
.'|4/([^/]++)(*:375)'
|
||||
.')'
|
||||
.')|(?i:c\\.example\\.com)(?'
|
||||
.'|/route15/([^/]++)(*:428)'
|
||||
.'|/route15/([^/]++)(*:425)'
|
||||
.')|[^/]*+(?'
|
||||
.'|/route16/([^/]++)(*:463)'
|
||||
.'|/a(?'
|
||||
.'|/a\\.\\.\\.(*:484)'
|
||||
.'|/b(?'
|
||||
.'|/([^/]++)(*:506)'
|
||||
.'|/c/([^/]++)(*:525)'
|
||||
.'|/route16/([^/]++)(*:460)'
|
||||
.'|/a/(?'
|
||||
.'|a\\.\\.\\.(*:481)'
|
||||
.'|b/(?'
|
||||
.'|([^/]++)(*:502)'
|
||||
.'|c/([^/]++)(*:520)'
|
||||
.')'
|
||||
.')'
|
||||
.')'
|
||||
@ -146,53 +152,38 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
foreach ($regexList as $offset => $regex) {
|
||||
while (preg_match($regex, $matchedPathinfo, $matches)) {
|
||||
switch ($m = (int) $matches['MARK']) {
|
||||
case 104:
|
||||
case 103:
|
||||
$matches = array('foo' => $matches[1] ?? null);
|
||||
|
||||
// baz4
|
||||
$ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array());
|
||||
if ('/' === $pathinfo[-1]) {
|
||||
// no-op
|
||||
} elseif ('GET' !== $canonicalMethod) {
|
||||
$allow['GET'] = 'GET';
|
||||
goto not_baz4;
|
||||
} else {
|
||||
return array_replace($ret, $this->redirect($rawPathinfo.'/', 'baz4'));
|
||||
return $this->mergeDefaults(array('_route' => 'baz4') + $matches, array());
|
||||
|
||||
// baz5
|
||||
$ret = $this->mergeDefaults(array('_route' => 'baz5') + $matches, array());
|
||||
if (!isset(($a = array('POST' => 0))[$requestMethod])) {
|
||||
$allow += $a;
|
||||
goto not_baz5;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
not_baz4:
|
||||
|
||||
// baz5
|
||||
if ('/' === $pathinfo[-1]) {
|
||||
$ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array());
|
||||
if (!isset(($a = array('POST' => 0))[$requestMethod])) {
|
||||
$allow += $a;
|
||||
goto not_baz5;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
not_baz5:
|
||||
|
||||
// baz.baz6
|
||||
if ('/' === $pathinfo[-1]) {
|
||||
$ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array());
|
||||
if (!isset(($a = array('PUT' => 0))[$requestMethod])) {
|
||||
$allow += $a;
|
||||
goto not_bazbaz6;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
$ret = $this->mergeDefaults(array('_route' => 'baz.baz6') + $matches, array());
|
||||
if (!isset(($a = array('PUT' => 0))[$requestMethod])) {
|
||||
$allow += $a;
|
||||
goto not_bazbaz6;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
not_bazbaz6:
|
||||
|
||||
break;
|
||||
case 149:
|
||||
case 148:
|
||||
$matches = array('foo' => $matches[1] ?? null);
|
||||
|
||||
// foo1
|
||||
$ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array());
|
||||
$ret = $this->mergeDefaults(array('_route' => 'foo1') + $matches, array());
|
||||
if (!isset(($a = array('PUT' => 0))[$requestMethod])) {
|
||||
$allow += $a;
|
||||
goto not_foo1;
|
||||
@ -202,18 +193,18 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
not_foo1:
|
||||
|
||||
break;
|
||||
case 195:
|
||||
case 192:
|
||||
$matches = array('foo1' => $matches[1] ?? null);
|
||||
|
||||
// foo2
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array());
|
||||
return $this->mergeDefaults(array('_route' => 'foo2') + $matches, array());
|
||||
|
||||
break;
|
||||
case 270:
|
||||
case 267:
|
||||
$matches = array('_locale' => $matches[1] ?? null, 'foo' => $matches[2] ?? null);
|
||||
|
||||
// foo3
|
||||
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array());
|
||||
return $this->mergeDefaults(array('_route' => 'foo3') + $matches, array());
|
||||
|
||||
break;
|
||||
default:
|
||||
@ -221,20 +212,20 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
34 => array(array('_route' => 'foo', 'def' => 'test'), array('bar'), null, null),
|
||||
57 => array(array('_route' => 'bar'), array('foo'), array('GET' => 0, 'HEAD' => 1), null),
|
||||
77 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null),
|
||||
120 => array(array('_route' => 'quoter'), array('quoter'), null, null),
|
||||
157 => array(array('_route' => 'bar1'), array('bar'), null, null),
|
||||
171 => array(array('_route' => 'overridden'), array('var'), null, null),
|
||||
203 => array(array('_route' => 'bar2'), array('bar1'), null, null),
|
||||
239 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null),
|
||||
278 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null),
|
||||
300 => array(array('_route' => 'foo4'), array('foo'), null, null),
|
||||
360 => array(array('_route' => 'route13'), array('var1', 'name'), null, null),
|
||||
378 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null),
|
||||
428 => array(array('_route' => 'route15'), array('name'), null, null),
|
||||
463 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null),
|
||||
484 => array(array('_route' => 'a'), array(), null, null),
|
||||
506 => array(array('_route' => 'b'), array('var'), null, null),
|
||||
525 => array(array('_route' => 'c'), array('var'), null, null),
|
||||
119 => array(array('_route' => 'quoter'), array('quoter'), null, null),
|
||||
156 => array(array('_route' => 'bar1'), array('bar'), null, null),
|
||||
169 => array(array('_route' => 'overridden'), array('var'), null, null),
|
||||
200 => array(array('_route' => 'bar2'), array('bar1'), null, null),
|
||||
236 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null),
|
||||
275 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null),
|
||||
297 => array(array('_route' => 'foo4'), array('foo'), null, null),
|
||||
357 => array(array('_route' => 'route13'), array('var1', 'name'), null, null),
|
||||
375 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null),
|
||||
425 => array(array('_route' => 'route15'), array('name'), null, null),
|
||||
460 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null),
|
||||
481 => array(array('_route' => 'a'), array(), null, null),
|
||||
502 => array(array('_route' => 'b'), array('var'), null, null),
|
||||
520 => array(array('_route' => 'c'), array('var'), null, null),
|
||||
);
|
||||
|
||||
list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m];
|
||||
@ -251,7 +242,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
break;
|
||||
}
|
||||
|
||||
return array_replace($ret, $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)));
|
||||
return $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)) + $ret;
|
||||
}
|
||||
|
||||
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
|
||||
@ -262,7 +253,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if (525 === $m) {
|
||||
if (520 === $m) {
|
||||
break;
|
||||
}
|
||||
$regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m));
|
||||
@ -270,6 +261,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
}
|
||||
}
|
||||
|
||||
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
|
||||
|
@ -19,7 +19,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
|
||||
|
@ -15,11 +15,26 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function match($rawPathinfo)
|
||||
public function match($pathinfo)
|
||||
{
|
||||
$allow = array();
|
||||
if ($ret = $this->doMatch($pathinfo, $allow)) {
|
||||
return $ret;
|
||||
}
|
||||
if ('/' !== $pathinfo && in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
|
||||
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
|
||||
if ($ret = $this->doMatch($pathinfo)) {
|
||||
return $this->redirect($pathinfo, $ret['_route']) + $ret;
|
||||
}
|
||||
}
|
||||
|
||||
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
private function doMatch(string $rawPathinfo, array &$allow = array()): ?array
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
|
||||
@ -27,36 +42,27 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
$canonicalMethod = 'GET';
|
||||
}
|
||||
|
||||
switch ($trimmedPathinfo) {
|
||||
switch ($pathinfo) {
|
||||
default:
|
||||
$routes = array(
|
||||
'/a/11' => array(array('_route' => 'a_first'), null, null, null),
|
||||
'/a/22' => array(array('_route' => 'a_second'), null, null, null),
|
||||
'/a/333' => array(array('_route' => 'a_third'), null, null, null),
|
||||
'/a/44' => array(array('_route' => 'a_fourth'), null, null, null, true),
|
||||
'/a/55' => array(array('_route' => 'a_fifth'), null, null, null, true),
|
||||
'/a/66' => array(array('_route' => 'a_sixth'), null, null, null, true),
|
||||
'/nested/group/a' => array(array('_route' => 'nested_a'), null, null, null, true),
|
||||
'/nested/group/b' => array(array('_route' => 'nested_b'), null, null, null, true),
|
||||
'/nested/group/c' => array(array('_route' => 'nested_c'), null, null, null, true),
|
||||
'/slashed/group' => array(array('_route' => 'slashed_a'), null, null, null, true),
|
||||
'/slashed/group/b' => array(array('_route' => 'slashed_b'), null, null, null, true),
|
||||
'/slashed/group/c' => array(array('_route' => 'slashed_c'), null, null, null, true),
|
||||
'/a/44/' => array(array('_route' => 'a_fourth'), null, null, null),
|
||||
'/a/55/' => array(array('_route' => 'a_fifth'), null, null, null),
|
||||
'/a/66/' => array(array('_route' => 'a_sixth'), null, null, null),
|
||||
'/nested/group/a/' => array(array('_route' => 'nested_a'), null, null, null),
|
||||
'/nested/group/b/' => array(array('_route' => 'nested_b'), null, null, null),
|
||||
'/nested/group/c/' => array(array('_route' => 'nested_c'), null, null, null),
|
||||
'/slashed/group/' => array(array('_route' => 'slashed_a'), null, null, null),
|
||||
'/slashed/group/b/' => array(array('_route' => 'slashed_b'), null, null, null),
|
||||
'/slashed/group/c/' => array(array('_route' => 'slashed_c'), null, null, null),
|
||||
);
|
||||
|
||||
if (!isset($routes[$trimmedPathinfo])) {
|
||||
if (!isset($routes[$pathinfo])) {
|
||||
break;
|
||||
}
|
||||
list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$trimmedPathinfo];
|
||||
|
||||
if (empty($routes[$trimmedPathinfo][4]) || '/' === $pathinfo[-1]) {
|
||||
// no-op
|
||||
} elseif ('GET' !== $canonicalMethod) {
|
||||
$allow['GET'] = 'GET';
|
||||
break;
|
||||
} else {
|
||||
return array_replace($ret, $this->redirect($rawPathinfo.'/', $ret['_route']));
|
||||
}
|
||||
list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo];
|
||||
|
||||
if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
@ -64,7 +70,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
break;
|
||||
}
|
||||
|
||||
return array_replace($ret, $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)));
|
||||
return $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)) + $ret;
|
||||
}
|
||||
|
||||
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
|
||||
@ -106,7 +112,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
break;
|
||||
}
|
||||
|
||||
return array_replace($ret, $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)));
|
||||
return $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)) + $ret;
|
||||
}
|
||||
|
||||
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
|
||||
@ -125,6 +131,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
}
|
||||
}
|
||||
|
||||
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
|
||||
@ -56,17 +55,17 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
$matchedPathinfo = $pathinfo;
|
||||
$regexList = array(
|
||||
0 => '{^(?'
|
||||
.'|/trailing/regex(?'
|
||||
.'|/no\\-methods/([^/]++)/(*:47)'
|
||||
.'|/get\\-method/([^/]++)/(*:76)'
|
||||
.'|/head\\-method/([^/]++)/(*:106)'
|
||||
.'|/post\\-method/([^/]++)/(*:137)'
|
||||
.'|/trailing/regex/(?'
|
||||
.'|no\\-methods/([^/]++)/(*:47)'
|
||||
.'|get\\-method/([^/]++)/(*:75)'
|
||||
.'|head\\-method/([^/]++)/(*:104)'
|
||||
.'|post\\-method/([^/]++)/(*:134)'
|
||||
.')'
|
||||
.'|/not\\-trailing/regex(?'
|
||||
.'|/no\\-methods/([^/]++)(*:190)'
|
||||
.'|/get\\-method/([^/]++)(*:219)'
|
||||
.'|/head\\-method/([^/]++)(*:249)'
|
||||
.'|/post\\-method/([^/]++)(*:279)'
|
||||
.'|/not\\-trailing/regex/(?'
|
||||
.'|no\\-methods/([^/]++)(*:187)'
|
||||
.'|get\\-method/([^/]++)(*:215)'
|
||||
.'|head\\-method/([^/]++)(*:244)'
|
||||
.'|post\\-method/([^/]++)(*:273)'
|
||||
.')'
|
||||
.')$}sD',
|
||||
);
|
||||
@ -77,13 +76,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
default:
|
||||
$routes = array(
|
||||
47 => array(array('_route' => 'regex_trailing_slash_no_methods'), array('param'), null, null),
|
||||
76 => array(array('_route' => 'regex_trailing_slash_GET_method'), array('param'), array('GET' => 0), null),
|
||||
106 => array(array('_route' => 'regex_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null),
|
||||
137 => array(array('_route' => 'regex_trailing_slash_POST_method'), array('param'), array('POST' => 0), null),
|
||||
190 => array(array('_route' => 'regex_not_trailing_slash_no_methods'), array('param'), null, null),
|
||||
219 => array(array('_route' => 'regex_not_trailing_slash_GET_method'), array('param'), array('GET' => 0), null),
|
||||
249 => array(array('_route' => 'regex_not_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null),
|
||||
279 => array(array('_route' => 'regex_not_trailing_slash_POST_method'), array('param'), array('POST' => 0), null),
|
||||
75 => array(array('_route' => 'regex_trailing_slash_GET_method'), array('param'), array('GET' => 0), null),
|
||||
104 => array(array('_route' => 'regex_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null),
|
||||
134 => array(array('_route' => 'regex_trailing_slash_POST_method'), array('param'), array('POST' => 0), null),
|
||||
187 => array(array('_route' => 'regex_not_trailing_slash_no_methods'), array('param'), null, null),
|
||||
215 => array(array('_route' => 'regex_not_trailing_slash_GET_method'), array('param'), array('GET' => 0), null),
|
||||
244 => array(array('_route' => 'regex_not_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null),
|
||||
273 => array(array('_route' => 'regex_not_trailing_slash_POST_method'), array('param'), array('POST' => 0), null),
|
||||
);
|
||||
|
||||
list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m];
|
||||
@ -102,7 +101,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if (279 === $m) {
|
||||
if (273 === $m) {
|
||||
break;
|
||||
}
|
||||
$regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m));
|
||||
|
@ -15,11 +15,26 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function match($rawPathinfo)
|
||||
public function match($pathinfo)
|
||||
{
|
||||
$allow = array();
|
||||
if ($ret = $this->doMatch($pathinfo, $allow)) {
|
||||
return $ret;
|
||||
}
|
||||
if ('/' !== $pathinfo && in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) {
|
||||
$pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1);
|
||||
if ($ret = $this->doMatch($pathinfo)) {
|
||||
return $this->redirect($pathinfo, $ret['_route']) + $ret;
|
||||
}
|
||||
}
|
||||
|
||||
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
private function doMatch(string $rawPathinfo, array &$allow = array()): ?array
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
|
||||
@ -27,32 +42,23 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
$canonicalMethod = 'GET';
|
||||
}
|
||||
|
||||
switch ($trimmedPathinfo) {
|
||||
switch ($pathinfo) {
|
||||
default:
|
||||
$routes = array(
|
||||
'/trailing/simple/no-methods' => array(array('_route' => 'simple_trailing_slash_no_methods'), null, null, null, true),
|
||||
'/trailing/simple/get-method' => array(array('_route' => 'simple_trailing_slash_GET_method'), null, array('GET' => 0), null, true),
|
||||
'/trailing/simple/head-method' => array(array('_route' => 'simple_trailing_slash_HEAD_method'), null, array('HEAD' => 0), null, true),
|
||||
'/trailing/simple/post-method' => array(array('_route' => 'simple_trailing_slash_POST_method'), null, array('POST' => 0), null, true),
|
||||
'/trailing/simple/no-methods/' => array(array('_route' => 'simple_trailing_slash_no_methods'), null, null, null),
|
||||
'/trailing/simple/get-method/' => array(array('_route' => 'simple_trailing_slash_GET_method'), null, array('GET' => 0), null),
|
||||
'/trailing/simple/head-method/' => array(array('_route' => 'simple_trailing_slash_HEAD_method'), null, array('HEAD' => 0), null),
|
||||
'/trailing/simple/post-method/' => array(array('_route' => 'simple_trailing_slash_POST_method'), null, array('POST' => 0), null),
|
||||
'/not-trailing/simple/no-methods' => array(array('_route' => 'simple_not_trailing_slash_no_methods'), null, null, null),
|
||||
'/not-trailing/simple/get-method' => array(array('_route' => 'simple_not_trailing_slash_GET_method'), null, array('GET' => 0), null),
|
||||
'/not-trailing/simple/head-method' => array(array('_route' => 'simple_not_trailing_slash_HEAD_method'), null, array('HEAD' => 0), null),
|
||||
'/not-trailing/simple/post-method' => array(array('_route' => 'simple_not_trailing_slash_POST_method'), null, array('POST' => 0), null),
|
||||
);
|
||||
|
||||
if (!isset($routes[$trimmedPathinfo])) {
|
||||
if (!isset($routes[$pathinfo])) {
|
||||
break;
|
||||
}
|
||||
list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$trimmedPathinfo];
|
||||
|
||||
if (empty($routes[$trimmedPathinfo][4]) || '/' === $pathinfo[-1]) {
|
||||
// no-op
|
||||
} elseif ('GET' !== $canonicalMethod) {
|
||||
$allow['GET'] = 'GET';
|
||||
break;
|
||||
} else {
|
||||
return array_replace($ret, $this->redirect($rawPathinfo.'/', $ret['_route']));
|
||||
}
|
||||
list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo];
|
||||
|
||||
if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
@ -60,7 +66,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
break;
|
||||
}
|
||||
|
||||
return array_replace($ret, $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)));
|
||||
return $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)) + $ret;
|
||||
}
|
||||
|
||||
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
|
||||
@ -74,17 +80,17 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
$matchedPathinfo = $pathinfo;
|
||||
$regexList = array(
|
||||
0 => '{^(?'
|
||||
.'|/trailing/regex(?'
|
||||
.'|/no\\-methods/([^/]++)/?(*:48)'
|
||||
.'|/get\\-method/([^/]++)/?(*:78)'
|
||||
.'|/head\\-method/([^/]++)/(*:108)'
|
||||
.'|/post\\-method/([^/]++)/(*:139)'
|
||||
.'|/trailing/regex/(?'
|
||||
.'|no\\-methods/([^/]++)/(*:47)'
|
||||
.'|get\\-method/([^/]++)/(*:75)'
|
||||
.'|head\\-method/([^/]++)/(*:104)'
|
||||
.'|post\\-method/([^/]++)/(*:134)'
|
||||
.')'
|
||||
.'|/not\\-trailing/regex(?'
|
||||
.'|/no\\-methods/([^/]++)(*:192)'
|
||||
.'|/get\\-method/([^/]++)(*:221)'
|
||||
.'|/head\\-method/([^/]++)(*:251)'
|
||||
.'|/post\\-method/([^/]++)(*:281)'
|
||||
.'|/not\\-trailing/regex/(?'
|
||||
.'|no\\-methods/([^/]++)(*:187)'
|
||||
.'|get\\-method/([^/]++)(*:215)'
|
||||
.'|head\\-method/([^/]++)(*:244)'
|
||||
.'|post\\-method/([^/]++)(*:273)'
|
||||
.')'
|
||||
.')$}sD',
|
||||
);
|
||||
@ -94,14 +100,14 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
switch ($m = (int) $matches['MARK']) {
|
||||
default:
|
||||
$routes = array(
|
||||
48 => array(array('_route' => 'regex_trailing_slash_no_methods'), array('param'), null, null, true),
|
||||
78 => array(array('_route' => 'regex_trailing_slash_GET_method'), array('param'), array('GET' => 0), null, true),
|
||||
108 => array(array('_route' => 'regex_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null),
|
||||
139 => array(array('_route' => 'regex_trailing_slash_POST_method'), array('param'), array('POST' => 0), null),
|
||||
192 => array(array('_route' => 'regex_not_trailing_slash_no_methods'), array('param'), null, null),
|
||||
221 => array(array('_route' => 'regex_not_trailing_slash_GET_method'), array('param'), array('GET' => 0), null),
|
||||
251 => array(array('_route' => 'regex_not_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null),
|
||||
281 => array(array('_route' => 'regex_not_trailing_slash_POST_method'), array('param'), array('POST' => 0), null),
|
||||
47 => array(array('_route' => 'regex_trailing_slash_no_methods'), array('param'), null, null),
|
||||
75 => array(array('_route' => 'regex_trailing_slash_GET_method'), array('param'), array('GET' => 0), null),
|
||||
104 => array(array('_route' => 'regex_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null),
|
||||
134 => array(array('_route' => 'regex_trailing_slash_POST_method'), array('param'), array('POST' => 0), null),
|
||||
187 => array(array('_route' => 'regex_not_trailing_slash_no_methods'), array('param'), null, null),
|
||||
215 => array(array('_route' => 'regex_not_trailing_slash_GET_method'), array('param'), array('GET' => 0), null),
|
||||
244 => array(array('_route' => 'regex_not_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null),
|
||||
273 => array(array('_route' => 'regex_not_trailing_slash_POST_method'), array('param'), array('POST' => 0), null),
|
||||
);
|
||||
|
||||
list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m];
|
||||
@ -112,22 +118,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($routes[$m][4]) || '/' === $pathinfo[-1]) {
|
||||
// no-op
|
||||
} elseif ('GET' !== $canonicalMethod) {
|
||||
$allow['GET'] = 'GET';
|
||||
break;
|
||||
} else {
|
||||
return array_replace($ret, $this->redirect($rawPathinfo.'/', $ret['_route']));
|
||||
}
|
||||
|
||||
if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) {
|
||||
if ('GET' !== $canonicalMethod) {
|
||||
$allow['GET'] = 'GET';
|
||||
break;
|
||||
}
|
||||
|
||||
return array_replace($ret, $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)));
|
||||
return $this->redirect($rawPathinfo, $ret['_route'], key($requiredSchemes)) + $ret;
|
||||
}
|
||||
|
||||
if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) {
|
||||
@ -138,7 +135,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if (281 === $m) {
|
||||
if (273 === $m) {
|
||||
break;
|
||||
}
|
||||
$regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m));
|
||||
@ -146,6 +143,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
}
|
||||
}
|
||||
|
||||
throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
|
||||
|
@ -19,7 +19,6 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
$allow = array();
|
||||
$pathinfo = rawurldecode($rawPathinfo);
|
||||
$trimmedPathinfo = rtrim($pathinfo, '/');
|
||||
$context = $this->context;
|
||||
$requestMethod = $canonicalMethod = $context->getMethod();
|
||||
$host = strtolower($context->getHost());
|
||||
@ -32,11 +31,11 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
case '/':
|
||||
// a
|
||||
if (preg_match('#^(?P<d>[^\\.]++)\\.e\\.c\\.b\\.a$#sDi', $host, $hostMatches)) {
|
||||
return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'a')), array());
|
||||
return $this->mergeDefaults(array('_route' => 'a') + $hostMatches, array());
|
||||
}
|
||||
// c
|
||||
if (preg_match('#^(?P<e>[^\\.]++)\\.e\\.c\\.b\\.a$#sDi', $host, $hostMatches)) {
|
||||
return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'c')), array());
|
||||
return $this->mergeDefaults(array('_route' => 'c') + $hostMatches, array());
|
||||
}
|
||||
// b
|
||||
if ('d.c.b.a' === $host) {
|
||||
|
@ -19,14 +19,6 @@ use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
class DumpedRedirectableUrlMatcherTest extends RedirectableUrlMatcherTest
|
||||
{
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Routing\Exception\MethodNotAllowedException
|
||||
*/
|
||||
public function testRedirectWhenNoSlashForNonSafeMethod()
|
||||
{
|
||||
parent::testRedirectWhenNoSlashForNonSafeMethod();
|
||||
}
|
||||
|
||||
protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null)
|
||||
{
|
||||
static $i = 0;
|
||||
|
@ -48,7 +48,7 @@ EOF
|
||||
),
|
||||
<<<EOF
|
||||
root
|
||||
/prefix/segment
|
||||
/prefix/segment/
|
||||
-> prefix_segment
|
||||
-> leading_segment
|
||||
EOF
|
||||
@ -61,7 +61,7 @@ EOF
|
||||
),
|
||||
<<<EOF
|
||||
root
|
||||
/prefix/segment
|
||||
/prefix/segment/
|
||||
-> prefix_segment
|
||||
-> leading_segment
|
||||
EOF
|
||||
@ -75,7 +75,7 @@ EOF
|
||||
),
|
||||
<<<EOF
|
||||
root
|
||||
/group
|
||||
/group/
|
||||
-> nested_segment
|
||||
-> some_segment
|
||||
-> other_segment
|
||||
@ -92,12 +92,12 @@ EOF
|
||||
array('/group/ff/', 'ff'),
|
||||
),
|
||||
<<<EOF
|
||||
/group
|
||||
/group/
|
||||
-> aa
|
||||
-> bb
|
||||
-> cc
|
||||
root
|
||||
/group
|
||||
/group/
|
||||
-> dd
|
||||
-> ee
|
||||
-> ff
|
||||
@ -118,17 +118,17 @@ EOF
|
||||
array('/aaa/333/', 'third_aaa'),
|
||||
),
|
||||
<<<EOF
|
||||
/aaa
|
||||
/aaa/
|
||||
-> first_aaa
|
||||
-> second_aaa
|
||||
-> third_aaa
|
||||
/prefixed
|
||||
-> /prefixed/group
|
||||
/prefixed/
|
||||
-> /prefixed/group/
|
||||
-> -> aa
|
||||
-> -> bb
|
||||
-> -> cc
|
||||
-> root
|
||||
-> /prefixed/group
|
||||
-> /prefixed/group/
|
||||
-> -> dd
|
||||
-> -> ee
|
||||
-> -> ff
|
||||
|
@ -27,6 +27,16 @@ class RedirectableUrlMatcherTest extends UrlMatcherTest
|
||||
$matcher->match('/foo');
|
||||
}
|
||||
|
||||
public function testRedirectWhenSlash()
|
||||
{
|
||||
$coll = new RouteCollection();
|
||||
$coll->add('foo', new Route('/foo'));
|
||||
|
||||
$matcher = $this->getUrlMatcher($coll);
|
||||
$matcher->expects($this->once())->method('redirect')->will($this->returnValue(array()));
|
||||
$matcher->match('/foo/');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
|
||||
*/
|
||||
|
Reference in New Issue
Block a user