Astuce Architecture Symfony

Licences

  • Symfony est sous Licence MIT Licence
  • La documentation officielle est sous Licence Creative Commons (Attribution-Share Alike 3.0 Unported Licence)
  • Twig est sous licence BSD-3

Composants, Bundles et Bridges

  • Un composant est une bibliothèque écrite en PHP, pouvant fonctionner sans Symfony.
    Exemples : Mailer, HttpClient, Form, ...

  • Un bundle est un plugin pour Symfony, servant d'interface entre une bibliothèque et Symfony. Il permet par exemple de configurer facilement la bibliothèque via Symfony.
    Exemples : twig-bundle qui permet de configurer Twig

  • Un bridge est un élément Symfony permettant d'étendre une bibliothèque en y ajoutant des choses utiles à Symfony Exemples : twig-bridge qui ajoute des fonctions à twig


Best practices

La liste des best practices est disponible ici.


Arborescence

Recommandée

├── assets/
├── bin/
│   └── console
├── config/
├── public/
│   └── index.php
├── src/
│   └── Kernel.php
├── templates/
├── tests/
├── translations/
├── var/
│   ├── cache/
│   └── log/
├── vendor/

Personnalisation

Non recommandé :

  • config/ : peut poser problème pour le déploiement des recipes Flex

Configurable :

  • /var/cache/ et /var/log/ : via surcharge des méthodes getCacheDir()et getCacheLog() du Kernel.php
  • templates/ : via twig.default_path (par défaut dans le twig.yaml)
  • translations/ : via framework.translator.default_path (par défaut dans le translation.yaml)

Risqué à modifier :

  • public/ : vérifier que tous les chemins restent corrects dans l'index.php.
    Il faut également ajouter la propriété extra.public-dir dans le composer.json (si on utilise Flex)

  • vendor/ : vérifier que tous les chemins restent corrects dans l'index.php. Il faut également ajouter la propriété config.vendor-dir dans le composer.json.


Flex

Documentation

C'est un plugin Composer qui va permettre d'ajouter/modifier les commandes Composer.

Par exemple, la commande require va permettre d'installer des paquets particuliers appelés Recipes.

Recipes

Une recipe est un paquet ne contenant qu'une liste de dépendances (via un composer.json), ainsi qu'une liste de modifications à effectuer (via un manifest.json) :

  • créer un fichier à tel endroit
  • ajouter telles lignes de configuration dans tel fichier
  • ...

Certains recipes sont officielles (= maintenues par la code team Symfony), d'autres contrib.

Elles sont toutes visibles sur https://flex.symfony.com.

Configurators

Les fichiers manifest.json des recipes sont lues via les Configurators.

Ex de fichier :

{
    "bundles": {
        "Alexandre\\EvcBundle\\AlexandreEvcBundle": ["all"]
    },
    "copy-from-recipe": {
        "config/": "%CONFIG_DIR%/"
    },
    "env": {
        "EVC_API": "Enter the api version provided by evc.de support",
        "EVC_USERNAME": "Enter your reseller account number",
        "EVC_PASSWORD": "Enter your api password, not the password to connect on evc.de website"
    }
}

Cette configuration nécessite par exemple 3 Configurators pour :

  • activer un bundle
  • copier un répertoire de configuration
  • ajouter des variables d'environnement

Les configurators natifs sont visibles ici.

Commandes composer pour Flex

De nouvelles commandes Composer ont été créées pour interagir avec Flex :

  • composer recipes : liste les recipes disponibles (et affiche les mises à jour éventuelles)
  • composer sync-recipes [--force] : met à jour une recipe

Gestion des erreurs et exceptions

Lorsqu'une erreur (PHP) est levée :

  • L'ErrorHandler la transforme en exception, qui est levée

Lorsqu'une exception est levée :

  • L'HttpKernel l'attrape et dispatche un évènement kernel.exception
  • L'ErrorListener effectue un forward vers l'ErrorController
  • L'ErrorController retourne une Response contenant l'erreur formatée via le ErrorRenderer

Gestion des évènements

Documentation

La classe Event contient deux méthodes :

  • stopPropagation() : elle indique qu'on ne souhaite pas que les prochains listeners ne traitent l'évènement
  • isPropagationStopped() : elle indique si on a demandé à stopper la propagation

À l'instar des services, on utilisait historiquement une chaîne de caractères comme identifiant de l'évènement (ex: kernel.terminate). Depuis Symfony 5, on utilise directement le nom de sa classe (ex: Symfony\Component\Mailer\Event\MessageEvent).

Commande Symfony

Pour lister les évènements disponibles et leurs listeners, utiliser la commande :

bin/console debug:event-dispatcher

Listener vs Subscriber

  • Un Listener est un callable qui reçoit un Event.
  • Un Subscriber est une classe qui mappe des évènements à des Listeners

Best practice :

Le subscriber est plus pratique d'utilisation, car il ne nécessite aucune configuration (en fichier yaml).
Il suffira qu'il implémente EventSubscriberInterface.

Principaux évènements

Les évènements du Kernel sont visibles ici.


Versions de Symfony

Documentation

Il y a deux versions principales à connaitre :

  • La dernière version stable (ex: 5.1.8) : elle inclue toutes les nouvelles fonctionnalités, corrections de bogue et patchs de sécurité.

  • La version LTS (Long Term Support) (ex: 4.4.16) : elle inclue toutes les dernières fonctionnalités de la version majeure, ainsi que les nouvelles corrections (pendant 3 ans au total) de bogue et patchs de sécurité (pendant 4 ans au total).

La version LTS est changée tous les 2 ans. Comme les versions mineures sortent tous les 6 mois, on a seulement 4 versions mineures par majeures.

Les versions non-LTS sont supportées 8 mois.

Note : en version 2.x, il y a eu plusieurs versions LTS

Backward compatibility promise

D'une version mineure à l'autre, Symfony garantie un fonctionnement à l'identique pour toute l'API publique (= ni @internal, ni @experimental).

Dépréciations

Documentation

Les dépréciations indiquent qu'une fonction/classe/... ne devrait plus être utilisée grâce à l'annotation @deprecated.

  • Elle doit préciser depuis quand la fonction/classe/... est dépréciée.
  • Elle peut indiquer la version à laquelle elle a été ajoutée, et la méthode à utiliser à la place.

Surcharger des éléments de Symfony ou de bundle

Comment surcharger un champ de formulaire (FormType) ?

On crée une extension de type.

Comment surcharger une route définie par un bundle ?

Il faut :

  • supprimer les imports des routes de ce bundle
  • déclarer sa propre route

Comment surcharger un contrôleur ?

On peut :

  • en surcharger la route (cf. ci-avant), pour pointer vers un nouveau contrôleur.
  • le décorer comme n'importe quel service

Comment modifier un service ?

On peut :

  • le décorer (best practice)
  • redéfinir sa définition (via le service.yaml) et le faire pointer vers un nouveau service
  • le modifier via le Compiler Pass

Interopérabilité

Le framework est compatible avec de nombreuse PSR :

  • PSR-1/PSR-2 : coding standards

  • PSR-4 : namespaces et autoloading

  • PSR-3 : logger (via MonologBundle)

  • PSR-6/PSR-16 : cache

  • PSR-11 : conteneur de services

  • PSR-14 : dispatcheur d'évènements

  • HttpClient est compatible : PSR-7/PSR-17/PSR-18

  • HttpPlug est compatible avec l'implémentation : PSR-7