From 64dcd0a76ac0105d854a925c8b45b7a0efd8e033 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 12 Sep 2013 12:15:48 +0200 Subject: [PATCH] [DependencyInjection] added a simple way to replace a service by keeping a reference to the old one --- .../DependencyInjection/CHANGELOG.md | 5 ++ .../Compiler/DecoratorServicePass.php | 54 +++++++++++++++++++ .../Compiler/PassConfig.php | 1 + .../DependencyInjection/Definition.php | 26 +++++++++ 4 files changed, 86 insertions(+) create mode 100644 src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index d3ca34c2bcb97..b47db13313543 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +2.5.0 +----- + +* added DecoratorServicePass and a way to override a service definition (Definition::setDecoratedService()) + 2.4.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php new file mode 100644 index 0000000000000..c6db811485603 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Alias; + +/** + * Overwrites a service but keeps the overridden one. + * + * @author Christophe Coevoet + * @author Fabien Potencier + */ +class DecoratorServicePass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + foreach ($container->getDefinitions() as $id => $definition) { + if (!$decorated = $definition->getDecoratedService()) { + continue; + } + + list ($decorated, $renamedId) = $decorated; + if (!$renamedId) { + $renamedId = $id.'.inner'; + } + + // we create a new alias/service for the service we are replacing + // to be able to reference it in the new one + if ($container->hasAlias($decorated)) { + $alias = $container->getAlias($decorated); + $public = $alias->isPublic(); + $container->setAlias($renamedId, new Alias((string) $alias, false)); + } else { + $definition = $container->getDefinition($decorated); + $public = $definition->isPublic(); + $definition->setPublic(false); + $container->setDefinition($renamedId, $definition); + } + + $container->setAlias($decorated, new Alias($id, $public)); + } + } +} diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index e863f75640fb1..0ae77693ba2b5 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -50,6 +50,7 @@ public function __construct() $this->optimizationPasses = array( new ResolveDefinitionTemplatesPass(), + new DecoratorServicePass(), new ResolveParameterPlaceHoldersPass(), new CheckDefinitionValidityPass(), new ResolveReferencesToAliasesPass(), diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 1168444389ef1..e31b7ff3ad187 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -38,6 +38,7 @@ class Definition private $abstract; private $synchronized; private $lazy; + private $decoratedService; protected $arguments; @@ -64,6 +65,31 @@ public function __construct($class = null, array $arguments = array()) $this->properties = array(); } + /** + * Sets the service that this service is decorating. + * + * @param string $id The decorated service id + * @param string $renamedId The new decorated service id + */ + public function setDecoratedService($id, $renamedId = null) + { + if ($renamedId && $id == $renamedId) { + throw new \LogicException(sprintf('The decorated service parent name for "%s" must be different than the service name itself.', $id)); + } + + $this->decoratedService = array($id, $renamedId); + } + + /** + * Gets the service that decorates this service. + * + * @return array An array composed of the decorated service id and the new id for it + */ + public function getDecoratedService() + { + return $this->decoratedService; + } + /** * Sets the name of the class that acts as a factory using the factory method, * which will be invoked statically.