Auteur : Jean-Marc El Baki

Quoi de neuf depuis 1 an ?

Etat des lieux

La politique de release de Symfony a permis de voir l’émergence de Symfony 3 sur 2016. Pas moins de quatre versions mineures de 3.0 (en élargissant un peu puisque sortie fin 2015) à la sortie imminente de 3.3, plus de 70 releases, 1928 demandes de merge acceptées et plus de 3400 encore ouvertes.

L’événement était donc le moment idéal pour faire un petit point sur les nouveautés mais aussi les dépréciations afin de préparer au mieux l’arrivée des prochaines versions importantes. C’est en effet en novembre 2017 que sortiront conjointement la version 3.4, première LTS de Symfony 3 et Symfony 4.0 qui verra le retrait du code de toutes les dépréciations. La version 3.4 servira de pont entre Symfony 3 et Symfony 4 comme l’a été la 2.8 à l’époque.

Dépréciations

Variables d’environnement

Il est possible d’injecter des paramètres via l’utilisation des variables d’environnement. Mais ces variables étaient déclarées spécifiquement pour être utilisées par Symfony via des règles de nommage dont la principale l’utilisation du préfix SYMFONY__. Désormais toutes les variables d’environnement seront accessibles dans les fichiers de configuration via la syntaxe %env().

# app/config/config.yml
doctrine:
    dbal:
        # ...
        password: "%env(ENVVAR_NAME)%"

Il est possible de définir une valeur par défaut au niveau des paramètres de la façon suivante :

# app/config/parameters.yml
parameters:
    env(ENVVAR_NAME): anyValue

Warmup du cache

Avant de se lancer pour la 1ère fois, une application Symfony génère plusieurs fichiers de cache. Au cours de cette opération est notamment effectuée la conversion des configurations (annotations, YAML et XML) en PHP natif, ainsi que les templates Twig. Cette étape est ce que l’on appelle le warmup. Pour obtenir la liste des warmup existants, la commande suivante peut-être utilisée :

$ bin/console debug:container --show-private --tag kernel.cache_warmer

Il est également possible de créer ses propres warmup en implémentant Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface

Symfony fourni deux méthodes pour préparer les caches

# warmup du cache mais ne le nettoie pas
$ bin/console cache:warmup [--env=prod|dev]
# nettoyage et warmup du cache
$ bin/console cache:clear [--env=prod|dev]

Mais l’implémentation du warmup de la commande cache:clear ne donne pas le même résultat que la commande cache:warmup et pose quelques problèmes.

Dans la pratique il est donc conseillé d’effectuer :

# warmup du cache mais ne le nettoie pas
$ bin/console cache:clear --no-warmup [--env=prod|dev]
$ bin/console cache:warmup [--env=prod|dev]

X-Status-Code

Lors de la gestion des exceptions dans une application, il était possible de changer le code de la réponse HTTP grâce à l’utilisation du header X-Status-Code.

use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;

public function onKernelException(GetResponseForExceptionEvent $event)
{
    $exception = $event->getException();
    $response = new Response('...', 500, ['X-Status-Code' => 404]);

    /* ... */

    $event->setResponse($response);
}

Ce header ne fait pas partie des standards de HTTP, il sera donc enlevé en version 3.3 et le code retour sera surchargé au niveau de l’événement grâce à la nouvelle méthode allowCustomResponseCode().

public function onKernelException(GetResponseForExceptionEvent $event)
{
    $exception = $event->getException();
    $response = new Response('...', 404);

    /* ... */

    $event->allowCustomResponseCode();
    $event->setResponse($response);
}

Composant ClassLoader

Symfony intègre un composant gérant différentes règles d’autoloading depuis longtemps (avril 2012 exactement). À cette époque Composer était encore balbutiant, le composant avait donc été mis en place pour répondre aux problématiques suivantes :

  1. Autoloading
    Deux classes se chargent d’implémenter les règles d’autoloading telles que définies par les PSR-0 et PSR-4.
  2. Mapping de classes
    Notamment utilisé pour les bibliothèques tiers qui ne suivent pas les règles de nommage.
  3. Cache
    En plus des classes précédentes, Symfony en fournit permettant d’encapsuler une mise en cache des classes à charger.

Dans les faits l’autoader de Composer est rapidement devenu la méthode standard utilisée par Symfony pour cette tâche. Si l’on regarde comment est chargé le gestionnaire d’autoloading dans une application :

// web/app.php
<?php
/* ... */
$loader = require __DIR__.'/../app/autoload.php';
/* ... */
// app/autoload.php
<?php
/* ... */
$loader = require __DIR__.'/../vendor/autoload.php';
/* ... */
// vendor/autoload.php
<?php
/* ... */
require_once __DIR__ . '/composer/autoload_real.php';
/* ... */

De plus l’utilisation conjointe de Composer et des dernières optimisations de PHP 7 permet de se passer d’une mise en cache des classes au niveau applicatif que faisait le front controller (app.php et app_dev.php). On constate d’ailleurs que dans le code sur les développements en cours de la version 3.3 cette obsolescence est déjà prise en compte.

// web/app.php
/* ... */
$kernel = new AppKernel('prod', false);
if (PHP_VERSION_ID < 70000) {
    $kernel->loadClassCache();
}
/* ... */

Case pour les nom de services

Le nom des services était insensible à la case. Ce ne sera plus le cas.

Nouvelles fonctionnalités

Flash Messages depuis Twig

On dispose d’un accès plus direct, et ce nouveau helper gère automatiquement un point qui était gênant : la recherche d’existance d’un message entraînait le lancement d’une session ce qui obligeait, pour faire les choses proprement, à vérifier s’il en existait une.

Avant

{% if app.session is not null and app.session.started %}
    {% for label, messages in app.session.flashbag.all %}
    ...
    {% endfor %}
{% endif %}

Maintenant

{% for label, messages in app.flashes %}
    ...
{% endfor %}

Injection de dépendance dans les contrôleurs

Symfony encourage désormais l’utilisation de Symfony\Bundle\FrameworkBundle\Controller\AbstractController. Cette classe fournit les même helpers que la classe Controller classique. Sauf les méthode get(), has() et getParameter() qui ont disparu ne donnant ainsi plus accès au conteneur de services pour encourager une déclaration explicite des dépendances au niveau du contrôleur. En pratique toutes les méthodes ne concernant pas le conteneur de service ont été exportées dans un trait (Symfony\Bundle\FrameworkBundle\Controller\ComposerTrait) utilisé par la classe historique et la nouvelle classe abstraite.

Workflow et gestion des ACL

Le composant Workflow permet d’utiliser les expressions languages pour intégrer les fonctionnalités de Guard en terme de vérification des droits.

XLiff linter

La console permettait de vérifier la syntaxe de fichier Twig (lint:twig) et YAML (lint:yaml). S’ajoute maintenant une vérification des fichiers d’internationalisation.

bin/console lint:xliff

DataCollector pour le cache

Les Data Collectors permettent au profiler de Symfony de récupérer des informations pour le débogage. Il est possible d’en créer soit-même, et Symfony en propose déjà plusieurs en standard (formulaires, requêtes HTTP, Doctrine, etc.).

Une nouvelle section permet donc d’avoir quelques informations sur l’utilisation du cache.

Implémentation de la PSR-16

La PSR-16 (Simple Cache) définit une interface pour les bibliothèques de cache. Elle se veut une version simplifiée en terme d’utilisation de la PSR-6.

FQCN comme id de dervice

Il est possible d’utiliser le FQCN d’une classe directement comme nom de service, ce qui rend optionnel le champ class de la configuration de celui-ci.

#services.yml
services:
    AppBundle\Foo\Bar:
        arguments: []
use AppBundle\Foo\Bar;

/* ... */

public function anyAction()
{
    $service = $this->get(Bar::class);
}

WebServerBundle

PHP 5.4 a vu l’arrivée d’un serveur web intégré (cf. php -s) ce qui permettait de tester localement les applications sans avoir à mettre en place une stack avec un serveur web complet. Cette fonctionnalité a rapidement été intégrée à Symfony au travers de la ligne de commande (php bin/console server:{run,start,stop,status}).

Ces commandes sont déportées dans leur propre bundle et bénéficient de quelques améliorations comme la recherche automatique d’un port disponible et le fait qu’il n’est plus nécessaire de préciser l’hôte et le port (ces données sont sauvegardée dans un fichier .web-server-pid).

Imports des fichiers de configuration

La commande imports est utilisable avec les motifs de glob. Ainsi, les syntaxes suivantes sont donc maintenant reconnues pour charger une ensemble de fichiers.

# config.yaml
imports:
    - { resource: "*.yml"} 
    - { resource: "foo/**/*.{yml,xml}"}

Configuration par défaut des services

Il est possible de définir une configuration par défaut pour l’ensemble des services d’un fichier de configuration.

# /fichier/config.yaml
services:
    _default: 
        public: false
        autowire: true

Bien sûr, cette configuration sera surchargée si elle est redéfinie dans un service particulier.

Arguments nommés

La déclaration des services reconnaît les arguments nommés, ce qui est notamment utile pour les services reposant sur le système d’autowiring.

use Doctrine\ORM\EntityManager;
use Psr\Log\LoggerInterface;

namespace Acme;

class NewsletterManager
{
    private $logger;
    private $em;
    private $apiKey;

    public function __construct(LoggerInterface $logger, EntityManager $em, $apiKey)
    {
        $this->logger = $logger;
        $this->em = $em;
        $this->apiKey = $apiKey;
    }
}
# Syntaxe avec les différentes nouveautés du DI
services:
    _defaults: { autowire: true }
    Acme\NewsletterManager: { $apiKey: "%mandrill_api_key%" }

# Syntaxe classique
services:
    newsletter_manager:
        class: Acme\NewsletterManager
        arguments:
            $apiKey: "%mandrill_api_key%"
        autowire: true

Implémentation de la PSR-11

La PSR-11 (Container Interface) toujours en cours de validation permet de rendre interopérable les différents systèmes de chargements de services entre les frameworks (cf. cette conférence sur la notion de framework-agnostic pour un exemple concret).

Avec la version 3.3, Symfony\Component\DependencyInjection\ContainerInterface étend maintenant Psr\Container\ContainerInterface ce qui ajoute également de nouvelles exceptions.