09 November 2012

Drupal 8 with 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 have little 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.
  • 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 (Dependency Injection).
  • 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' (Event Dispatcher), , '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).
  • 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.

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.

07 November 2012

How does caching work in Drupal?

In my experience, there seems to be a lot of misunderstanding how Drupal caching works. I'm not talking about advanced caching mechanisms like Varnish, APC or memcache here, I'm talking about the core Drupal caching: page cache and block cache which are available in Drupal 7 core. Drupal 6 is similar but works a but differently because page caching was simplified for Drupal 7 (e.g. aggresive mode is now a available as a settings.php var) and the entire system also has become pluggable.

Everybody knows this screen and the settings. But what do they exactly mean? What difference do they actually make? What do they do? Here is an attempt to explain the settings at '/admin/config/development/performance'.

Page Caching:
  • If enables, page caching will only be used for anonymous users.
  • If you enable page caching, the entire HTML of each page will be stored in the database. This significantly reduces the amount of queries needed.
  • This automatically means that blocks will also get cached, disregarded their own cache settings.
  • If Varnish is configured, this will also enable varnish caching
  • The page cache max-age will be used (see below) for each page.
  • You can check the "X-Drupal-Cache" HTTP header to check if this cache is HIT.
Block Caching:
  • If you enable block caching, without enabling page caching, the block cache settings will be used (for anonymous and authenticated).
  • The defaults for most blocks is "don't cache" because that is the default for blocks who don't have their cache settings specifically declared.
  • You can use block_cache_alter module to set this for each block.
  • When it's block created by Views, you can set the cache settings for a block in Views.
Minimum Cache Lifetime:
  • This is the amount of time before the page cache is cleared.
  • Page caches are cleared on each cron run
  • Be warned: incorrect settings of your cron and this setting might kill your website performance.
Expiration of Cached Page:
  • This only applies for external caching mechanisms, for example your browser cache or Varnish.
  • It sets the Cache-Control max-age value in the HTTP-HEADERS.
  • This setting only takes effect when page caching is turned on.