Issue #2147451 by dawehner: ContainerBuild has to ensure synchronized services.

8.0.x
Dries 2014-01-10 13:56:09 -05:00
parent f3b78fbfe6
commit d329ec0f15
2 changed files with 98 additions and 0 deletions

View File

@ -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])));
}
}

View File

@ -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());
}
}