Drupal 8 and Symfony
We all know that Symfony is already in the core of Drupal 8 but how it works and how both systems work? Not that many people understand fully this integration. Me neither but I would like to pubilsh my research notes about how Drupal 8 works now. Surely this material is based on the snapshot of beginning September and I really hope that more things will happen so this information is relevant only for some time.
I don't have any real life experience of the building projects with Symfony so my knowledge is very close to majority of drupal developers.
So lets start.
First changes we see in index.php that bootstrap is done on the level of DRUPAL_BOOTSTRAP_CODE instead of DRUPAL_BOOTSTRAP_FULL like in Drupal 7. From documentation about phases of bootstrap of Drupal 8 we can see that phase that “initialize language, path, theme, and modules” is not done yet.
Next thing we see that we instantiate object of the DrupalKernel class (inherits from Symfony Kernel class).
Kernel class in Symfony is responsible for building Dependency Injection Container and registering bundles. We will come back to Dependency Injection Container later. Bundles are like “modules” of drupal world.
Next thing that happens that we instantiate Request object (instance of Symfony HttpFoundation component). This is the object that grabs all global variables form the request and has methods to retrieve them. The idea behind it is that we do not use any global variables in the code anywhere but interact only with this object as a source.
Next part is simple ask kernel object to handle our request
$response = $kernel->handle($request)->prepare($request)->send(); $kernel->terminate($request, $response);
Now lets take a look of internals of the kernel object and what it does to handle our request.
Kernel handle method calls $this->boot() method and then $this->getHttpKernel()->handle($request, $type, $catch);
Booting of the Kernel consists of following steps:
- Registering Bundles. Bundles are like modules in drupal world. DrupalKernel overrides method registerBundles() to register CoreBundle and allows other modules to provide Symfony-like bundles to the system.
- Pass Container to all registered bundles (it is saved in bundle's container property) and call boot() method on bundles. boot() method doesn't do anything at the moment.
- Initialize Dependency Injection Container. Container is the object that handles all information about dependencies between objects. So when you want to initiate object of class A but in order to do that system should pass object B to constructor of the object A, Container knows about this dependency and does it for you. In Symfony code this is also called Service Container. Documentation I have found about it is here http://symfony.com/doc/current/components/dependency_injection/introduct.... Also Container is statically cached with drupal_container() function so we can access it in any place of the code. Minimal available Container will consist of information about config system of drupal (config.storage.options). Every bundle also registers new components to Container (see CoreBundle:: build() method for that. It registers services: 'request', 'dispatcher' (http://symfony.com/doc/current/components/event_dispatcher/introduction....), 'resolver', 'http_kernel', 'language_manager'. We can also see that plenty subscribers registered. In terms of this is similar to our hooks system. Here event is fired and dispatcher knows what subscribers are 'registered' for which events and executes their correspond methods (see Drupal\Core\EventSubscriber\PathSubscriber for example).
After booting the kernel we run its handler method. It comes down firing KernelEvents::REQUEST event where our routing system plays its role. I believe new routing system deserves separate article. After event we have controller we fire event KernelEvents::CONTROLLER that resolves menu callback (in terms of our old menu system). After executing menu callback we fire KernelEvents::VIEW event and our subscribers prepare $response object that is finally returned.
Having response object avaialble we run prepare( ) method that prepares headers, and run send( ) method that prints rendered output.
Finally we run kernel's terminate method that in the end fire KernelEvents::TERMINATE event.
This is very brief overview from the beginner point of view to the system. I hope it made some feeling of understanding of how things work now or at least triggered your interest to learn more about it. Also please remember that things are changing as this are parts that are in active development right now.