This article originally appeared on our sibling site, Symfony Station.
This is a follow-on to our article, How Symfony components power Drupal’s drive to new frontiers. In it we will look at the nuts and bolts of four of the components/libraries Drupal uses, plus the Twig templating engine.
If you read the pevious article, you saw that Symfony is useful for providing Drupal with up-to-date and clean code, more stability and fewer maintenance headaches, better coding workflows and developer experiences, and better application performance.
And Twig improves content design experiences due to its outstanding templating capabilities.
A quick FYI, Symfony Station is a Drupal site. You can understand our decision-making process for choosing it by reading our article, How Symfony Station was built: an adventurous exploration of layout solutions. Another quick aside, we cover the components each Symfony-based CMS uses in our article, Exploring the 17 Content Management Systems of Symfony.
Now that I am finished with internal linking, let’s move on to four of the Symfony components used in Drupal core. ;)
Components
Symfony components are decoupled libraries available for use in your PHP applications. This is true if your apps are based on Symfony, Laravel, eCommerce platforms, Sulu, Drupal, or a roll-your-own CMS. They are integrated into thousands of projects and have been downloaded billions of times.
There is no need to reinvent the wheel with your coding if a safe, reliable, and cost-free wheel is available. Thus the use of components in programming. Or even in frameworks or content management systems. This is true for frontends or, in Symfony’s case, backends.
Drupal integrated Symfony components in Drupal 8 and has further incorporated them since. Drupal 10 is tied to Symfony 6.
The CMS/DXP platform uses many Symfony components as third-party libraries in its core code. See the image below.
Why? As seen in our previous article, the Drupal Association justifies using it by saying:
“Symfony offers many benefits to Drupal developers:
- We save time that would otherwise be used for solving issues or writing and maintaining code already written and tested by others.
- To an extent, it encourages us Drupalists to use endorsed, tested, and maintained code from elsewhere. Meaning we step outside of our Drupal bubble to learn something new that adds value to the project. So we might even be able to step away from less-than-ideal Drupal solutions in favor of a Symfony solution.
- It promotes decoupled code.
- It also invites standardization of best practices.”
General Visualization of Drupal Core
So, let’s look at the nuts and bolts of four of the components/libraries Drupal uses, plus Twig.
Routing
I am sure most of you are familiar with routing; if not, here’s a quick explanation from Symfony:
“When your application receives a request, it calls a controller action to generate the response. The routing configuration defines which action to run for each incoming URL. It also provides other useful features, like generating SEO-friendly URLs (e.g. /read/intro-to-symfony
instead of index.php?article_id=57
).
Routes can be configured in YAML, XML, PHP, or using attributes. Symfony recommends attributes because it's convenient to put the route and controller in the same place. Attributes are native in PHP 8 and higher versions so that you can use them right away.”
For a detailed explanation, read this.
In Drupal 💧
Routing in Drupal is built on top of the Symfony component. So, like with Symfony, each route possesses a method in a controller class that provides a response. In Drupal, the returned response is usually a render array.
Specifically, when Drupal receives an HTTP request, it tries to match the path to known routes. If matched, the route’s definition is used to deliver a page. If it isn’t you, get the dreaded 404 page.
Drupal also checks access based on requirement keys. If the key conditions fail, a 403 or 404 page may be displayed.
After the response is returned, Drupal checks if the value is a response to Symfony’s HttpFoundation component or a render array. If it’s an array, you get your HTML response.
As mentioned above, routes define access requirements via the requirements key. Multiple validators can be added, but there must be at least one with a true result to avoid a 403, access denied.
Drupal’s routing system also allows Drupal modules to define routes programmatically.
EventDispatcher
Using the EventDispatcher component allows your app components to communicate with one another by dispatching events and listening to them.
Here’s an introduction and example from Symfony:
“The Symfony EventDispatcher component implements the Mediator and Observer design patterns to make all these things possible and to make your projects truly extensible.
Take an example from the HttpKernel component. Once a Response
object has been created, it may be helpful to allow other elements in the system to modify it (e.g. add some cache headers) before it's used. To make this possible, the Symfony kernel throws an event - kernel.response
.
Here's how it works:
- A listener (PHP object) tells a central dispatcher object that it wants to listen to the
kernel.response
event; - At some point, the Symfony kernel tells the dispatcher object to dispatch the
kernel.response
event, passing with it anEvent
object that has access to theResponse
object; - The dispatcher notifies (i.e. calls a method on) all listeners of the
kernel.response
event, allowing each of them to modify theResponse
object.”
For a detailed explanation, read this.
In Drupal 💧
Hooks and events are used in Drupal to integrate parts of its system. Hooks use implicit registration, and events use explicit registration.
Events are used to intercept specific actions or flows in order to stop or modify them. Many things that were handled by hooks prior to Drupal 8 are now handled by dispatching events as a result of integrating Symfony.
Using its event system, Drupal component events can be passed to the event dispatcher.
Symfony provides the events dispatcher system, and it allows Drupal components to interact with each other. Events are dispatched, and event subscribers listen to them and then react to changes or other processes.
Drupal mainly uses named events rather than event objects, as many events leverage the same event object class. For building event subscribers, you need to understand how to create services, register them, and dependency injection.
The CMS’s event system components include:
- Event Subscribers - A class that implements the
\\Symfony\\Component\\EventDispatcher\\EventSubscriberInterface
. - Event Dispatcher - A class that implements
\\Symfony\\Contracts\\EventDispatcher\\EventDispatcherInterface
. - Event Registry - The registry for subscribers is stored within the Event Dispatcher object as an array keyed by the event name and the event priority (order).
- Event Context - A class that extends the
\\Drupal\\Component\\EventDispatcher\\Event
class.
DependencyInjection
If you are an experienced PHP developer, you are familiar with dependency injection. It is a staple of object-oriented programming. This software design approach allows you to use a class without directly referencing it.
Symfony’s DependencyInjection component implements a PSR-11 compatible service container that lets you standardize and centralize how objects are constructed in your app.
For a detailed explanation, read this.
In Drupal 💧
With the integration of Symfony in Drupal 8 the CMS introduced the concept of services to decouple reusable functionality and makes these services pluggable and replaceable by registering them with a service container.
A Drupal service container allows you to declare classes and services. You can then define their dependencies. A service is an object managed by the services container. With services, dependencies are arguments passed to their constructor.
This allows you to create services that can be injected into your app to take care of specific functional tasks. You can also change existing logic.
Example include services used to access the database or sending an e-mail.
Forms that require a Drupal service or a custom service should also access the service using dependency injection.
An additional benefit of dependency injection in Drupal is that code is easier to test with PHPUnit tests. Your domain's business logic being separated from Drupal dependencies is why.
Validator
Validation is a common task in web applications. Data entered into forms needs to be validated. It also needs to be validated before it is written into a database or passed to a web service. This is one of the most important things you do. Because → security!
Symfony’s Validator component can handle this for you. Here’s an explanation from Symfony:
“The goal of validation is to tell you if the data of an object is valid. For this to work, you'll configure a list of rules (called constraints) that the object must follow in order to be valid. These rules are usually defined using PHP code or attributes, but they can also be defined as .yaml
or .xml
files inside the config/validator/
directory.
To actually guarantee that the value adheres to the constraint, the object must be passed to the validator service to be checked.”
For a detailed explanation, read this.
In Drupal 💧
Drupal entities are validated before they are saved. The validate method is invoked and runs the Symfony validation system against the entity’s constraints and field values. The method returns an object with any constraint violations and doesn’t throw an exception.
You must check for violations and build an array of error messages and return a 400 bad request response. You can loop over violations to identify invalid fields and get messages for the invalid value.
Entity validation works with an outside-in strategy. First entity-level constraints are validated, and then each field is validated, going through each field’s properties. Entity validation is not automatic. It must be explicitly invoked when using entities programmatically.
Twig
Symfony’s founder Fabien Potencier also developed PHP’s premiere templating engine, Twig.
You can explore all of its advantages in our article, Twig: The Ultimate Guide to the Premier PHP Templating Language. As I wrote there, “to put it simply, Twig or any templating engine is used to output variables inside HTML. It is a modern, robust, OOP-based engine.”
Twig template files have the .html.twig extension. The Twig language uses a syntax similar to Django and was inspired by Jinja. It’s simple, with basic logic and functions.
Read the Twig documentation for all the details.
In Drupal 💧
Twig was adopted for templating in Drupal 8. Theme functions were deprecated in favor of outputting everything via a Twig file. This vastly improved security and readability and decreased reliance on PHP.
It also made many frontend Drupal developers very happy. And the experience will get even better with Drupal 10.1.
Twig allows you to design custom and optimal templates for all your Drupal page and article types.
Drupal’s theme layer (a processor) passes variables to Twig. Variables or properties of objects are printed by wrapping the name in curly brackets in the code. Drupal’s default templates provide info in the file’s document block that details all the available Twig variables. When programming, keep in mind that Drupal caches Twig templates.
According to Ryan Weaver of SymfonyCasts, when writing Twig code in your HTML, there are only two different syntaxes:
- {{ }} The "say something" syntax
- {% %} The "do something" syntax
The "Say Something" Syntax: {{ ... }}
The double-curly-brace ({{) is always used to print something. If what you need to do will print something to the screen, then you'll use this syntax.
The "Do Something" Syntax: {% ... %}
The curly-percent ({%) is the other syntax, which I call the "do something" syntax. It's used for things like if and for tags and other things that "do" something.
The Comment Syntax: {# ... #}
Actually, we've lied a little. There is a third syntax, used for comments: {#. Just like with the "say something" and "do something" syntaxes, write the opening {# and also the closing #} at the end of your comments.
Twig also has functions that can be used in a say something or do something Twig delimiter. And it has filters as well.
Templates can inherit from a parent template and use blocks.
You can see Drupal’s documentation of Twig to learn more.
Summing it up
That’s all, folks. Thanks for reading and, more importantly, learning how these four Symfony components, plus Twig, work in Drupal.
You’ve seen how Drupal decided to improve and simplify its core by integrating Symfony components. This was a wise decision, as Drupal desperately needs simplifying. See my article, Does Drupal Have a Path to Growth? for why.
Personally, I look forward to Drupal being even more tightly integrated with Symfony in the future.
Bonus
Here are the remaining Symfony components used by Drupal, as covered in our previous article.
HttpFoundation
The Symfony HttpFoundation component replaces default PHP global variables and functions with an object-oriented layer.
HttpKernel
The HttpKernel component provides a structured process for converting a Request
into a Response
by using the EventDispatcher component. It's flexible enough to create a full-stack framework (Symfony), a micro-framework (Silex), or an advanced CMS (Drupal).”
Translation
The term "internationalization" (often abbreviated i18n) refers to the process of abstracting strings and other locale-specific pieces out of your application into a layer where they can be translated and converted based on the user's locale (i.e., language and country).
Console
The Console component eases the creation of beautiful and testable command line interfaces. It allows you to create command-line commands. Your console commands can be used for recurring tasks, such as cronjobs, imports, or other batch jobs.
Yaml
The Symfony Yaml component parses YAML strings to convert them to PHP arrays. It is also able to convert PHP arrays to YAML strings.
Polyfill Iconv
The Polyfill Iconv component provides a native PHP implementation of the php.net/iconv functions (short of [ob_iconv_handler](<https://php.net/ob-iconv-handler>)
). More information can be found in the main Polyfill README.
Polyfill PHP 8.0
The Polyfill PHP 8.0 (as you would expect) component provides features added to PHP 8.0 core.
Process
The Process class executes a command in a sub-process, taking care of the differences between operating system and escaping arguments to prevent security issues. It replaces PHP functions like exec, passthru, shell_exec and system.
Serializer
The Serializer component is meant to be used to turn objects into a specific format (XML, JSON, YAML, ...) and the other way around.
Mime
The Mime component allows manipulating the MIME messages used to send emails and provides utilities related to MIME types.
Resources
Docs
Explore the Symfony Documentation.
And Drupal component documentation.
Books
Drupal 10 Development Cookbook - Third Edition
Learn More
When Drupal met Symfony (Video)
This video is a valuable resource.
Training
SymfonyCasts
If you subscribe to SymfonyCasts (and you should), this course is also helpful. Note that it covers Drupal 8, which as mentioned at the top of this article, is when the DXP first implemented Symfony. But it still has lots to offer. Plus, it has dinosaurs! 🦖 Don’t miss their Twig course, either. 🌿