Commit 5b578a55 authored by catch's avatar catch

Issue #2004282 by dawehner, smiletrl, amateescu, tim.plunkett: Injected...

Issue #2004282 by dawehner, smiletrl, amateescu, tim.plunkett: Injected dependencies and serialization hell .
parent 085a24e0
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Container.
*/
namespace Drupal\Core\DependencyInjection;
use Symfony\Component\DependencyInjection\Container as SymfonyContainer;
/**
* Extends the symfony container to set the service ID on the created object.
*/
class Container extends SymfonyContainer {
/**
* {@inheritdoc}
*/
public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) {
$service = parent::get($id, $invalidBehavior);
// Some services are called but do not exist, so the parent returns nothing.
if (is_object($service)) {
$service->_serviceId = $id;
}
return $service;
}
}
......@@ -7,15 +7,15 @@
namespace Drupal\Core\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder as BaseContainerBuilder;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder as SymfonyContainerBuilder;
use Symfony\Component\DependencyInjection\Container as SymfonyContainer;
/**
* Drupal's dependency injection container builder.
*
* @todo Submit upstream patches to Symfony to not require these overrides.
*/
class ContainerBuilder extends BaseContainerBuilder {
class ContainerBuilder extends SymfonyContainerBuilder {
/**
* Overrides Symfony\Component\DependencyInjection\ContainerBuilder::addObjectResource().
......@@ -39,7 +39,7 @@ public function addObjectResource($object) {
* services in a frozen builder.
*/
public function set($id, $service, $scope = self::SCOPE_CONTAINER) {
Container::set($id, $service, $scope);
SymfonyContainer::set($id, $service, $scope);
}
}
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\DependencySerialization.
*/
namespace Drupal\Core\DependencyInjection;
/**
* Provides a dependency injection friendly methods for serialization.
*/
abstract class DependencySerialization {
/**
* An array of service IDs keyed by property name used for serialization.
*
* @var array
*/
protected $_serviceIds = array();
/**
* {@inheritdoc}
*/
public function __sleep() {
$this->_serviceIds = array();
$vars = get_object_vars($this);
foreach ($vars as $key => $value) {
if (is_object($value) && isset($value->_serviceId)) {
// If a class member was instantiated by the dependency injection
// container, only store its ID so it can be used to get a fresh object
// on unserialization.
$this->_serviceIds[$key] = $value->_serviceId;
unset($vars[$key]);
}
}
return array_keys($vars);
}
/**
* {@inheritdoc}
*/
public function __wakeup() {
$container = \Drupal::getContainer();
foreach ($this->_serviceIds as $key => $service_id) {
$this->$key = $container->get($service_id);
}
unset($this->_serviceIds);
}
}
......@@ -36,7 +36,7 @@
*/
class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
const CONTAINER_BASE_CLASS = 'Container';
const CONTAINER_BASE_CLASS = '\Drupal\Core\DependencyInjection\Container';
/**
* Holds the container instance.
......@@ -166,21 +166,6 @@ public function __construct($environment, ClassLoader $class_loader, $allow_dump
$this->allowDumping = $allow_dumping;
}
/**
* {@inheritdoc}
*/
public function serialize() {
return serialize(array($this->environment, $this->classLoader, $this->allowDumping));
}
/**
* {@inheritdoc}
*/
public function unserialize($data) {
list($environment, $class_loader, $allow_dumping) = unserialize($data);
$this->__construct($environment, $class_loader, $allow_dumping);
}
/**
* {@inheritdoc}
*/
......
......@@ -15,7 +15,7 @@
* This interface extends Symfony's KernelInterface and adds methods for
* responding to modules being enabled or disabled during its lifetime.
*/
interface DrupalKernelInterface extends HttpKernelInterface, \Serializable {
interface DrupalKernelInterface extends HttpKernelInterface {
/**
* Boots the current kernel.
......
......@@ -8,6 +8,7 @@
namespace Drupal\Core\Form;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\DependencyInjection\DependencySerialization;
use Drupal\Core\StringTranslation\TranslationInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
......@@ -15,7 +16,7 @@
/**
* Provides a base class for forms.
*/
abstract class FormBase implements FormInterface, ContainerInjectionInterface {
abstract class FormBase extends DependencySerialization implements FormInterface, ContainerInjectionInterface {
/**
* The translation manager service.
......
......@@ -60,7 +60,7 @@ function testCompileDIC() {
$container = $kernel->getContainer();
$refClass = new ReflectionClass($container);
$is_compiled_container =
$refClass->getParentClass()->getName() == 'Symfony\Component\DependencyInjection\Container' &&
$refClass->getParentClass()->getName() == 'Drupal\Core\DependencyInjection\Container' &&
!$refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder');
$this->assertTrue($is_compiled_container);
......@@ -74,7 +74,7 @@ function testCompileDIC() {
$container = $kernel->getContainer();
$refClass = new ReflectionClass($container);
$is_compiled_container =
$refClass->getParentClass()->getName() == 'Symfony\Component\DependencyInjection\Container' &&
$refClass->getParentClass()->getName() == 'Drupal\Core\DependencyInjection\Container' &&
!$refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder');
$this->assertTrue($is_compiled_container);
// Test that our synthetic services are there.
......@@ -114,15 +114,4 @@ function testCompileDIC() {
$this->assertEqual($modules['service_provider_test'], drupal_get_filename('module', 'service_provider_test'));
}
/**
* Tests kernel serialization/unserialization.
*/
public function testSerialization() {
$classloader = drupal_classloader();
$kernel = new DrupalKernel('testing', $classloader);
$string = serialize($kernel);
$unserialized_kernel = unserialize($string);
$this->assertTrue($unserialized_kernel instanceof DrupalKernel);
}
}
<?php
/**
* @file
* Contains \Drupal\Tests\Core\DependencyInjection\ContainerTest.
*/
namespace Drupal\Tests\Core\DependencyInjection;
use Drupal\Core\DependencyInjection\Container;
use Drupal\Tests\UnitTestCase;
/**
* Tests the dependency injection container overrides of Drupal.
*
* @see \Drupal\Core\DependencyInjection\Container
*/
class ContainerTest extends UnitTestCase {
/**
* The tested container.
*
* @var \Drupal\Core\DependencyInjection\Container
*/
public $container;
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Dependency injection container',
'description' => 'Tests the dependency injection container overrides of Drupal.',
'group' => 'System'
);
}
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->container = new Container();
}
/**
* Tests the get method.
*
* @see \Drupal\Core\DependencyInjection\Container::get()
*/
public function testGet() {
$service = new \stdClass();
$service->key = 'value';
$this->container->set('test_service', $service);
$result = $this->container->get('test_service');
$this->assertSame($service, $result);
$this->assertEquals('test_service', $result->_serviceId);
}
}
<?php
/**
* @file
* Contains \Drupal\Tests\Core\DependencyInjection\DependencySerializationTest.
*/
namespace Drupal\Tests\Core\DependencyInjection;
use Drupal\Core\DependencyInjection\DependencySerialization;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Tests the dependency serialization base class.
*
* @see \Drupal\Core\DependencyInjection\DependencySerialization
*/
class DependencySerializationTest extends UnitTestCase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Service dependency serialization',
'description' => 'Tests the dependency serialization base class.',
'group' => 'System'
);
}
/**
* Tests serialization and unserialization.
*/
public function testSerialization() {
// Create a pseudo service and dependency injected object.
$service = new \stdClass();
$service->_serviceId = 'test_service';
$container = $this->getMock('Drupal\Core\DependencyInjection\Container');
$container->expects($this->exactly(1))
->method('get')
->with('test_service')
->will($this->returnValue($service));
\Drupal::setContainer($container);
$dependencySerialization = new TestClass($service);
$string = serialize($dependencySerialization);
$object = unserialize($string);
// The original object got _serviceIds added so let's remove it to check
// equality
unset($dependencySerialization->_serviceIds);
// Ensure dependency injected object remains the same after serialization.
$this->assertEquals($dependencySerialization, $object);
// Ensure that _serviceIds does not exist on the object anymore.
$this->assertFalse(isset($object->_serviceIds));
// Ensure that both the service and the variable are in the unserialized
// object.
$this->assertSame($service, $object->service);
}
}
/**
* Defines a test class which has a single service as dependency.
*/
class TestClass extends DependencySerialization {
/**
* A test service.
*
* @var \stdClass
*/
public $service;
/**
* {@inheritdoc}
*
* Make the property accessible for the test.
*/
public $_serviceIds;
/**
* Constructs a new TestClass object.
*
* @param \stdClass $service
* A test service.
*/
public function __construct(\stdClass $service) {
$this->service = $service;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment