diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php index 68502c67a94e43bdb7d43086c5362df56378623f..89220498a536c455efce0a3b38f1824be4f68dae 100644 --- a/core/lib/Drupal/Core/Config/Config.php +++ b/core/lib/Drupal/Core/Config/Config.php @@ -11,6 +11,7 @@ use Drupal\Component\Utility\String; use Drupal\Core\Config\ConfigNameException; use Drupal\Core\Config\Schema\SchemaIncompleteException; +use Drupal\Core\DependencyInjection\DependencySerialization; use Drupal\Core\TypedData\PrimitiveInterface; use Drupal\Core\TypedData\Type\FloatInterface; use Drupal\Core\TypedData\Type\IntegerInterface; @@ -20,7 +21,7 @@ /** * Defines the default configuration object. */ -class Config { +class Config extends DependencySerialization { /** * The maximum length of a configuration object name. diff --git a/core/lib/Drupal/Core/Controller/FormController.php b/core/lib/Drupal/Core/Controller/FormController.php index 271de99f39ebfcd8b583b6d546c46fc6280ac85a..5296a4b23877a3c54c5372782d23abe1384bd7b5 100644 --- a/core/lib/Drupal/Core/Controller/FormController.php +++ b/core/lib/Drupal/Core/Controller/FormController.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Controller; +use Drupal\Core\DependencyInjection\DependencySerialization; use Drupal\Core\Form\FormBuilderInterface; use Symfony\Component\HttpFoundation\Request; @@ -15,7 +16,7 @@ * * @todo Make this a trait in PHP 5.4. */ -abstract class FormController { +abstract class FormController extends DependencySerialization { /** * The form definition. The format may vary depending on the child class. diff --git a/core/lib/Drupal/Core/DependencyInjection/Container.php b/core/lib/Drupal/Core/DependencyInjection/Container.php index 1869bfcc459390d7bc927f0deb1d48fb044f0ff0..0d4c972e965876f44d1375aac03d6c4428fbb3f7 100644 --- a/core/lib/Drupal/Core/DependencyInjection/Container.php +++ b/core/lib/Drupal/Core/DependencyInjection/Container.php @@ -27,4 +27,12 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE return $service; } + /** + * {@inheritdoc} + */ + public function __sleep() { + trigger_error('The container was serialized.', E_USER_ERROR); + return array_keys(get_object_vars($this)); + } + } diff --git a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php index 736680d5a693725f521e4dcd80cdbc6bb548411a..56f1627ed59cb3d93998634b8ab28c1e284ebbbc 100644 --- a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php +++ b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php @@ -28,6 +28,19 @@ class ContainerBuilder extends SymfonyContainerBuilder { public function addObjectResource($object) { } + /** + * {@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; + } + /** * Overrides Symfony\Component\DependencyInjection\ContainerBuilder::set(). * @@ -89,4 +102,12 @@ protected function callMethod($service, $call) { call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->resolveValue($call[1]))); } + /** + * {@inheritdoc} + */ + public function __sleep() { + trigger_error('The container was serialized.', E_USER_ERROR); + return array_keys(get_object_vars($this)); + } + } diff --git a/core/lib/Drupal/Core/DependencyInjection/DependencySerialization.php b/core/lib/Drupal/Core/DependencyInjection/DependencySerialization.php index feb50f15c92c72a4698756e48c6f42dfbc6fc4fb..256ce745d4a16c5d36ac7c7386324051a89b4fbf 100644 --- a/core/lib/Drupal/Core/DependencyInjection/DependencySerialization.php +++ b/core/lib/Drupal/Core/DependencyInjection/DependencySerialization.php @@ -6,6 +6,7 @@ */ namespace Drupal\Core\DependencyInjection; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a dependency injection friendly methods for serialization. @@ -33,6 +34,11 @@ public function __sleep() { $this->_serviceIds[$key] = $value->_serviceId; unset($vars[$key]); } + // Special case the container, which might not have a service ID. + elseif ($value instanceof ContainerInterface) { + $this->_serviceIds[$key] = 'service_container'; + unset($vars[$key]); + } } return array_keys($vars); diff --git a/core/lib/Drupal/Core/Entity/EntityControllerBase.php b/core/lib/Drupal/Core/Entity/EntityControllerBase.php index f468303ebf6134ff888c305eed81e02e8f83615a..7b60766308cddfb72053d4739b2618417ff04f38 100644 --- a/core/lib/Drupal/Core/Entity/EntityControllerBase.php +++ b/core/lib/Drupal/Core/Entity/EntityControllerBase.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Entity; +use Drupal\Core\DependencyInjection\DependencySerialization; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\StringTranslation\TranslationInterface; @@ -15,7 +16,7 @@ * * @todo Convert this to a trait. */ -abstract class EntityControllerBase { +abstract class EntityControllerBase extends DependencySerialization { /** * The translation manager service. diff --git a/core/lib/Drupal/Core/Language/LanguageManager.php b/core/lib/Drupal/Core/Language/LanguageManager.php index 2bd3f202a757ce5c655d5a1bee19602d91c35e6f..6ddf67310d0970f97ed61376aff7f2b19a2aaf08 100644 --- a/core/lib/Drupal/Core/Language/LanguageManager.php +++ b/core/lib/Drupal/Core/Language/LanguageManager.php @@ -8,12 +8,13 @@ namespace Drupal\Core\Language; use Drupal\Component\Utility\String; +use Drupal\Core\DependencyInjection\DependencySerialization; use Drupal\Core\StringTranslation\TranslationInterface; /** * Class responsible for providing language support on language-unaware sites. */ -class LanguageManager implements LanguageManagerInterface { +class LanguageManager extends DependencySerialization implements LanguageManagerInterface { /** * The string translation service. diff --git a/core/lib/Drupal/Core/Plugin/PluginBase.php b/core/lib/Drupal/Core/Plugin/PluginBase.php index 03f952a6426da9b92b27491984a681f82892bbba..f0bce24488a32938baaa91bd359bb7e0cf166a54 100644 --- a/core/lib/Drupal/Core/Plugin/PluginBase.php +++ b/core/lib/Drupal/Core/Plugin/PluginBase.php @@ -9,12 +9,23 @@ use Drupal\Component\Plugin\PluginBase as ComponentPluginBase; use Drupal\Core\StringTranslation\TranslationInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Base class for plugins supporting metadata inspection and translation. */ abstract class PluginBase extends ComponentPluginBase { + /** + * An array of service IDs keyed by property name used for serialization. + * + * @todo Remove when Drupal\Core\DependencyInjection\DependencySerialization + * is converted to a trait. + * + * @var array + */ + protected $_serviceIds = array(); + /** * The translation manager service. * @@ -58,4 +69,45 @@ public function setTranslationManager(TranslationInterface $translation_manager) return $this; } + /** + * {@inheritdoc} + * + * @todo Remove when Drupal\Core\DependencyInjection\DependencySerialization + * is converted to a trait. + */ + 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]); + } + // Special case the container, which might not have a service ID. + elseif ($value instanceof ContainerInterface) { + $this->_serviceIds[$key] = 'service_container'; + unset($vars[$key]); + } + } + + return array_keys($vars); + } + + /** + * {@inheritdoc} + * + * @todo Remove when Drupal\Core\DependencyInjection\DependencySerialization + * is converted to a trait. + */ + public function __wakeup() { + $container = \Drupal::getContainer(); + foreach ($this->_serviceIds as $key => $service_id) { + $this->$key = $container->get($service_id); + } + unset($this->_serviceIds); + } + } diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php index 9d9c857ed325108c2f5055860dd0e66c6a672f76..2fd33bfe8aab08b9fdee56137ec02c8f3da0d8f9 100644 --- a/core/modules/views/lib/Drupal/views/ViewExecutable.php +++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php @@ -7,6 +7,7 @@ namespace Drupal\views; +use Drupal\Core\DependencyInjection\DependencySerialization; use Drupal\Core\Session\AccountInterface; use Drupal\views\Plugin\views\query\QueryPluginBase; use Drupal\views\ViewStorageInterface; @@ -25,7 +26,7 @@ * An object to contain all of the data to generate a view, plus the member * functions to build the view query, execute the query and render the output. */ -class ViewExecutable { +class ViewExecutable extends DependencySerialization { /** * The config entity in which the view is stored. diff --git a/core/tests/Drupal/Tests/Core/DependencyInjection/ContainerBuilderTest.php b/core/tests/Drupal/Tests/Core/DependencyInjection/ContainerBuilderTest.php index 3ada9324bb05e490626cdd254efc222609a9103c..fcc898f34759888b3fbf334e2b02f0b52df7e875 100644 --- a/core/tests/Drupal/Tests/Core/DependencyInjection/ContainerBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/DependencyInjection/ContainerBuilderTest.php @@ -14,7 +14,7 @@ require_once __DIR__ . '../../../../../../vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php'; /** - * Dependency injection container builder. + * Tests the dependency injection container builder overrides of Drupal. * * @see \Drupal\Core\DependencyInjection\ContainerBuilder */ @@ -48,4 +48,18 @@ public function testSetOnSynchronizedService() { $this->assertSame($baz, $container->get('bar')->getBaz()); } + /** + * Tests the get method. + * + * @see \Drupal\Core\DependencyInjection\Container::get() + */ + public function testGet() { + $container = new ContainerBuilder(); + $container->register('bar', 'BarClass'); + + $result = $container->get('bar'); + $this->assertTrue($result instanceof \BarClass); + $this->assertEquals('bar', $result->_serviceId); + } + } diff --git a/core/tests/Drupal/Tests/Core/DependencyInjection/DependencySerializationTest.php b/core/tests/Drupal/Tests/Core/DependencyInjection/DependencySerializationTest.php index d6af542d4299bc0d0f29d5c1058bc8dcd3a89323..1cf99cc081ceb2117fb4c080c7544c7b58d6ac59 100644 --- a/core/tests/Drupal/Tests/Core/DependencyInjection/DependencySerializationTest.php +++ b/core/tests/Drupal/Tests/Core/DependencyInjection/DependencySerializationTest.php @@ -7,9 +7,11 @@ namespace Drupal\Tests\Core\DependencyInjection; +use Drupal\Core\DependencyInjection\Container; use Drupal\Core\DependencyInjection\DependencySerialization; use Drupal\Tests\UnitTestCase; -use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Tests the dependency serialization base class. @@ -36,20 +38,19 @@ 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)); + $container = new Container(); + $container->set('test_service', $service); + $container->set('service_container', $container); \Drupal::setContainer($container); $dependencySerialization = new TestClass($service); + $dependencySerialization->setContainer($container); $string = serialize($dependencySerialization); $object = unserialize($string); - // The original object got _serviceIds added so let's remove it to check - // equality + // The original object got _serviceIds added so removing it to check + // equality. unset($dependencySerialization->_serviceIds); // Ensure dependency injected object remains the same after serialization. @@ -61,6 +62,7 @@ public function testSerialization() { // Ensure that both the service and the variable are in the unserialized // object. $this->assertSame($service, $object->service); + $this->assertSame($container, $object->container); } } @@ -68,7 +70,7 @@ public function testSerialization() { /** * Defines a test class which has a single service as dependency. */ -class TestClass extends DependencySerialization { +class TestClass extends DependencySerialization implements ContainerAwareInterface { /** * A test service. @@ -77,6 +79,13 @@ class TestClass extends DependencySerialization { */ public $service; + /** + * The container. + * + * @var \Symfony\Component\DependencyInjection\ContainerInterface + */ + public $container; + /** * {@inheritdoc} * @@ -94,4 +103,10 @@ public function __construct(\stdClass $service) { $this->service = $service; } + /** + * {@inheritdoc} + */ + public function setContainer(ContainerInterface $container = NULL) { + $this->container = $container; + } }