From d329ec0f1594ff6f7494955f9b48467623c99a29 Mon Sep 17 00:00:00 2001 From: Dries Date: Fri, 10 Jan 2014 13:56:09 -0500 Subject: [PATCH] Issue #2147451 by dawehner: ContainerBuild has to ensure synchronized services. --- .../DependencyInjection/ContainerBuilder.php | 47 +++++++++++++++++ .../ContainerBuilderTest.php | 51 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 core/tests/Drupal/Tests/Core/DependencyInjection/ContainerBuilderTest.php diff --git a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php index 0e86fda1096..736680d5a69 100644 --- a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php +++ b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php @@ -9,6 +9,7 @@ namespace Drupal\Core\DependencyInjection; use Symfony\Component\DependencyInjection\ContainerBuilder as SymfonyContainerBuilder; use Symfony\Component\DependencyInjection\Container as SymfonyContainer; +use Symfony\Component\DependencyInjection\Reference; /** * Drupal's dependency injection container builder. @@ -40,6 +41,52 @@ class ContainerBuilder extends SymfonyContainerBuilder { */ public function set($id, $service, $scope = self::SCOPE_CONTAINER) { SymfonyContainer::set($id, $service, $scope); + + if ($this->hasDefinition($id) && ($definition = $this->getDefinition($id)) && $definition->isSynchronized()) { + $this->synchronize($id); + } + } + + /** + * Synchronizes a service change. + * + * This method is a copy of the ContainerBuilder of symfony. + * + * This method updates all services that depend on the given + * service by calling all methods referencing it. + * + * @param string $id A service id + */ + private function synchronize($id) { + foreach ($this->getDefinitions() as $definitionId => $definition) { + // only check initialized services + if (!$this->initialized($definitionId)) { + continue; + } + + foreach ($definition->getMethodCalls() as $call) { + foreach ($call[1] as $argument) { + if ($argument instanceof Reference && $id == (string) $argument) { + $this->callMethod($this->get($definitionId), $call); + } + } + } + } + } + + /** + * A 1to1 copy of parent::callMethod. + */ + protected function callMethod($service, $call) { + $services = self::getServiceConditionals($call[1]); + + foreach ($services as $s) { + if (!$this->has($s)) { + return; + } + } + + call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->resolveValue($call[1]))); } } diff --git a/core/tests/Drupal/Tests/Core/DependencyInjection/ContainerBuilderTest.php b/core/tests/Drupal/Tests/Core/DependencyInjection/ContainerBuilderTest.php new file mode 100644 index 00000000000..3ada9324bb0 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/DependencyInjection/ContainerBuilderTest.php @@ -0,0 +1,51 @@ + 'Dependency injection container builder', + 'description' => 'Tests the dependency injection container builder overrides of Drupal.', + 'group' => 'System' + ); + } + + /** + * Tests set with a synchronized service. + */ + public function testSetOnSynchronizedService() { + $container = new ContainerBuilder(); + $container->register('baz', 'BazClass') + ->setSynchronized(TRUE); + $container->register('bar', 'BarClass') + ->addMethodCall('setBaz', array(new Reference('baz'))); + + $container->set('baz', $baz = new \BazClass()); + $this->assertSame($baz, $container->get('bar')->getBaz()); + + $container->set('baz', $baz = new \BazClass()); + $this->assertSame($baz, $container->get('bar')->getBaz()); + } + +}