diff --git a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php index 0e86fda109632aab6b308b9e4c23d123507bce90..736680d5a693725f521e4dcd80cdbc6bb548411a 100644 --- a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php +++ b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php @@ -9,6 +9,7 @@ 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 @@ public function addObjectResource($object) { */ 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 0000000000000000000000000000000000000000..3ada9324bb05e490626cdd254efc222609a9103c --- /dev/null +++ b/core/tests/Drupal/Tests/Core/DependencyInjection/ContainerBuilderTest.php @@ -0,0 +1,51 @@ +<?php + +/** + * @file + * Contains \Drupal\Tests\Core\DependencyInjection\ContainerBuilderTest. + */ + +namespace Drupal\Tests\Core\DependencyInjection; + +use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Tests\UnitTestCase; +use Symfony\Component\DependencyInjection\Reference; + +require_once __DIR__ . '../../../../../../vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php'; + +/** + * Dependency injection container builder. + * + * @see \Drupal\Core\DependencyInjection\ContainerBuilder + */ +class ContainerBuilderTest extends UnitTestCase { + + /** + * {@inheritdoc} + */ + public static function getInfo() { + return array( + 'name' => '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()); + } + +}