diff --git a/UPGRADE-4.2.md b/UPGRADE-4.2.md
index b50dde0b79..8d310cc6fb 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 e9c147b7f9..8f592cd22e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -452,6 +452,7 @@ class Configuration implements ConfigurationInterface
)
->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 b3908ace88..b1b31e6cda 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -687,6 +687,9 @@ class FrameworkExtension extends Extension
$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 a2e24b00c8..03bac811b2 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 0755cc161a..709fc65faf 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 @@ class DelegatingLoader extends BaseDelegatingLoader
}
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 58f7e564c9..f60594f549 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
@@ -226,6 +226,7 @@ class ConfigurationTest extends TestCase
'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 a0ad94b33e..2cef381f72 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php
@@ -22,6 +22,49 @@ class DelegatingLoaderTest extends TestCase
$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.