PHP Core

On the way to Symfony 5

The most important new features in Symfony 4.3 at a glance

Denis Brumann

The new minor version of Symfony was released at the end of May, heralding the launch of Symfony 5. In addition to big new features such as the Mailer and HTTP Client component, existing components have also been extended and improved and additional deprecations introduced; these deprecated functions will subsequently be removed in Symfony 5. Following is an overview of the most important new features.

Version 4.3 is the second to last minor release in Symfony’s 4.x branch. Versions 4.4 and 5.0 will be released simultaneously in November. Both releases have the same feature set, with the only difference being that in Symfony 5 all features marked as deprecated that have accumulated since the previous major release will be removed. In addition, version 4.4 will become the next long-term support release (LTS) that will replace version 3.4. As a result, the new features are an essential basis for upcoming features in Symfony 5 and for those who rely on LTS releases. In the following article, I would like to briefly outline the most important new features. I will focus on three areas here: What will be removed? What has been changed? What’s new?

What will be removed?

Symfony has a clearly formulated backwards compatibility promise. This guarantees that an upgrade within the current major version does not change existing features in such a way that the expected behavior changes and leads to errors. Security-relevant adjustments are the exception here. In other words, an upgrade from 4.2 to 4.3 should be possible with minimal adjustment and you only get a notice in the deprecation log if you rely on “obsolete” behavior. To make a later upgrade to version 5 easier, you should become familiar with these deprecations and remove them during regular maintenance work. A complete list of deprecations is maintained here; examples of how a required adaptation could look like is also included there.

One of the most important deprecations in Symfony 4.3 concerns the handling of different types in environment variables. So far it was possible for example to store integer or float values as defaults for environment variables. In the future, all environment variables will have to be defined as a string because the system also passes these values as a string, thereby avoiding processing errors.

parameters: # before:
  env(NAME): 1.5 # Deprecation warning

parameters: # after:
  env(NAME): '1.5' #The value must be explicitly declared as a string

The signature of the dispatch()method in the EventDispatcherwas also drastically changed to conform to the recently established PSR-14 standard. While two arguments were previously needed (the name of the event and the event object passed to the observer), in the future only the event object will be mandatory, while the name will be optional. This causes the order of the parameters to be switched. Both variants can be used with Symfony 4.3, but the first one triggers a deprecation warning, while in Symfony 5 it is completely removed. To continue to use the old logic, the EventDispatcherneeds to be decorated using LegacyEventDispatcherProxy::decorate($dispatcher). Another change in the context of the EventDispatchercomponent is the eventclass that up until now always had to be used as a basis (even for custom events). In place of the eventbase class from the component, Symfony\Contracts\EventDispatcher\Eventshould be used in the future. The key difference is that the new class implements the Stoppable Event Interfacefrom the PSR standard, therefore allowing events to be used with any PSR-14 compatible dispatcher. The adjustments in the EventDispatcheralso entail the renaming of Symfony-specific events (Table 1), which makes working with Symfony’s Event Cycle much easier since the class name has been adapted to the event designation.

Event designation old class name new class name
kernel.request GetResponseEvent RequestEvent
kernel.controller FilterControllerEvent ControllerEvent
kernel.view GetResponseForControllerEvent ViewEvent
kernel.response FilterResponseEvent ResponseEvent
kernel.terminate PostResponseEvent TerminateEvent
kernel.exception GetResponseForExceptionEvent ExceptionEvent
Table 1: Renaming Event Classes in Symfony 4.3

 

In Symfony 4.2, a new CacheInterfacewas introduced that deviates from the PSR-6 and PSR-16 standards. The reason for this is the cache stampede protection feature, which is incompatible with these standards. With the current release, the resulting clutter on various cache adapters has been cleaned up a bit and the SimpleCacheAdapteras well as the Psr16Adapterhave been marked as deprecated, along with the PSR-16 adapters. Instead, either the Psr16Cacheor the new CacheInterfacefrom the Contracts should be used. This also results in adjustments to the framework configuration. Instead of referencing the SimpleCacheInterfaceor its service ID cache.app.simple, the cache.appor the CacheInterfaceshould be used when services need a cache as an argument.

The Securitycomponent has some deprecations that should be carefully analyzed, especially when using roles and custom token classes. In the past, the Advanced User Interface had already been marked as deprecated, now Role and SwitchUserRolehave also been caught. At first sight, the first one sounds more serious than it actually is. Symfony already supports string-based user roles, for example in User Interface::getRoles(), In more recent Symfony applications, this class should therefore already play a minor role while in the future, it will no longer be needed internally. Developers who have expanded the Securitycomponent to that effect can see in the corresponding pull request # 22048, how the application can be removed. Things look a bit differently with the removal of SwitchUserRole. Here all references to this role must be replaced by a check for SwitchUserToken. At the same time, various role-related methods were adapted to reflect this change. For example, getRoles()in TokenInterfacehas been renamed to getRoleNames(). Another important change in TokenInterfaceis the (de)serialization. In the future, PHP-specific magic methods must be used; the corresponding methods serialize()or deserialize()will be removed.

Moreover, there are still additional deprecations, for example in the area of the Workflowcomponent, the HttpFoundationas well as in forms. The support of several template engines, which was rarely used in reality, will also be removed. Therefore, Twig has finally prevailed as the de facto standard for templates in Symfony and as of version 5, Symfony will no longer provide custom PHP templates and themes for forms. At the latest before upgrading to Symfony 5, you should also take a close look at these changes and keep an eye on the deprecation logs of the applications you are running to make sure that the transition is possible without major complications.

What’s changing?

In addition to deprecations, numerous existing components have undergone adaptation and expansion. The Validationcomponent will be expanded with a lot of new constraints as of Version 4.3. Both new Ibanand Bicconstraints help to validate payment details according to the ISO 9362 standard. The existing CardSchemeconstraint was expanded with the UATP(Universal Air Travel Plan) type, which can be particularly interesting for applications with payments in the hotel and travel industry. The Uniqueconstraint created the possibility to check an entry in a list for uniqueness – this should not be confused with the existing UniqueEntityconstraint that guarantees that a database field (for example, a user’s e-mail address) is unique. Using the NotCompromisedPasswordconstraint, you can secure password entries by passing the hashed password to a web service to see if it appears on a list of known compromised passwords. For this, the API of https://haveibeenpwned.com is used and it is one of the first uses of the new HttpClientcomponent, which will be introduced in the next section. In the area of date validation, the Timezoneconstraint has been added that checks if an entry has a valid timezone for PHP, for example “Europe/Berlin”; options can be used to further restrict the scope of validity, for example to timezones of a particular geographic region. There are four additional constraints for the validation of numbers: Positive, PositiveOrZero, Negative and NegativeOrZero. The NotBlankconstraint has additionally got the new allowNull option, which is set to false by default to replicate the known behavior. To be sure that values can be serialized as JSON, there is now a separate Jsonconstraint that executes json_decode()and then checks for errors in json_last_error().

In addition to the validation, there are also a few improvements in the Formcomponent. One feature that has been frequently requested is the possibility to provide a help text to the Formelements that can contain HTML code. To this end, an additional option help_htmlis now available to which you can pass a Boolean value; see the first example in Listing 1. In addition, DateTimeTypeand DateTypehave been expanded with the input_formatoption, with which you can now specify the date format as a valid PHP format, as shown in the second example in Listing 1.

Listing 1

$builder->add('accept_agb', CheckboxType::class, [
  'mapped' => false,
  'help' => '<a href="…">I have read the terms and conditions.</a>',
  'help_html' => true,
]);

$builder->add('starts_at', DateTimeType::class, [
  'input' => 'string',
  'input_format' => 'm-d H:i',
]);

Symfony’s DomCrawlerfhas a crucial role above all in functional tests and the new Panther test tool introduced in Symfony 4.2. The integration of the HTML5 PHP library will compensate for some issues and inconsistencies with HTML5 elements when using the PHP DOM extension. The library has already proven itself in other projects such as Drupal and is now also optionally used in DomCrawlerif it has been installed by Composer in the project. Moreover, the component has obtained some new features. A default value can now be additionally passed to both html()and text()methods, which output the content of the current element; the default value is returned instead of an exception if the element is empty. That way you can provide failed tests with informative value, rather than get a generic “The current node list is empty” message. With the new WebTestAssertionTrait, Symfony also provides an extensive collection of assertions that can be used in functional tests to minimize the writing effort. The trait is provided in the WebTestCaseand can be used immediately after an update.

In routing, there are several improvements available that increase comfort. This includes a feature of the debugcommand that also indicates conditions that must be met for a route. The handling of the conditionoption has also been expanded. In addition to expressions, Boolean parameters can also be passed now. Some commonly used options like utf8, localeand formatcan also be defined directly on the route now and do not have to be additionally specified in options. This provides significantly greater clarity, especially for routes that are configured via annotations or XML.

The configuration of services has also been further improved. Symfony provides the ability to provide services with a tag to, for example, pass all tagged services as arguments to another service. The use of tagged services had already been greatly simplified in the past. While before you needed to register compiler passports, as of Symfony 4 you can use the !taggedkeyword in the YAML configuration to pass them as an argument to services. In Symfony 4.3, you can now control the index of this service list using various attributes. In particular, if you want to pass several services, of which probably only one or a few will actually be needed, you’ll probably want to pass a PSR-11 container rather than an iterator, since it supports lazy loading. For this purpose. the !taggedkeyword has been divided into two variations: tagged_iteratorand tagged_locator, The use of immutables, or classes that are no longer modified after instantiation but merely return a new instance with the changed values, will also become easier thanks to support from immutable setters in the service configuration.

Further changes in existing components, such as the Messenger, Workflowand HttpCachecomponents are also described in the Symfony blog in the “New in Symfony” posts, which include detailed descriptions of the changes introduced here.

What’s new?

The Mailerand HttpClientcomponents bring two major innovations to Symfony 4.3. The Mailer had already been announced for the first time at SymfonyLive London in October of last year. The component is the spiritual successor of the independent Swift Mailer library, offering several new features in addition to a modernized codebase. Mime, the third new component not mentioned so far, looks after the improved handling of file attachments in Mailer. In Symfony 4.3, it will also be integrated in the HttpFoundationfor responses with binary data. Two outstanding modernizations for users in the Mailercomponent additionally include the simplified templating of messages using Twig and the support of additional adapters. For API-based adapters, such as for Mailgun, the new HttpClientcomponent will come into effect.

E-mails sent using the Mailercomponent are part of MimeComponent and offer a few advantages over the Swift_Message. Due to the optimized data model, the resulting object graph is significantly smaller and with it, the size of the serialized e-mail and the memory usage as well. Attaching and embedding images is also much easier, since explicit helper methods such as attach()or embedFromPath()are provided. The latter allows you to reference the embedded images in an HTML mail, as the following example shows:

$email = new Email();
$email->from('[email protected]')
  ->to(new Address('[email protected]'))
  ->subject('This is a test')
  ->text('This is a simple text.')
  ->html('<b>New Logo: <img src="cid:logo.png"></b>')
  ->embedFromPath('images/neues_logo.png', 'logo.png');

The Mimecomponent also includes an extension of the Email class called TemplatedEmail, It can be used to generate HTML emails from a Twig template. In addition, you pass the file name of the Twig template to the htmlTemplate()method. If necessary, you can pass an array with template variables to the context()method, something we already know from Symfony controllers of $this->render(). The Twig template also provides us access to the emailobject, so you can add additional attachments or embeddings, or reuse addressees and the subject in the template. Moreover, there are some extensions in Twig that help you create HTML emails, from inline_cssfilters to an inkyextension, with which you can use Foundation for Emails, a CSS framework for responsive HTML emails.

The delivery of emails is controlled in the Mailercomponent. In addition to generic SMTP transport and various vendor-specific variants, such as for Google or Sendgrid as they had already supported Swift Mailer, HTTP and API-based delivery methods are supported. On top of all this are special transports, which are particularly helpful when sending multiple e-mails, such as Failover or Round Robin. Because all transports are addressed using the same interface and the same Mimemessages, you can seamlessly switch between SMTP and API providers by adjusting the appropriate provider DSNs. If you want to send e-mails asynchronously, you only need to initialize the Mailerwith a correspondingly configured MessageBusfrom the Messengercomponent.

The HttpClientcomponent is a key element in supporting the delivery of emails via API. It is already being used in the new NotCompromisedPasswordvalidation constraint as well. Unlike most established libraries like Guzzle, it is designed from the ground up to work asynchronously. If you send a request using the HttpClient, you receive a response which sends a request to the API only when the appropriate methods are called. For example, if you call the getHeaders()method, a stream is created that only queries the method and then pauses. If you then call getContent(), reading of the stream will continue to load the response body as well. The newly introduced HttpClientInterfaceoffers, in addition to the request()method from which you get the responses, yet another method called stream()to which you can pass a list of responses. It can be used to stream any number of HTTP requests and thus significantly reduce the load times for multiple requests. In his presentation of the components at the SymfonyLive in Paris, Nicolas Grekas presented an example that loads 379 images per HttpClient at the same time within 0.4 seconds of a CDN.

Conclusion

Symfony 4.3 is a comprehensive release offering numerous enhancements to existing components and three new components. The new Symfony version is also interesting for those who rely on long-term support releases, as the upcoming release heralds the end of the current LTS version 3.4. It is possible that certain self-written solutions will become obsolete as a result of the improvements. The new components show that even established libraries such as Swift Mailer show room for improvement. They offer not only cosmetic changes through modernized code that meets current standards, but also provide functional added value through better support for parallelization and extended functionality. If you plan to upgrade to version 5, you can already work through some deprecations with the current release, which will make the transition easier.

Top Articles About PHP Core

Stay tuned!

Register for our newsletter

Behind the Tracks of IPC

PHP Core
Best practices & applications

General Web Development
Broader web development topics

Test & Performance
Software testing and performance improvements

Agile & People
Getting agile right is so important

Software Architecture
All about PHP frameworks, concepts &
environments

DevOps & Deployment
Learn about DevOps and transform your development pipeline

Content Management Systems
Sessions on content management systems

#slideless (pure coding)
See how technology really works

Web Security
All about
web security

PUSH YOUR CODE FURTHER