fromMarch 2013
Feature:

Componentized Drupal

Drupal 8 and Symfony2
0

One of the most widely reported changes to Drupal 8 has been the adoption of several Symfony2 components into Drupal core. So what exactly is Symfony2? Why has Drupal started using it and other third party libraries? And what changes should people expect as they start to build sites and modules based on Drupal 8?

How Did We Get to Symfony2?

Having existed as a project for 11 years — and with a strong CMS basis for much of that time —
Drupal has been primarily focused on serving fully rendered HTML pages to web browsers. Over the past few years, the explosion of web services and mobile usage has led to a need to serve many different types of requests in many different ways, whether partial page requests for parallel processing (client, server, or edge side includes), a JSON response to a web service call, or responding to drush on the command line. Several contributed Drupal modules now exist to support using Drupal like that (http://drupal.org/project/esi or http://drupal.org/project/services), but in all cases they contain a large number of workarounds in order to circumvent the full HTML page rendering that Drupal 7 and previous releases assume.

The Web Services and Context in Core Initiative (WSCCI) began during the Drupal 7 release cycle and became one of several official Drupal 8 initiatives during 2011; its goal was to update Drupal’s core request handling and page serving in order to better meet the various kinds of requests that a Drupal site must now serve.

The WSCCI’s goal was described by initiative-lead Larry Garfield thus:

Drupal needs to evolve, and quickly, from a first-class web CMS into a first-class REST server that includes a first-class web CMS.

Not a Small Task Then!

To support meaningful RESTful request handling in core, Drupal needed to be able to parse HTTP requests in a standardized way. However, most of our HTTP request handling previously had been one-off support for specific headers and raw superglobals, which were needed to serve HTML and a handful of other response types.

To avoid the task of writing a new library from scratch in order to process all this additional necessary information, WSCCI set about the task of reviewing existing HTTP libraries to see whether any would be suitable for inclusion. The Symfony HTTP Foundation component was chosen from several different contenders (the other finalist was Zend Framework), and added to core mid-2011.

Additional Symfony2 components have been pulled in over time, but it’s important to note it doesn’t mean we finally decided to “build Drupal as a CMS on top of an existing web framework,” something which has been suggested numerous times over the years. That has never really been a practical proposition, and wouldn’t have been even with a traditional full stack framework. Drupal has its own unique framework functionality that has to be able to co-exist with third party code in order to support essential Drupal site building features like entities, fields, views, rules, and the other configuration-driven tools which have been critical to its popularity.

Rather than just pulling in a full entire framework with its own assumptions, and trying to add Drupal-ish CMS features on top of that, Drupal is following a newer trend in the PHP community: the rewrite of Symfony2 was one of the earlier signs.

Symfony2 is really two projects: Symfony Components — which are decoupled libraries that can be used individually — and Symfony Standard Edition — which combines those libraries with some third party dependencies into a functioning web framework.

With the adoption of PHP 5.3 and namespaces, it has become easier to write code that is interoperable between projects: class and namespace clashes are no longer a problem, and Drupal had already started using interfaces for most classes, which allow different implementations to be used without changing the external API.

The ”https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md”>PSR-0 standard specifies a class naming and file organization scheme, primarily to allow code from multiple projects to be used with a single class autoloader. On top of PSR-0, there is also rapid standardization on ”Composer for managing and downloading project dependencies. Drupal has adopted the PSR-0 standard for it’s own object-oriented code, and also started using Composer to manage external dependencies.

Given this context, Symfony adoption is part of a much broader trend in changes to Drupal’s architecture and development process, which follows how modern PHP applications tend to be built over the past three to four years:

  • Existing core functionality is being refactored to form decoupled OOP components, following the PSR-0 standard.Third-party code is being pulled in (via Composer and where it makes sense) to either replace existing core functionality or to enable new features.These components, regardless of where they originally came from, are wired together via the Dependency Injection Container (DIC) to reduce interdependencies between them. The DIC, once it's been configured, allows classes to accept dependencies in their constructor, but handles much of the work of instantiating those dependencies.
  • Code that used to be a mixture of procedural and OOP libraries in core’s /includes folder is instead being slowly migrated to a core/lib folder as PSR-0 libraries. There are now about fifty components provided by Drupal that follow the PSR-0 standard; some are tightly coupled to the rest of Drupal, some are generic libraries that could be used independently.

As well as HTTP Foundation, Drupal 8 also includes the Class Loader, Event Dispatcher, HTTP Kernel, Process, Routing, YAML, and Serializer components from Symfony 2.

Apart from Symfony, we’ve also pulled in the Guzzle HTTP client, Twig templating engine, Assetic asset manager and Doctrine Common libraries, again to augment or replace existing features.

External PHP Libraries and How They'll be Used in Drupal 8

  • Symfony Class Loader: class autoloading for PSR-0 classes.
  • Symfony Event Dispatcher: events and listeners (similar to hooks).
  • Symfony HTTP Kernel: request/response, bundles, http caching.
  • Symfony Process: dependency for Assetic.
  • Symfony Routing: replacement for Drupal's menu router.
  • Symfony YAML: YAML parsing used by configuration system.
  • Guzzle: outgoing HTTP requests.
  • ”Twig: replacement for PHP Template.
  • Assetic: CSS and JavaScript pre-processing and aggregation.
  • Doctrine Common: class annotation parsing for Drupal's plugin system.

What Does This Mean When We Start to Build Sites and Modules with Drupal 8?

At the time of this writing (December, 2012), there are still several months before Drupal 8 code freeze; many API changes are still in the process of being developed or applied throughout core.

In terms of deep architectural changes, the following particularly stand out:

Drupal’s bootstrap process is moving from a multi-step process — where more and more services are loaded — to relying on Symfony’s Dependency Injection Container (DIC), which holds a registry of all available services on the site, then initializes them and their dependencies when asked for.

Eventually, this should mean that when serving a JSON response, it’s not necessary to load any of the plumbing specific to serving an HTML, file, or XML response, and vice versa.

The use of the DIC also means that many things which were available via PHP globals or procedural functions, should now be accessed via the DIC, whether it’s global $language or the cache system. For object oriented code, this means the dependencies can be passed into the constructor and managed in the container. For procedural code there’s a drupal_container() wrapper which allows access to all available services from a central location.

Other important changes for module developers include: a new configuration management system; revamped and feature-complete Entity API with CRUD; query, render, and form support; Twig templating to replace PHPTemplate; and changes to the menu router to replace it with Symfony's routing component. Some of these changes take advantage of Symfony components, but many don’t expose those components directly to module developers — e.g., the configuration system uses Symfony’s YAML parser, but it could transparently switch to a different parser without effecting the API.

Do I Have to Learn Everything All Over Again?

If you’re new to Drupal or have just started using Drupal 7, then this might seem like a very large number of changes in a single release, and far too much to absorb. While there are a lot of changes, change is traditional in Drupal core, and each major release sees significant API changes. Drupal 8 will have further-reaching changes than Drupal 7, but Drupal 7 had further-reaching changes than Drupal 6, and so on.

While third party components in many cases are allowing changes to be made quicker, due to re-using code rather than writing (or rewriting) it from scratch, it’s not really driving the API changes — in many cases there has been existing momentum to refactor something or add a new feature, and the library has been adopted as a consequence of that rather than a precursor to it.

As well as all the change, there are also many parts of Drupal 8 which haven’t changed that much, or which have been updated rather than replaced. New features for site builders have been added to Drupal 8, and some existing features have changed, but the fundamental concepts for a site builder to learn remain the same: entities, fields, relationships between entities, views, and blocks are all conceptually intact and just as important, even if their underlying architecture and APIs have been updated.

There are also core APIs which rather closely match Drupal 8’s architecture and were added during Drupal 7; they haven’t changed significantly — for example the database abstraction layer and the queue API. There is some hope that this fundamental re-architecture of the system around PSR-0 classes and dependency injection will mean that certain APIs stay more stable into the future. By reducing interdependencies and relying on interfaces to expose APIs, changing one part of the system should be easier to do without rippling out across every other part. There’s still a lot of work to get to the point where API changes are less frequently needed during refactoring, and this is likely to continue as work begins on Drupal 9.

Adopting third-party libraries means not having to write code ourselves, but also brings many challenges, especially around bug fixing, security, and release cycles. Drupal contributors are already contributing bug fixes and enhancements to upstream libraries that have been adopted by Drupal 8, and it's likely this will increase as they are used more heavily by core. With each library that's been adopted, we've also been in discussions with the maintainers around security reporting and release schedules, so that Drupal is able to apply upstream security fixes promptly and reliably. For version upgrades — as opposed to security fixes — the approach taken in Drupal 8 is still under discussion. Personally, I'd like to try to stay up-to-date with libraries when new versions are released that don't break backwards compatibility, so that we're able to take advantages of bug fixes and performance improvements. However, this will need to be done on a case-by-case basis since there are always potential trade-offs with new improvements vs. API stability, even when backwards compatibility is attempted.

In conclusion, Drupal has adopted more third-party PHP code during the 8.x release than it has during any previous major core version. This also comes at a time when the PHP community in general has been working on making code more interoperable and attempting to consolidate on particular libraries for providing low-level functionality, a movement which Drupal is now taking advantage of and hoping to push forward. While Drupal 8 will see significant API changes, this is consistent with our previous approach to overhauling and replacing core subsystems, when necessary, and with removing legacy code rather than attempting to maintain backwards compatibility. Work is continuing non-stop on Drupal 8 as we near release, and even brand new users can help with testing and bug reporting, so don't be shy downloading it and trying it out.