From 8f359cc04706d319cccee11f0bb2c857dbea2072 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 29 Jun 2018 14:29:05 +0200 Subject: [PATCH] [FrameworkBundle] allow turning routes to utf8 mode by default --- UPGRADE-4.2.md | 8 ++++ .../DependencyInjection/Configuration.php | 1 + .../FrameworkExtension.php | 3 ++ .../Resources/config/routing.xml | 1 + .../Routing/DelegatingLoader.php | 7 ++- .../DependencyInjection/ConfigurationTest.php | 1 + .../Tests/Routing/DelegatingLoaderTest.php | 43 +++++++++++++++++++ 7 files changed, 63 insertions(+), 1 deletion(-) diff --git a/UPGRADE-4.2.md b/UPGRADE-4.2.md index b50dde0b79cd0..8d310cc6fba54 100644 --- a/UPGRADE-4.2.md +++ b/UPGRADE-4.2.md @@ -37,6 +37,14 @@ Form {% endfor %} ``` +FrameworkBundle +--------------- + + * The `framework.router.utf8` configuration option has been added. If your app's charset + is UTF-8 (see kernel's `getCharset()` method), it is recommended to set it to `true`: + this will generate 404s for non-UTF-8 URLs, which are incompatible with you app anyway, + and will allow dumping optimized routers and using Unicode classes in requirements. + Security -------- diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index e9c147b7f92fd..8f592cd22eef8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -452,6 +452,7 @@ private function addRouterSection(ArrayNodeDefinition $rootNode) ) ->defaultTrue() ->end() + ->booleanNode('utf8')->defaultFalse()->end() ->end() ->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index b3908ace88487..b1b31e6cda036 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -687,6 +687,9 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co $loader->load('routing.xml'); + if ($config['utf8']) { + $container->getDefinition('routing.loader')->replaceArgument(2, array('utf8' => true)); + } if (!interface_exists(ContainerBagInterface::class)) { $container->getDefinition('router.default') ->replaceArgument(0, new Reference('service_container')) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml index a2e24b00c8e3c..03bac811b2553 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -48,6 +48,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php index 0755cc161a3f0..709fc65faff21 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php @@ -28,14 +28,16 @@ class DelegatingLoader extends BaseDelegatingLoader { protected $parser; private $loading = false; + private $defaultOptions; /** * @param ControllerNameParser $parser A ControllerNameParser instance * @param LoaderResolverInterface $resolver A LoaderResolverInterface instance */ - public function __construct(ControllerNameParser $parser, LoaderResolverInterface $resolver) + public function __construct(ControllerNameParser $parser, LoaderResolverInterface $resolver, array $defaultOptions = array()) { $this->parser = $parser; + $this->defaultOptions = $defaultOptions; parent::__construct($resolver); } @@ -73,6 +75,9 @@ public function load($resource, $type = null) } foreach ($collection->all() as $route) { + if ($this->defaultOptions) { + $route->setOptions($route->getOptions() + $this->defaultOptions); + } if (!is_string($controller = $route->getDefault('_controller'))) { continue; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 58f7e564c97f3..f60594f54948f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -226,6 +226,7 @@ protected static function getBundleDefaultConfig() 'http_port' => 80, 'https_port' => 443, 'strict_requirements' => true, + 'utf8' => false, ), 'session' => array( 'enabled' => false, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php index a0ad94b33e02e..2cef381f7272c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php @@ -22,6 +22,49 @@ public function testConstructorApi() $this->assertTrue(true, '__construct() takes a ControllerNameParser and LoaderResolverInterface respectively as its first and second argument.'); } + public function testLoadDefaultOptions() + { + $controllerNameParser = $this->getMockBuilder(ControllerNameParser::class) + ->disableOriginalConstructor() + ->getMock(); + + $loaderResolver = $this->getMockBuilder(LoaderResolverInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $loader = $this->getMockBuilder(LoaderInterface::class)->getMock(); + + $loaderResolver->expects($this->once()) + ->method('resolve') + ->willReturn($loader); + + $routeCollection = new RouteCollection(); + $routeCollection->add('foo', new Route('/', array(), array(), array('utf8' => false))); + $routeCollection->add('bar', new Route('/', array(), array(), array('foo' => 123))); + + $loader->expects($this->once()) + ->method('load') + ->willReturn($routeCollection); + + $delegatingLoader = new DelegatingLoader($controllerNameParser, $loaderResolver, array('utf8' => true)); + + $loadedRouteCollection = $delegatingLoader->load('foo'); + $this->assertCount(2, $loadedRouteCollection); + + $expected = array( + 'compiler_class' => 'Symfony\Component\Routing\RouteCompiler', + 'utf8' => false, + ); + $this->assertSame($expected, $routeCollection->get('foo')->getOptions()); + + $expected = array( + 'compiler_class' => 'Symfony\Component\Routing\RouteCompiler', + 'foo' => 123, + 'utf8' => true, + ); + $this->assertSame($expected, $routeCollection->get('bar')->getOptions()); + } + /** * @group legacy * @expectedDeprecation Referencing controllers with foo:bar:baz is deprecated since Symfony 4.1, use "some_parsed::controller" instead.