From 4b6fab0b2cf27c873e00bd02fe3364d8d64a6ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Baptiste=20Clavi=C3=A9?= Date: Fri, 7 Aug 2015 17:07:23 +0200 Subject: [PATCH 1/6] [DI] Add a deprecated status to definitions --- .../DependencyInjection/Definition.php | 31 +++++++++++++++++++ .../Tests/DefinitionTest.php | 12 +++++++ 2 files changed, 43 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 49d705cf7d..8dc0af6fa9 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -38,6 +38,7 @@ class Definition private $public = true; private $synthetic = false; private $abstract = false; + private $deprecated = false; private $synchronized = false; private $lazy = false; private $decoratedService; @@ -829,6 +830,36 @@ class Definition return $this->abstract; } + /** + * Whether this definition is deprecated, that means it should not be called + * anymore. + * + * @param bool $status + * + * @return Definition the current instance + * + * @api + */ + public function setDeprecated($status = true) + { + $this->deprecated = (bool) $status; + + return $this; + } + + /** + * Whether this definition is deprecated, that means it should not be called + * anymore. + * + * @return bool + * + * @api + */ + public function isDeprecated() + { + return $this->deprecated; + } + /** * Sets a configurator to call after the service is fully initialized. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php index e8df122953..131581096f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -233,6 +233,18 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase $this->assertTrue($def->isAbstract(), '->isAbstract() returns true if the instance must not be public.'); } + /** + * @covers Symfony\Component\DependencyInjection\Definition::setDeprecated + * @covers Symfony\Component\DependencyInjection\Definition::isDeprecated + */ + public function testSetIsDeprecated() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isDeprecated(), '->isDeprecated() returns false by default'); + $this->assertSame($def, $def->setDeprecated(true), '->setDeprecated() implements a fluent interface'); + $this->assertTrue($def->isDeprecated(), '->isDeprecated() returns true if the instance should not be used anymore.'); + } + /** * @covers Symfony\Component\DependencyInjection\Definition::setConfigurator * @covers Symfony\Component\DependencyInjection\Definition::getConfigurator From 8f6c21c2b60cd7cdf3246d37343265ffa6e4a0cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Baptiste=20Clavi=C3=A9?= Date: Fri, 7 Aug 2015 17:13:16 +0200 Subject: [PATCH 2/6] [DI] Supports the deprecated tag in loaders --- .../Component/DependencyInjection/Loader/XmlFileLoader.php | 2 +- .../Component/DependencyInjection/Loader/YamlFileLoader.php | 4 ++++ .../Loader/schema/dic/services/services-1.0.xsd | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index de2ea9852a..de57789da4 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -147,7 +147,7 @@ class XmlFileLoader extends FileLoader $definition = new Definition(); } - foreach (array('class', 'shared', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'lazy', 'abstract') as $key) { + foreach (array('class', 'shared', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'lazy', 'abstract', 'deprecated') as $key) { if ($value = $service->getAttribute($key)) { if (in_array($key, array('factory-class', 'factory-method', 'factory-service'))) { @trigger_error(sprintf('The "%s" attribute of service "%s" in file "%s" is deprecated since version 2.6 and will be removed in 3.0. Use the "factory" element instead.', $key, (string) $service->getAttribute('id'), $file), E_USER_DEPRECATED); diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 74a7ed4f1a..f5c2166b53 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -196,6 +196,10 @@ class YamlFileLoader extends FileLoader $definition->setAbstract($service['abstract']); } + if (isset($service['deprecated'])) { + $definition->setDeprecated($service['deprecated']); + } + if (isset($service['factory'])) { if (is_string($service['factory'])) { if (strpos($service['factory'], ':') !== false && strpos($service['factory'], '::') === false) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index 15bdd791dc..c521a4936e 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -94,6 +94,7 @@ + From 2f37cb184f3be0d0eb2db583b4cf0b83b9af0047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Baptiste=20Clavi=C3=A9?= Date: Fri, 7 Aug 2015 18:08:40 +0200 Subject: [PATCH 3/6] [DI] Dump the deprecated status --- .../DependencyInjection/Dumper/PhpDumper.php | 14 +++++++++++++- .../DependencyInjection/Dumper/XmlDumper.php | 3 +++ .../DependencyInjection/Dumper/YamlDumper.php | 4 ++++ .../Tests/Fixtures/containers/container9.php | 4 ++++ .../Tests/Fixtures/graphviz/services9.dot | 1 + .../Tests/Fixtures/php/services9.php | 18 ++++++++++++++++++ .../Tests/Fixtures/php/services9_compiled.php | 18 ++++++++++++++++++ .../Tests/Fixtures/xml/services9.xml | 1 + .../Tests/Fixtures/yaml/services9.yml | 3 +++ 9 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index c1c6f80f70..c9b8d0e358 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -592,7 +592,15 @@ class PhpDumper extends Dumper $return[] = sprintf("@throws InactiveScopeException when the '%s' service is requested while the '%s' scope is not active", $id, $scope); } - $return = implode("\n * ", $return); + if ($definition->isDeprecated()) { + if ($return && 0 === strpos($return[count($return) - 1], '@return')) { + $return[] = ''; + } + + $return[] = '@deprecated'; + } + + $return = str_replace("\n * \n", "\n *\n", implode("\n * ", $return)); $doc = ''; if ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope) { @@ -652,6 +660,10 @@ EOF; if ($definition->isSynthetic()) { $code .= sprintf(" throw new RuntimeException('You have requested a synthetic service (\"%s\"). The DIC does not know how to construct this service.');\n }\n", $id); } else { + if ($definition->isDeprecated()) { + $code .= sprintf(" @trigger_error('The service %s has been marked as deprecated. You should stop using it.', E_USER_DEPRECATED);\n\n", $id); + } + $code .= $this->addServiceInclude($id, $definition). $this->addServiceLocalTempVariables($id, $definition). diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index c405c4ce90..f9fee16d4b 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -148,6 +148,9 @@ class XmlDumper extends Dumper if ($definition->isLazy()) { $service->setAttribute('lazy', 'true'); } + if ($definition->isDeprecated()) { + $service->setAttribute('deprecated', 'true'); + } if (null !== $decorated = $definition->getDecoratedService()) { list($decorated, $renamedId, $priority) = $decorated; $service->setAttribute('decorates', $decorated); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index 3fac53e01c..987bb6d26c 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -104,6 +104,10 @@ class YamlDumper extends Dumper $code .= sprintf(" synchronized: true\n"); } + if ($definition->isDeprecated()) { + $code .= " deprecated: true\n"; + } + if ($definition->getFactoryClass(false)) { $code .= sprintf(" factory_class: %s\n", $definition->getFactoryClass(false)); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php index 5d63e348e5..96f334fdd1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php @@ -89,6 +89,10 @@ $container ->register('decorator_service_with_name', 'stdClass') ->setDecoratedService('decorated', 'decorated.pif-pouf') ; +$container + ->register('deprecated_service', 'stdClass') + ->setDeprecated(true) +; $container ->register('new_factory', 'FactoryClass') ->setProperty('foo', 'bar') diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot index b3b424e2e7..f6536980aa 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot @@ -17,6 +17,7 @@ digraph sc { node_decorated [label="decorated\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_decorator_service [label="decorator_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_decorator_service_with_name [label="decorator_service_with_name\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_deprecated_service [label="deprecated_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_new_factory [label="new_factory\nFactoryClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_factory_service [label="factory_service\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_new_factory_service [label="new_factory_service\nFooBarBaz\n", shape=record, fillcolor="#eeeeee", style="filled"]; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php index 5977c1c6e7..1dcc429c3f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -33,6 +33,7 @@ class ProjectServiceContainer extends Container 'decorated' => 'getDecoratedService', 'decorator_service' => 'getDecoratorServiceService', 'decorator_service_with_name' => 'getDecoratorServiceWithNameService', + 'deprecated_service' => 'getDeprecatedServiceService', 'factory_service' => 'getFactoryServiceService', 'foo' => 'getFooService', 'foo.baz' => 'getFoo_BazService', @@ -143,6 +144,23 @@ class ProjectServiceContainer extends Container return $this->services['decorator_service_with_name'] = new \stdClass(); } + /** + * Gets the 'deprecated_service' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + * + * @deprecated + */ + protected function getDeprecatedServiceService() + { + @trigger_error('The service deprecated_service has been marked as deprecated. You should stop using it.', E_USER_DEPRECATED); + + return $this->services['deprecated_service'] = new \stdClass(); + } + /** * Gets the 'factory_service' service. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index 8c1e71f437..447e5454b2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -37,6 +37,7 @@ class ProjectServiceContainer extends Container 'configured_service' => 'getConfiguredServiceService', 'decorator_service' => 'getDecoratorServiceService', 'decorator_service_with_name' => 'getDecoratorServiceWithNameService', + 'deprecated_service' => 'getDeprecatedServiceService', 'factory_service' => 'getFactoryServiceService', 'foo' => 'getFooService', 'foo.baz' => 'getFoo_BazService', @@ -144,6 +145,23 @@ class ProjectServiceContainer extends Container return $this->services['decorator_service_with_name'] = new \stdClass(); } + /** + * Gets the 'deprecated_service' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + * + * @deprecated + */ + protected function getDeprecatedServiceService() + { + @trigger_error('The service deprecated_service has been marked as deprecated. You should stop using it.', E_USER_DEPRECATED); + + return $this->services['deprecated_service'] = new \stdClass(); + } + /** * Gets the 'factory_service' service. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml index f1e6e98efa..a9743d2c26 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -87,6 +87,7 @@ + bar diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml index ddb5d3a96a..1fac542312 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -76,6 +76,9 @@ services: class: stdClass decorates: decorated decoration_inner_name: decorated.pif-pouf + deprecated_service: + class: stdClass + deprecated: true new_factory: class: FactoryClass public: false From 954247d55011514c9ba404a6c4ba51115ce4918e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Baptiste=20Clavi=C3=A9?= Date: Mon, 10 Aug 2015 12:33:01 +0200 Subject: [PATCH 4/6] [DI] Trigger a deprecated error on the container builder --- .../DependencyInjection/ContainerBuilder.php | 4 ++++ .../Tests/ContainerBuilderTest.php | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 5ac89cde57..dcd1be632a 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -931,6 +931,10 @@ class ContainerBuilder extends Container implements TaggedContainerInterface throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id)); } + if ($definition->isDeprecated()) { + @trigger_error(sprintf('The service %s relies on a deprecated definition. You should avoid using it.', $id), E_USER_DEPRECATED); + } + if ($tryProxy && $definition->isLazy()) { $container = $this; diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 7e5564c92b..8853dd4d08 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -63,6 +63,28 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase } } + public function testCreateDeprecatedService() + { + $definition = new Definition('stdClass'); + $definition->setDeprecated(true); + + $that = $this; + $wasTriggered = false; + + set_error_handler(function ($errno, $errstr) use ($that, &$wasTriggered) { + $that->assertSame(E_USER_DEPRECATED, $errno); + $that->assertSame('The service deprecated_foo relies on a deprecated definition. You should avoid using it.', $errstr); + $wasTriggered = true; + }); + + $builder = new ContainerBuilder(); + $builder->createService($definition, 'deprecated_foo'); + + restore_error_handler(); + + $this->assertTrue($wasTriggered); + } + /** * @covers Symfony\Component\DependencyInjection\ContainerBuilder::register */ From 0b3d0a0bd9c7ada0933f97f2bad16ac95546f9a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Baptiste=20Clavi=C3=A9?= Date: Mon, 10 Aug 2015 14:35:12 +0200 Subject: [PATCH 5/6] [DI] Allow to change the deprecation message in Definition --- .../DependencyInjection/ContainerBuilder.php | 2 +- .../DependencyInjection/Definition.php | 36 +++++++++++++++++-- .../DependencyInjection/Dumper/PhpDumper.php | 4 +-- .../DependencyInjection/Dumper/XmlDumper.php | 10 ++++-- .../DependencyInjection/Dumper/YamlDumper.php | 2 +- .../Loader/XmlFileLoader.php | 6 +++- .../Loader/YamlFileLoader.php | 4 +-- .../schema/dic/services/services-1.0.xsd | 2 +- .../Tests/ContainerBuilderTest.php | 2 +- .../Tests/DefinitionTest.php | 24 +++++++++++++ .../Tests/Fixtures/php/services9.php | 4 +-- .../Tests/Fixtures/php/services9_compiled.php | 4 +-- .../Tests/Fixtures/xml/services9.xml | 4 ++- .../Tests/Fixtures/yaml/services9.yml | 2 +- 14 files changed, 85 insertions(+), 21 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index dcd1be632a..52405413af 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -932,7 +932,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface } if ($definition->isDeprecated()) { - @trigger_error(sprintf('The service %s relies on a deprecated definition. You should avoid using it.', $id), E_USER_DEPRECATED); + @trigger_error($definition->getDeprecationMessage($id), E_USER_DEPRECATED); } if ($tryProxy && $definition->isLazy()) { diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 8dc0af6fa9..8a39d4fb93 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -30,6 +30,8 @@ class Definition private $factoryMethod; private $factoryService; private $shared = true; + private $deprecated = false; + private $deprecationTemplate = 'The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed.'; private $scope = ContainerInterface::SCOPE_CONTAINER; private $properties = array(); private $calls = array(); @@ -38,7 +40,6 @@ class Definition private $public = true; private $synthetic = false; private $abstract = false; - private $deprecated = false; private $synchronized = false; private $lazy = false; private $decoratedService; @@ -834,14 +835,29 @@ class Definition * Whether this definition is deprecated, that means it should not be called * anymore. * - * @param bool $status + * @param bool $status + * @param string $template Template message to use if the definition is deprecated * * @return Definition the current instance * + * @throws InvalidArgumentException When the message template is invalid. + * * @api */ - public function setDeprecated($status = true) + public function setDeprecated($status = true, $template = null) { + if (null !== $template) { + if (preg_match('#[\r\n]|\*/#', $template)) { + throw new InvalidArgumentException('Invalid characters found in deprecation template.'); + } + + if (false === strpos($template, '%service_id%')) { + throw new InvalidArgumentException('The deprecation template must contain the "%service_id%" placeholder.'); + } + + $this->deprecationTemplate = $template; + } + $this->deprecated = (bool) $status; return $this; @@ -860,6 +876,20 @@ class Definition return $this->deprecated; } + /** + * Message to use if this definition is deprecated. + * + * @param string $id Service id relying on this definition + * + * @return string + * + * @api + */ + public function getDeprecationMessage($id) + { + return str_replace('%service_id%', $id, $this->deprecationTemplate); + } + /** * Sets a configurator to call after the service is fully initialized. * diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index c9b8d0e358..1cdeb2d35b 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -597,7 +597,7 @@ class PhpDumper extends Dumper $return[] = ''; } - $return[] = '@deprecated'; + $return[] = sprintf('@deprecated %s', $definition->getDeprecationMessage($id)); } $return = str_replace("\n * \n", "\n *\n", implode("\n * ", $return)); @@ -661,7 +661,7 @@ EOF; $code .= sprintf(" throw new RuntimeException('You have requested a synthetic service (\"%s\"). The DIC does not know how to construct this service.');\n }\n", $id); } else { if ($definition->isDeprecated()) { - $code .= sprintf(" @trigger_error('The service %s has been marked as deprecated. You should stop using it.', E_USER_DEPRECATED);\n\n", $id); + $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", var_export($definition->getDeprecationMessage($id), true)); } $code .= diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index f9fee16d4b..d4fbd6a202 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -148,9 +148,6 @@ class XmlDumper extends Dumper if ($definition->isLazy()) { $service->setAttribute('lazy', 'true'); } - if ($definition->isDeprecated()) { - $service->setAttribute('deprecated', 'true'); - } if (null !== $decorated = $definition->getDecoratedService()) { list($decorated, $renamedId, $priority) = $decorated; $service->setAttribute('decorates', $decorated); @@ -204,6 +201,13 @@ class XmlDumper extends Dumper $service->appendChild($factory); } + if ($definition->isDeprecated()) { + $deprecated = $this->document->createElement('deprecated'); + $deprecated->appendChild($this->document->createTextNode($definition->getDeprecationMessage('%service_id%'))); + + $service->appendChild($deprecated); + } + if ($callable = $definition->getConfigurator()) { $configurator = $this->document->createElement('configurator'); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index 987bb6d26c..94929f84a1 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -105,7 +105,7 @@ class YamlDumper extends Dumper } if ($definition->isDeprecated()) { - $code .= " deprecated: true\n"; + $code .= sprintf(" deprecated: %s\n", $definition->getDeprecationMessage('%service_id%')); } if ($definition->getFactoryClass(false)) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index de57789da4..8a0e4ac24a 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -147,7 +147,7 @@ class XmlFileLoader extends FileLoader $definition = new Definition(); } - foreach (array('class', 'shared', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'lazy', 'abstract', 'deprecated') as $key) { + foreach (array('class', 'shared', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'lazy', 'abstract') as $key) { if ($value = $service->getAttribute($key)) { if (in_array($key, array('factory-class', 'factory-method', 'factory-service'))) { @trigger_error(sprintf('The "%s" attribute of service "%s" in file "%s" is deprecated since version 2.6 and will be removed in 3.0. Use the "factory" element instead.', $key, (string) $service->getAttribute('id'), $file), E_USER_DEPRECATED); @@ -181,6 +181,10 @@ class XmlFileLoader extends FileLoader $definition->setFile($files[0]->nodeValue); } + if ($deprecated = $this->getChildren($service, 'deprecated')) { + $definition->setDeprecated(true, $deprecated[0]->nodeValue); + } + $definition->setArguments($this->getArgumentsAsPhp($service, 'argument')); $definition->setProperties($this->getArgumentsAsPhp($service, 'property')); diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index f5c2166b53..19191a760c 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -196,8 +196,8 @@ class YamlFileLoader extends FileLoader $definition->setAbstract($service['abstract']); } - if (isset($service['deprecated'])) { - $definition->setDeprecated($service['deprecated']); + if (array_key_exists('deprecated', $service)) { + $definition->setDeprecated(true, $service['deprecated']); } if (isset($service['factory'])) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index c521a4936e..cbaa3606f7 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -81,6 +81,7 @@ + @@ -94,7 +95,6 @@ - diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 8853dd4d08..511b29c939 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -73,7 +73,7 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase set_error_handler(function ($errno, $errstr) use ($that, &$wasTriggered) { $that->assertSame(E_USER_DEPRECATED, $errno); - $that->assertSame('The service deprecated_foo relies on a deprecated definition. You should avoid using it.', $errstr); + $that->assertSame('The "deprecated_foo" service is deprecated. You should stop using it, as it will soon be removed.', $errstr); $wasTriggered = true; }); diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php index 131581096f..ea3045df1a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -236,6 +236,8 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase /** * @covers Symfony\Component\DependencyInjection\Definition::setDeprecated * @covers Symfony\Component\DependencyInjection\Definition::isDeprecated + * @covers Symfony\Component\DependencyInjection\Definition::hasCustomDeprecationTemplate + * @covers Symfony\Component\DependencyInjection\Definition::getDeprecationMessage */ public function testSetIsDeprecated() { @@ -243,6 +245,28 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase $this->assertFalse($def->isDeprecated(), '->isDeprecated() returns false by default'); $this->assertSame($def, $def->setDeprecated(true), '->setDeprecated() implements a fluent interface'); $this->assertTrue($def->isDeprecated(), '->isDeprecated() returns true if the instance should not be used anymore.'); + $this->assertSame('The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.', $def->getDeprecationMessage('deprecated_service'), '->getDeprecationMessage() should return a formatted message template'); + } + + /** + * @dataProvider invalidDeprecationMessageProvider + * @covers Symfony\Component\DependencyInjection\Definition::setDeprecated + * @expectedException Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testSetDeprecatedWithInvalidDeprecationTemplate($message) + { + $def = new Definition('stdClass'); + $def->setDeprecated(false, $message); + } + + public function invalidDeprecationMessageProvider() + { + return array( + "With \rs" => array("invalid \r message %service_id%"), + "With \ns" => array("invalid \n message %service_id%"), + 'With */s' => array('invalid */ message %service_id%'), + 'message not containing require %service_id% variable' => array('this is deprecated'), + ); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php index 1dcc429c3f..675f17ec2a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -152,11 +152,11 @@ class ProjectServiceContainer extends Container * * @return \stdClass A stdClass instance. * - * @deprecated + * @deprecated The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed. */ protected function getDeprecatedServiceService() { - @trigger_error('The service deprecated_service has been marked as deprecated. You should stop using it.', E_USER_DEPRECATED); + @trigger_error('The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED); return $this->services['deprecated_service'] = new \stdClass(); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index 447e5454b2..c616432bac 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -153,11 +153,11 @@ class ProjectServiceContainer extends Container * * @return \stdClass A stdClass instance. * - * @deprecated + * @deprecated The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed. */ protected function getDeprecatedServiceService() { - @trigger_error('The service deprecated_service has been marked as deprecated. You should stop using it.', E_USER_DEPRECATED); + @trigger_error('The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED); return $this->services['deprecated_service'] = new \stdClass(); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml index a9743d2c26..4ddb8655c5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -87,7 +87,9 @@ - + + The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed. + bar diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml index 1fac542312..0525b3068c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -78,7 +78,7 @@ services: decoration_inner_name: decorated.pif-pouf deprecated_service: class: stdClass - deprecated: true + deprecated: The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed. new_factory: class: FactoryClass public: false From 83f4e9cf4576fd5efb7ecfe8f7acc565fb19b98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Baptiste=20Clavi=C3=A9?= Date: Wed, 9 Sep 2015 11:35:13 +0200 Subject: [PATCH 6/6] [DI] Support deprecated definitions in decorators --- .../ResolveDefinitionTemplatesPass.php | 6 ++++ .../DefinitionDecorator.php | 10 +++++++ .../ResolveDefinitionTemplatesPassTest.php | 30 +++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php index 92bf97d9cf..ab5f702d26 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php @@ -127,6 +127,9 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface if ($parentDef->getFactoryService(false)) { $def->setFactoryService($parentDef->getFactoryService(false)); } + if ($parentDef->isDeprecated()) { + $def->setDeprecated(true, $parentDef->getDeprecationMessage('%service_id%')); + } $def->setFactory($parentDef->getFactory()); $def->setConfigurator($parentDef->getConfigurator()); $def->setFile($parentDef->getFile()); @@ -162,6 +165,9 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface if (isset($changes['lazy'])) { $def->setLazy($definition->isLazy()); } + if (isset($changes['deprecated'])) { + $def->setDeprecated($definition->isDeprecated(), $definition->getDeprecationMessage('%service_id%')); + } if (isset($changes['decorated_service'])) { $decoratedService = $definition->getDecoratedService(); if (null === $decoratedService) { diff --git a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php b/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php index 7764079d51..87004c29b4 100644 --- a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php +++ b/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php @@ -180,6 +180,16 @@ class DefinitionDecorator extends Definition return parent::setDecoratedService($id, $renamedId, $priority); } + /** + * {@inheritdoc} + */ + public function setDeprecated($boolean = true, $template = null) + { + $this->changes['deprecated'] = true; + + return parent::setDeprecated($boolean, $template); + } + /** * Gets an argument to pass to the service constructor/factory method. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php index 2180483e2b..675630933d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php @@ -244,6 +244,36 @@ class ResolveDefinitionTemplatesPassTest extends \PHPUnit_Framework_TestCase $this->assertEquals(array('foo', 'foo_inner', 0), $container->getDefinition('child1')->getDecoratedService()); } + public function testDecoratedServiceCopiesDeprecatedStatusFromParent() + { + $container = new ContainerBuilder(); + $container->register('deprecated_parent') + ->setDeprecated(true) + ; + + $container->setDefinition('decorated_deprecated_parent', new DefinitionDecorator('deprecated_parent')); + + $this->process($container); + + $this->assertTrue($container->getDefinition('decorated_deprecated_parent')->isDeprecated()); + } + + public function testDecoratedServiceCanOverwriteDeprecatedParentStatus() + { + $container = new ContainerBuilder(); + $container->register('deprecated_parent') + ->setDeprecated(true) + ; + + $container->setDefinition('decorated_deprecated_parent', new DefinitionDecorator('deprecated_parent')) + ->setDeprecated(false) + ; + + $this->process($container); + + $this->assertFalse($container->getDefinition('decorated_deprecated_parent')->isDeprecated()); + } + protected function process(ContainerBuilder $container) { $pass = new ResolveDefinitionTemplatesPass();