diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 9a41e8570551..25e7fc66aaaf 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 000000000000..c6db81148560 --- /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 ac395db22e9a..bcef41052b9b 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -46,6 +46,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 428fee27c3a2..ce3646a9fb0b 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -38,6 +38,7 @@ class Definition private $abstract = false; private $synchronized = false; private $lazy = false; + private $decoratedService; protected $arguments; @@ -100,6 +101,31 @@ public function setFactoryMethod($factoryMethod) return $this; } + /** + * 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; + } + /** * Gets the factory method. *