[Routing] removed trailing slash support for routes that are not available for GET/HEAD methods (as the redirection will always occurs with a GET/HEAD request, closes #2626)

This commit is contained in:
Fabien Potencier 2011-12-30 19:30:23 +01:00
parent 7b00662df3
commit d12f5b202c
5 changed files with 42 additions and 21 deletions

View File

@ -49,7 +49,16 @@ class ApacheMatcherDumper extends MatcherDumper
$regex = preg_replace('/\?P<.+?>/', '', substr(str_replace(array("\n", ' '), '', $compiledRoute->getRegex()), 1, -3));
$regex = '^'.preg_quote($options['base_uri']).substr($regex, 1);
$hasTrailingSlash = '/$' == substr($regex, -2) && '^/$' != $regex;
$methods = array();
if ($req = $route->getRequirement('_method')) {
$methods = explode('|', strtoupper($req));
// GET and HEAD are equivalent
if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
$methods[] = 'HEAD';
}
}
$hasTrailingSlash = (!$methods || in_array('HEAD', $methods)) && '/$' == substr($regex, -2) && '^/$' != $regex;
$variables = array('E=_ROUTING__route:'.$name);
foreach ($compiledRoute->getVariables() as $i => $variable) {

View File

@ -148,8 +148,18 @@ EOF;
$conditions = array();
$hasTrailingSlash = false;
$matches = false;
$methods = array();
if ($req = $route->getRequirement('_method')) {
$methods = explode('|', strtoupper($req));
// GET and HEAD are equivalent
if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
$methods[] = 'HEAD';
}
}
$supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods));
if (!count($compiledRoute->getVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#', str_replace(array("\n", ' '), '', $compiledRoute->getRegex()), $m)) {
if ($supportsRedirections && substr($m['url'], -1) === '/') {
if ($supportsTrailingSlash && substr($m['url'], -1) === '/') {
$conditions[] = sprintf("rtrim(\$pathinfo, '/') === %s", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true));
$hasTrailingSlash = true;
} else {
@ -161,7 +171,7 @@ EOF;
}
$regex = str_replace(array("\n", ' '), '', $compiledRoute->getRegex());
if ($supportsRedirections && $pos = strpos($regex, '/$')) {
if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) {
$regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
$hasTrailingSlash = true;
}
@ -179,12 +189,7 @@ EOF;
if ($conditions) {
EOF;
if ($req = $route->getRequirement('_method')) {
$methods = explode('|', strtoupper($req));
// GET and HEAD are equivalent
if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
$methods[] = 'HEAD';
}
if ($methods) {
if (1 === count($methods)) {
$code[] = <<<EOF
if (\$this->context->getMethod() != '$methods[0]') {
@ -239,7 +244,7 @@ EOF
}
$code[] = " }";
if ($req) {
if ($methods) {
$code[] = " $gotoname:";
}

View File

@ -42,13 +42,20 @@ RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz4,E=_ROUTING_foo:%1]
# baz5
RewriteCond %{REQUEST_URI} ^/test/([^/]+?)/$
RewriteCond %{REQUEST_METHOD} !^(POST)$ [NC]
RewriteRule .* - [S=2,E=_ROUTING__allow_POST:1]
RewriteCond %{REQUEST_METHOD} !^(GET|HEAD)$ [NC]
RewriteRule .* - [S=2,E=_ROUTING__allow_GET:1,E=_ROUTING__allow_HEAD:1]
RewriteCond %{REQUEST_URI} ^/test/([^/]+?)$
RewriteRule .* $0/ [QSA,L,R=301]
RewriteCond %{REQUEST_URI} ^/test/([^/]+?)/$
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz5,E=_ROUTING_foo:%1]
# baz5unsafe
RewriteCond %{REQUEST_URI} ^/testunsafe/([^/]+?)/$
RewriteCond %{REQUEST_METHOD} !^(POST)$ [NC]
RewriteRule .* - [S=1,E=_ROUTING__allow_POST:1]
RewriteCond %{REQUEST_URI} ^/testunsafe/([^/]+?)/$
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz5unsafe,E=_ROUTING_foo:%1]
# baz6
RewriteCond %{REQUEST_URI} ^/test/baz$
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz6,E=_ROUTING_foo:bar\ baz]

View File

@ -80,28 +80,22 @@ class ProjectUrlMatcher extends Symfony\Tests\Component\Routing\Fixtures\Redirec
}
// baz5
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/]+?)/?$#xs', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/]+?)/$#xs', $pathinfo, $matches)) {
if ($this->context->getMethod() != 'POST') {
$allow[] = 'POST';
goto not_baz5;
}
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'baz5');
}
$matches['_route'] = 'baz5';
return $matches;
}
not_baz5:
// baz.baz6
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/]+?)/?$#xs', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/]+?)/$#xs', $pathinfo, $matches)) {
if ($this->context->getMethod() != 'PUT') {
$allow[] = 'PUT';
goto not_bazbaz6;
}
if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'baz.baz6');
}
$matches['_route'] = 'baz.baz6';
return $matches;
}

View File

@ -62,10 +62,16 @@ class ApacheMatcherDumperTest extends \PHPUnit_Framework_TestCase
$collection->add('baz4', new Route(
'/test/{foo}/'
));
// trailing slash and method
// trailing slash and safe method
$collection->add('baz5', new Route(
'/test/{foo}/',
array(),
array('_method' => 'get')
));
// trailing slash and unsafe method
$collection->add('baz5unsafe', new Route(
'/testunsafe/{foo}/',
array(),
array('_method' => 'post')
));
// complex