4

I have noticed that some classes contain the following line:

use DependencySerializationTrait;

I know this trait is used for serialization, but I don't get when to use it and what kind of classes need to support serialization.

4

@kiamlaluno described the technical details. This is the background why the DependencySerializationTrait was introduced:

This was the situation before:

  • Any class that receives injected dependencies (whether by constructor injection or setter injection) typically keeps a reference to its dependencies in class members. That's how dependency injection works.
  • Drupal being Drupal, it serializes stuff in several places:
    • Anything that is in a $form / $form_state : the FormInterface object, but also any object whose methods are used by FAPI #callbacks ('#pre_render' = array($some_object, 'someSubmit')).
    • Anything that gets cached, stored in state(), or more generally in the k/v stores...
  • When an object is serialized, all the objects it references get serialized as well, and recursively, all the dependencies of those objects...
  • This leads to a serialization chain of hell: [#1909418] hit a case where serializing the form controller meant serializing the EntityManager -> the Container -> (among many other things) the DrupalKernel. This got fixed by making sure the kernel was serializable - which is just insane :-).

And this was the proposed solution:

The answer for "DIC-friendly serialization" seems to be:

  • On serialize, do not serialize dependencies.
  • On unserialize, re-pull them from the container. Granted, this breaks mocking, but serialization/unserialization is not something you typically do in unit tests, so I don't see this as a real problem.

How to do this might not be too simple though. Two approaches come to mind:

  1. Classes that want to be "DIC-friendly serialized" need to implement an unserialize() method that hardcodes the services ids of its dependencies. That means doing something similar to the create() factory method that is currently used at instantiation time in a couple places in core (controllers, some plugins). Hardcoding service ids in more and more classes is not a joyful perspective, though.
  2. Crazy idea:
    • Modify the DIC so that each time a service gets instantiated, the service id by which it got instantiated gets placed in a public __serviceId (or something) property on the object.
    • Classes that want to be "DIC-friendly serialized" do:
    • On serialize() : foreach member, if (!empty($this->member->__serviceId)) {just serialize the __serviceId string instead of the whole object});
    • On unserialize() : $this->member = \Drupal::service($service_id)

Quoted from Injected dependencies and serialization hell

3

It is used from classes that are serialized and that have services in their properties. For those classes, the trait replaces the service with its service ID, so that when an object of those classes is restored after being serialized, those service properties get a fresh object, as said in the code comment.

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.

For example, a class that just saves arrays in its properties would not have much use of that trait, especially if it's not going to be serialized.

If then the class stores the dependency injection container, the trait replaces it with the string service_container.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.