Merge branch '2.7' into 2.8

* 2.7:
  Preserve percent-encoding in URLs when performing redirects in the UrlMatcher
  [Console] Fix a bug when passing a letter that could be an alias
  add missing validation options to XSD file
This commit is contained in:
Fabien Potencier 2017-12-14 11:27:36 -08:00
commit bdbdf73bdd
16 changed files with 160 additions and 15 deletions

View File

@ -207,6 +207,8 @@
<xsd:attribute name="cache" type="xsd:string" /> <xsd:attribute name="cache" type="xsd:string" />
<xsd:attribute name="enable-annotations" type="xsd:boolean" /> <xsd:attribute name="enable-annotations" type="xsd:boolean" />
<xsd:attribute name="static-method" type="xsd:boolean" /> <xsd:attribute name="static-method" type="xsd:boolean" />
<xsd:attribute name="translation-domain" type="xsd:string" />
<xsd:attribute name="strict-email" type="xsd:boolean" />
<xsd:attribute name="api" type="validator_api_version" /> <xsd:attribute name="api" type="validator_api_version" />
</xsd:complexType> </xsd:complexType>

View File

@ -0,0 +1,7 @@
<?php
$container->loadFromExtension('framework', array(
'validation' => array(
'strict_email' => true,
),
));

View File

@ -0,0 +1,7 @@
<?php
$container->loadFromExtension('framework', array(
'validation' => array(
'translation_domain' => 'messages',
),
));

View File

@ -0,0 +1,11 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
<framework:config>
<framework:validation strict-email="true" />
</framework:config>
</container>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
<framework:config>
<framework:validation translation-domain="messages" />
</framework:config>
</container>

View File

@ -0,0 +1,3 @@
framework:
validation:
strict_email: true

View File

@ -0,0 +1,3 @@
framework:
validation:
translation_domain: messages

View File

@ -428,6 +428,20 @@ abstract class FrameworkExtensionTest extends TestCase
// no cache, no annotations, no static methods // no cache, no annotations, no static methods
} }
public function testValidationTranslationDomain()
{
$container = $this->createContainerFromFile('validation_translation_domain');
$this->assertSame('messages', $container->getParameter('validator.translation_domain'));
}
public function testValidationStrictEmail()
{
$container = $this->createContainerFromFile('validation_strict_email');
$this->assertTrue($container->getDefinition('validator.email')->getArgument(0));
}
public function testFormsCanBeEnabledWithoutCsrfProtection() public function testFormsCanBeEnabledWithoutCsrfProtection()
{ {
$container = $this->createContainerFromFile('form_no_csrf'); $container = $this->createContainerFromFile('form_no_csrf');

View File

@ -286,6 +286,8 @@ class ArgvInput extends Input
} }
if (0 === strpos($token, '-') && 0 !== strpos($token, '--')) { if (0 === strpos($token, '-') && 0 !== strpos($token, '--')) {
$noValue = explode('=', $token);
$token = $noValue[0];
$searchableToken = str_replace('-', '', $token); $searchableToken = str_replace('-', '', $token);
$searchableValue = str_replace('-', '', $value); $searchableValue = str_replace('-', '', $value);
if ('' !== $searchableToken && '' !== $searchableValue && false !== strpos($searchableToken, $searchableValue)) { if ('' !== $searchableToken && '' !== $searchableValue && false !== strpos($searchableToken, $searchableValue)) {

View File

@ -299,6 +299,9 @@ class ArgvInputTest extends TestCase
$input = new ArgvInput(array('cli.php', '-fh')); $input = new ArgvInput(array('cli.php', '-fh'));
$this->assertTrue($input->hasParameterOption('-fh'), '->hasParameterOption() returns true if the given short option is in the raw input'); $this->assertTrue($input->hasParameterOption('-fh'), '->hasParameterOption() returns true if the given short option is in the raw input');
$input = new ArgvInput(array('cli.php', '-e=test'));
$this->assertFalse($input->hasParameterOption('-s'), '->hasParameterOption() returns true if the given short option is in the raw input');
$input = new ArgvInput(array('cli.php', '--foo', 'foo')); $input = new ArgvInput(array('cli.php', '--foo', 'foo'));
$this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given short option is in the raw input'); $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given short option is in the raw input');

View File

@ -96,10 +96,10 @@ EOF;
$code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n"); $code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n");
return <<<EOF return <<<EOF
public function match(\$pathinfo) public function match(\$rawPathinfo)
{ {
\$allow = array(); \$allow = array();
\$pathinfo = rawurldecode(\$pathinfo); \$pathinfo = rawurldecode(\$rawPathinfo);
\$context = \$this->context; \$context = \$this->context;
\$request = \$this->request; \$request = \$this->request;
@ -284,7 +284,7 @@ EOF;
if ($hasTrailingSlash) { if ($hasTrailingSlash) {
$code .= <<<EOF $code .= <<<EOF
if (substr(\$pathinfo, -1) !== '/') { if (substr(\$pathinfo, -1) !== '/') {
return \$this->redirect(\$pathinfo.'/', '$name'); return \$this->redirect(\$rawPathinfo.'/', '$name');
} }
@ -299,7 +299,7 @@ EOF;
$code .= <<<EOF $code .= <<<EOF
\$requiredSchemes = $schemes; \$requiredSchemes = $schemes;
if (!isset(\$requiredSchemes[\$this->context->getScheme()])) { if (!isset(\$requiredSchemes[\$this->context->getScheme()])) {
return \$this->redirect(\$pathinfo, '$name', key(\$requiredSchemes)); return \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes));
} }

View File

@ -15,10 +15,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
$this->context = $context; $this->context = $context;
} }
public function match($pathinfo) public function match($rawPathinfo)
{ {
$allow = array(); $allow = array();
$pathinfo = rawurldecode($pathinfo); $pathinfo = rawurldecode($rawPathinfo);
$context = $this->context; $context = $this->context;
$request = $this->request; $request = $this->request;

View File

@ -15,10 +15,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
$this->context = $context; $this->context = $context;
} }
public function match($pathinfo) public function match($rawPathinfo)
{ {
$allow = array(); $allow = array();
$pathinfo = rawurldecode($pathinfo); $pathinfo = rawurldecode($rawPathinfo);
$context = $this->context; $context = $this->context;
$request = $this->request; $request = $this->request;
@ -67,7 +67,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
// baz3 // baz3
if ('/test/baz3' === rtrim($pathinfo, '/')) { if ('/test/baz3' === rtrim($pathinfo, '/')) {
if (substr($pathinfo, -1) !== '/') { if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'baz3'); return $this->redirect($rawPathinfo.'/', 'baz3');
} }
return array('_route' => 'baz3'); return array('_route' => 'baz3');
@ -78,7 +78,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
// baz4 // baz4
if (preg_match('#^/test/(?P<foo>[^/]++)/?$#s', $pathinfo, $matches)) { if (preg_match('#^/test/(?P<foo>[^/]++)/?$#s', $pathinfo, $matches)) {
if (substr($pathinfo, -1) !== '/') { if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'baz4'); return $this->redirect($rawPathinfo.'/', 'baz4');
} }
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ());
@ -171,7 +171,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
// hey // hey
if ('/multi/hey' === rtrim($pathinfo, '/')) { if ('/multi/hey' === rtrim($pathinfo, '/')) {
if (substr($pathinfo, -1) !== '/') { if (substr($pathinfo, -1) !== '/') {
return $this->redirect($pathinfo.'/', 'hey'); return $this->redirect($rawPathinfo.'/', 'hey');
} }
return array('_route' => 'hey'); return array('_route' => 'hey');
@ -318,7 +318,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
if ('/secure' === $pathinfo) { if ('/secure' === $pathinfo) {
$requiredSchemes = array ( 'https' => 0,); $requiredSchemes = array ( 'https' => 0,);
if (!isset($requiredSchemes[$this->context->getScheme()])) { if (!isset($requiredSchemes[$this->context->getScheme()])) {
return $this->redirect($pathinfo, 'secure', key($requiredSchemes)); return $this->redirect($rawPathinfo, 'secure', key($requiredSchemes));
} }
return array('_route' => 'secure'); return array('_route' => 'secure');
@ -328,7 +328,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
if ('/nonsecure' === $pathinfo) { if ('/nonsecure' === $pathinfo) {
$requiredSchemes = array ( 'http' => 0,); $requiredSchemes = array ( 'http' => 0,);
if (!isset($requiredSchemes[$this->context->getScheme()])) { if (!isset($requiredSchemes[$this->context->getScheme()])) {
return $this->redirect($pathinfo, 'nonsecure', key($requiredSchemes)); return $this->redirect($rawPathinfo, 'nonsecure', key($requiredSchemes));
} }
return array('_route' => 'nonsecure'); return array('_route' => 'nonsecure');

View File

@ -15,10 +15,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
$this->context = $context; $this->context = $context;
} }
public function match($pathinfo) public function match($rawPathinfo)
{ {
$allow = array(); $allow = array();
$pathinfo = rawurldecode($pathinfo); $pathinfo = rawurldecode($rawPathinfo);
$context = $this->context; $context = $this->context;
$request = $this->request; $request = $this->request;

View File

@ -13,11 +13,39 @@ namespace Symfony\Component\Routing\Tests\Matcher\Dumper;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper; use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper;
use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
class PhpMatcherDumperTest extends TestCase class PhpMatcherDumperTest extends TestCase
{ {
/**
* @var string
*/
private $matcherClass;
/**
* @var string
*/
private $dumpPath;
protected function setUp()
{
parent::setUp();
$this->matcherClass = uniqid('ProjectUrlMatcher');
$this->dumpPath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'php_matcher.'.$this->matcherClass.'.php';
}
protected function tearDown()
{
parent::tearDown();
@unlink($this->dumpPath);
}
/** /**
* @expectedException \LogicException * @expectedException \LogicException
*/ */
@ -36,6 +64,23 @@ class PhpMatcherDumperTest extends TestCase
$dumper->dump(); $dumper->dump();
} }
public function testRedirectPreservesUrlEncoding()
{
$collection = new RouteCollection();
$collection->add('foo', new Route('/foo:bar/'));
$class = $this->generateDumpedMatcher($collection, true);
$matcher = $this->getMockBuilder($class)
->setMethods(array('redirect'))
->setConstructorArgs(array(new RequestContext()))
->getMock();
$matcher->expects($this->once())->method('redirect')->with('/foo%3Abar/', 'foo');
$matcher->match('/foo%3Abar');
}
/** /**
* @dataProvider getRouteCollections * @dataProvider getRouteCollections
*/ */
@ -285,4 +330,31 @@ class PhpMatcherDumperTest extends TestCase
array($rootprefixCollection, 'url_matcher3.php', array()), array($rootprefixCollection, 'url_matcher3.php', array()),
); );
} }
/**
* @param $dumper
*/
private function generateDumpedMatcher(RouteCollection $collection, $redirectableStub = false)
{
$options = array('class' => $this->matcherClass);
if ($redirectableStub) {
$options['base_class'] = '\Symfony\Component\Routing\Tests\Matcher\Dumper\RedirectableUrlMatcherStub';
}
$dumper = new PhpMatcherDumper($collection);
$code = $dumper->dump($options);
file_put_contents($this->dumpPath, $code);
include $this->dumpPath;
return $this->matcherClass;
}
}
abstract class RedirectableUrlMatcherStub extends UrlMatcher implements RedirectableUrlMatcherInterface
{
public function redirect($path, $route, $scheme = null)
{
}
} }

View File

@ -69,4 +69,14 @@ class RedirectableUrlMatcherTest extends TestCase
; ;
$matcher->match('/foo'); $matcher->match('/foo');
} }
public function testRedirectPreservesUrlEncoding()
{
$coll = new RouteCollection();
$coll->add('foo', new Route('/foo:bar/'));
$matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext()));
$matcher->expects($this->once())->method('redirect')->with('/foo%3Abar/');
$matcher->match('/foo%3Abar');
}
} }