From d06bb910b21284c0a3e83161a836c200f655ef20 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 6 Mar 2016 10:27:56 +0100 Subject: [PATCH] deprecate most characters in service ids --- .../Compiler/AddConsoleCommandPassTest.php | 15 +-- .../DependencyInjection/Container.php | 9 ++ .../DependencyInjection/ContainerBuilder.php | 8 ++ .../Tests/ContainerBuilderTest.php | 104 +++++++++++++++++- .../Tests/ContainerTest.php | 37 +++++++ .../Tests/Fixtures/containers/container9.php | 2 +- .../Tests/Fixtures/xml/services9.xml | 2 +- .../Tests/Fixtures/yaml/services9.yml | 2 +- 8 files changed, 168 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php index 72902c99cd4d0..49a5414170efc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php @@ -31,13 +31,14 @@ public function testProcess($public) $definition = new Definition('%my-command.class%'); $definition->setPublic($public); $definition->addTag('console.command'); - $container->setDefinition('my-command', $definition); + $container->setDefinition('app.command', $definition); $container->compile(); $alias = 'console.command.symfony_bundle_frameworkbundle_tests_dependencyinjection_compiler_mycommand'; + if ($container->hasAlias($alias)) { - $this->assertSame('my-command', (string) $container->getAlias($alias)); + $this->assertSame('app.command', (string) $container->getAlias($alias)); } else { // The alias is replaced by a Definition by the ReplaceAliasByActualDefinitionPass // in case the original service is private @@ -59,7 +60,7 @@ public function visibilityProvider() /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The service "my-command" tagged "console.command" must not be abstract. + * @expectedExceptionMessage The service "app.command" tagged "console.command" must not be abstract. */ public function testProcessThrowAnExceptionIfTheServiceIsAbstract() { @@ -69,14 +70,14 @@ public function testProcessThrowAnExceptionIfTheServiceIsAbstract() $definition = new Definition('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'); $definition->addTag('console.command'); $definition->setAbstract(true); - $container->setDefinition('my-command', $definition); + $container->setDefinition('app.command', $definition); $container->compile(); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The service "my-command" tagged "console.command" must be a subclass of "Symfony\Component\Console\Command\Command". + * @expectedExceptionMessage The service "app.command" tagged "console.command" must be a subclass of "Symfony\Component\Console\Command\Command". */ public function testProcessThrowAnExceptionIfTheServiceIsNotASubclassOfCommand() { @@ -85,7 +86,7 @@ public function testProcessThrowAnExceptionIfTheServiceIsNotASubclassOfCommand() $definition = new Definition('SplObjectStorage'); $definition->addTag('console.command'); - $container->setDefinition('my-command', $definition); + $container->setDefinition('app.command', $definition); $container->compile(); } @@ -96,7 +97,7 @@ public function testHttpKernelRegisterCommandsIngoreCommandAsAService() $container->addCompilerPass(new AddConsoleCommandPass()); $definition = new Definition('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'); $definition->addTag('console.command'); - $container->setDefinition('my-command', $definition); + $container->setDefinition('app.command', $definition); $container->compile(); $application = $this->getMock('Symfony\Component\Console\Application'); diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index 34e0523340a40..b0bbe64c5df7a 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -58,6 +58,11 @@ */ class Container implements ResettableContainerInterface { + /** + * @internal + */ + const INVALID_SERVICE_ID_REGEX = '/[\s\x00-\x1F\x7F!?"\'`@-]/'; + /** * @var ParameterBagInterface */ @@ -163,6 +168,10 @@ public function setParameter($name, $value) */ public function set($id, $service) { + if (preg_match(self::INVALID_SERVICE_ID_REGEX, $id)) { + @trigger_error(sprintf('Using whitespaces, control sequences, quotes, or the characters "!", "?", "@", "`" and "-" in service ids ("%s" given) is deprecated since Symfony 3.1 and will throw an exception in 4.0.', $id), E_USER_DEPRECATED); + } + $id = strtolower($id); if ('service_container' === $id) { diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index aae5460ace603..e5201d636bfc9 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -613,6 +613,10 @@ public function setAlias($alias, $id) throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".', $alias)); } + if (preg_match(self::INVALID_SERVICE_ID_REGEX, $alias)) { + @trigger_error(sprintf('Using whitespaces, control sequences, quotes, or the characters "!", "?", "@", "`" and "-" in service ids ("%s" given) is deprecated since Symfony 3.1 and will throw an exception in 4.0.', $alias), E_USER_DEPRECATED); + } + unset($this->definitions[$alias]); $this->aliasDefinitions[$alias] = $id; @@ -731,6 +735,10 @@ public function getDefinitions() */ public function setDefinition($id, Definition $definition) { + if (preg_match(self::INVALID_SERVICE_ID_REGEX, $id)) { + @trigger_error(sprintf('Using whitespaces, control sequences, quotes, or the characters "!", "?", "@", "`" and "-" in service ids ("%s" given) is deprecated since Symfony 3.1 and will throw an exception in 4.0.', $id), E_USER_DEPRECATED); + } + if ($this->isFrozen()) { throw new BadMethodCallException('Adding definition to a frozen container is not allowed'); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 874a0e78b7891..e202980196f6a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -20,7 +20,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; -use Symfony\Component\DependencyInjection\Exception\InactiveScopeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Loader\ClosureLoader; @@ -732,6 +731,109 @@ public function testAutowiring() $this->assertEquals('a', (string) $container->getDefinition('b')->getArgument(0)); } + + /** + * @dataProvider getSpecialCharServiceIds + */ + public function testSpecialCharInServiceIdTriggersDeprecationInSet($id) + { + $container = new ContainerBuilder(); + + $deprecations = array(); + set_error_handler(function ($type, $msg) use (&$deprecations) { + if (E_USER_DEPRECATED === $type) { + $deprecations[] = $msg; + } + }); + + $container->set($id, new \stdClass()); + + restore_error_handler(); + + $this->assertCount(1, $deprecations); + $this->assertSame(sprintf('Using whitespaces, control sequences, quotes, or the characters "!", "?", "@", "`" and "-" in service ids ("%s" given) is deprecated since Symfony 3.1 and will throw an exception in 4.0.', $id), $deprecations[0]); + } + + /** + * @dataProvider getSpecialCharServiceIds + */ + public function testSpecialCharInServiceIdTriggersDeprecationInRegister($id) + { + $container = new ContainerBuilder(); + + $deprecations = array(); + set_error_handler(function ($type, $msg) use (&$deprecations) { + if (E_USER_DEPRECATED === $type) { + $deprecations[] = $msg; + } + }); + + $container->register($id, 'Foo'); + + restore_error_handler(); + + $this->assertCount(1, $deprecations); + $this->assertSame(sprintf('Using whitespaces, control sequences, quotes, or the characters "!", "?", "@", "`" and "-" in service ids ("%s" given) is deprecated since Symfony 3.1 and will throw an exception in 4.0.', $id), $deprecations[0]); + } + + /** + * @dataProvider getSpecialCharServiceIds + */ + public function testSpecialCharInServiceIdTriggersDeprecationInSetDefinition($id) + { + $container = new ContainerBuilder(); + + $deprecations = array(); + set_error_handler(function ($type, $msg) use (&$deprecations) { + if (E_USER_DEPRECATED === $type) { + $deprecations[] = $msg; + } + }); + + $container->setDefinition($id, new Definition('Foo')); + + restore_error_handler(); + + $this->assertCount(1, $deprecations); + $this->assertSame(sprintf('Using whitespaces, control sequences, quotes, or the characters "!", "?", "@", "`" and "-" in service ids ("%s" given) is deprecated since Symfony 3.1 and will throw an exception in 4.0.', $id), $deprecations[0]); + } + + /** + * @dataProvider getSpecialCharServiceIds + */ + public function testSpecialCharInServiceIdTriggersDeprecationInSetAlias($id) + { + $container = new ContainerBuilder(); + + $deprecations = array(); + set_error_handler(function ($type, $msg) use (&$deprecations) { + if (E_USER_DEPRECATED === $type) { + $deprecations[] = $msg; + } + }); + + $container->setAlias($id, 'foo'); + + restore_error_handler(); + + $this->assertCount(1, $deprecations); + $this->assertSame(sprintf('Using whitespaces, control sequences, quotes, or the characters "!", "?", "@", "`" and "-" in service ids ("%s" given) is deprecated since Symfony 3.1 and will throw an exception in 4.0.', $id), $deprecations[0]); + } + + public function getSpecialCharServiceIds() + { + return array( + 'contains whitespace' => array('service id'), + 'contains ASCII control sequence' => array("service\x07id"), + 'contains exclamation mark' => array('service!'), + 'contains question mark' => array('service?'), + 'contains @' => array('@service_id'), + 'contains double quote' => array('"my_service"'), + 'contains single quote' => array("'my_service'"), + 'contains backtick' => array('`my_service`'), + 'contains dash' => array('my-service'), + ); + } } class FooClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php index a9f41c56a22bb..e4761ebddf4fe 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php @@ -150,6 +150,43 @@ public function testSetReplacesAlias() $this->assertSame($foo, $c->get('alias'), '->set() replaces an existing alias'); } + /** + * @dataProvider getSpecialCharServiceIds + */ + public function testSpecialCharInServiceIdTriggersDeprecationInSet($id) + { + $container = new ProjectServiceContainer(); + + $deprecations = array(); + set_error_handler(function ($type, $msg) use (&$deprecations) { + if (E_USER_DEPRECATED === $type) { + $deprecations[] = $msg; + } + }); + + $container->set($id, new \stdClass()); + + restore_error_handler(); + + $this->assertCount(1, $deprecations); + $this->assertSame(sprintf('Using whitespaces, control sequences, quotes, or the characters "!", "?", "@", "`" and "-" in service ids ("%s" given) is deprecated since Symfony 3.1 and will throw an exception in 4.0.', $id), $deprecations[0]); + } + + public function getSpecialCharServiceIds() + { + return array( + 'contains whitespace' => array('service id'), + 'contains ASCII control sequence' => array("service\x07id"), + 'contains exclamation mark' => array('service!'), + 'contains question mark' => array('service?'), + 'contains @' => array('@service_id'), + 'contains double quote' => array('"my_service"'), + 'contains single quote' => array("'my_service'"), + 'contains backtick' => array('`my_service`'), + 'contains dash' => array('my-service'), + ); + } + public function testGet() { $sc = new ProjectServiceContainer(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php index 96f334fdd153f..abba0a5aab689 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php @@ -87,7 +87,7 @@ ; $container ->register('decorator_service_with_name', 'stdClass') - ->setDecoratedService('decorated', 'decorated.pif-pouf') + ->setDecoratedService('decorated', 'decorated.pif_pouf') ; $container ->register('deprecated_service', 'stdClass') diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml index 4ddb8655c55f2..99ca10f74287c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -86,7 +86,7 @@ - + The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed. diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml index 6efeb37e747d4..1f475f5cc7db6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -75,7 +75,7 @@ services: decorator_service_with_name: class: stdClass decorates: decorated - decoration_inner_name: decorated.pif-pouf + decoration_inner_name: decorated.pif_pouf deprecated_service: class: stdClass deprecated: The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed.