Compte-rendu Symfony Live Paris 2017

Liste des conférences et slides : https://joind.in/event/symfonylive-paris-2017/schedule/list

Keynote sur Symfony 4 (Fabien Potencier)

Traditionnellement, le Symfony Live démarre par une première conférence de Fabien Potencier, le créateur du fameux framework. Il nous présente les principes fondateurs de la future version 4 (attendue d'ici la fin de l'année), qui s'adapte à l'évolution actuelle du monde backend, réduit de plus en plus souvent à une API REST. En effet, les applications web modernes sont souvent des SPA (single page app), donc l'interface utilisateur est complètement écrite en html/css/javascript et chargée une seule fois lors de la première requête, ensuite tout est mis à jour en ajax. De plus, la multiplication des clients (appli web, appli mobile pour les différentes plateformes, objets connectés... ) favorise ce type d'usage.

Symfony Flex est le nouveau mode de fonctionnement de Symfony 4, qui sera un mode "micro-framework", c'est-à-dire qu'il n'embarquera que le strict minimum. Actuellement, beaucoup de développeurs partent de la distribution standard pour ensuite retirer les bundles dont ils n'ont pas besoin, ce qui est une opération répétitive et peu valorisante. Grâce à Symfony Flex, l'installation de nouveaux bundles sera facilitée (plus besoin de recopier des configurations à la main, ni d'activer le bundle dans AppKernel.php).

Autre nouveauté : le code de l'application (dans /src) ne devra plus être placé dans un bundle, le namespace par défaut sera "App" tout simplement. On poursuit donc une logique déjà introduite dans les dernières versions du framework et avec la publication des bonnes pratiques officielles.

L'arborescence par défaut est aussi simplifiée et se rapprochera d'une arborescence unix traditionnelle, par exemple avec la création d'un répertoire /var et /etc.

Fabien Potencier est en train de publier une série de 8 articles détaillés sur le sujet : http://fabien.potencier.org/

Pour vous faire une idée du futur Symfony : https://github.com/symfony/skeleton

Grâce aux tags Varnish, j'ai switché ma prod sur Raspberry Pi (Jérémy Derussé)

Cette conférence porte sur les problématiques de performance que rencontrent tous les frameworks backend, et sur l'utilisation d'un cache HTTP efficace avec Varnish. L'auteur commence par rappeler un adage bien connu : pour faire une application performante, le but à atteindre est d'appeler le backend le moins possible !

Traditionnellement, les caches HTTP posent plusieurs problèmes :

  • Difficulté de mettre en cache les pages "privées" liées à un contexte spécifique : Varnish permet de le faire, mais ce n'est pas l'objet de cette conférence.
  • Modèle de cache basé sur l'invalidation des ressources en détectant leur modification (etag, last-modified) : difficile à implémenter pour les ressources dynamiques (on ne parle pas ici des css, images... ).
  • Modèle de cache basé sur l'expiration au bout d'un temps défini (expires, cache-control) : pas de contrôle sur l'invalidation; on recalcule une ressource même si elle n'a pas changé, ou au contraire si la ressource a changé l'utilisateur aura encore l'ancienne version jusqu'à l'expiration du TTL.

Grâce à l'utilisation d'en-têtes HTTP spécifiques, Varnish permet de résoudre ces problèmes en ne regénérant que ce qui a été modifié, au moment où le changement a lieu. Tout l'enjeu consiste donc à tagguer précisément ses requêtes pour bénéficier d'un cache efficace. Une ressource qui change provoquera l'invalidation de tous les caches comprenant le tag correspondant à cette ressource.

Pour y arriver, il faut automatiser la création de ces tags en collectant les ressources affichées et en générant un identifiant. Dans l'optique d'une application de type API REST, cela peut se faire en créant un listener sur l'événement post serialize du composant JMS\Serializer. Le composant TagHandler du bundle FOSHTTPCache permet lui de s'interfacer avec Varnish.

Ensuite, il faut encore invalider le cache en détectant les changements et en appelant Varnish pour regénérer les caches taggués. Cette fois on passe par par l'événement onFlush de Doctrine, qui, grâce à l'objet UnitOfWork, permet de récupérer les changements effectués sur les entités.

Cette technique marche bien pour les applications où les requêtes en lecture sont supérieures aux écritures et où on peut identifier facilement les ressources modifiées, mais ralentit les écritures et rend le backend dépendant de l'infrastructure (couplage fort à Varnish).

L'auteur nous fait une démo impressionnante où une appli hébergée sur un ordinateur embarqué Orange Pi (équivalent encore moins cher du Raspberry Pi) passe de 7 requêtes par secondes à plus de 3000 quand il active le cache intelligent !

Slides : https://www.slideshare.net/JrmyDeruss/grce-aux-tags-varnish-jai-switch-ma-prod-sur-raspberry-pi

Utiliser Webpack dans une application Symfony (Alain Hippolyte)

Cette conférence concerne la partie front-end et la manière de manipuler les assets (images, css, js... ), traditionnellement gérés par l'outil Assetic, mais qui ne fait plus partie de la distribution standard de Symfony depuis la version 2.8. Assetic est en effet relativement limité parce qu'il n'interprète pas le contenu des fichiers et que le monde front-end a considérablement évolué en se complexifiant. De nouveaux outils dédiés au développement front sont dès lors apparus, comme Grunt, Gulp, Brunch... et Webpack.

Contrairement aux "simples" task runners que sont des outils comme Grunt et Gulp, Webpack est un module bundler, c'est-à-dire qu'il prend les modules sources (que ce soit du js, coffeescript, typescript, mais aussi scss, less... ) avec leurs dépendances et construit des assets statiques, un peu comme le conteneur de service de Symfony.

Quatre concepts sont au coeur du système (cf. https://webpack.js.org/concepts/) :

  • Entry points : comme son nom l'indique, on définit ici les points d'entrée vers les fichiers source à traiter, ce sera votre main.js, style.css, etc. L'outil va lire toute l'arborescence des fichiers js en suivant les dépendances selon la convention CommonJS.
  • Ouput : définit le chemin et le fichier de destination du bundle final.
  • Loaders : définit les règles de transformation des fichiers sources avant de les inclure dans le bundle final. C'est ici qu'on pourra convertir le scss/less en css, le coffeescript en js, utiliser babel, etc.
  • Plugins : dernière étape avant la création du module final, les plugins permettent d'exécuter une action sur le module construit jusqu'ici. Exemple : uglifyjs pour optimiser le js.

Voyons maintenant son utilisation dans le cadre d'un projet Symfony. Webpack étant un outil en nodejs, on l'installe avec npm. Les bundles créés par Webpack seront référencés comme un asset classique dans les templates Twig, par exemple :

<script src="{{ asset('/build/bundle.js') }}"></script>

Il faut bien être conscient que Webpack interprète absolument tous les fichiers qu'il rencontre. Donc, les polices de caractères et images référencés dans les css devront être traités en spécifiant des loaders de type fichier.

Un problème peut se poser pour les cas particuliers que sont le symbole $ de jquery ou le javascript inline. Des plugins existent pour traiter ces problématiques.

Enfin, pour éviter de devoir lancer la commande webpack à chaque modification de ressource front, Webpack propose un plugin de "live reload", qui installe un serveur websocket qui permet au navigateur de se rafraîchir tout seul quand c'est nécessaire. Ce plugin est bien sûr à activer en dev uniquement, l'auteur fournit la configuration Symfony pour modifier assets_base_url et le faire pointer sur le serveur websocket.

Slides : https://www.slideshare.net/alainhippolyte1/utiliser-webpack-dans-une-application-symfony

Site officiel de Webpack : https://webpack.js.org/

Déployer une app Symfony dans un PaaS (Tristan Darricau)

Le déploiement d'une application PHP sur un hébergement cloud de type PaaS est différent d'un déploiement sur un serveur traditionnel. Cette conférence est un retour d'expérience sur le sujet, sans s'attarder sur un PaaS particulier.

L'auteur commence par rappeler les différents types d'hébergement cloud :

  • IaaS (infrastructure as a service) : bas niveau, hébergement de VM
  • CaaS (container as a service) : hébergement de conteneurs de type Docker et liaison entre eux (orchestration)
  • PaaS (platform as a service) : hébergement d'applications à l'aide de briques logicielles pré-configurées pour différentes technologies : PHP, nodejs, python, redis, mysql, mongodb... Comparable aux hébergements mutualisés mais plus puissants.
  • SaaS (software as a service) : application utilisable directement en ligne comme gmail par exemple

Le PaaS permet de gérer l'infrastructure comme du code, c'est-à-dire concrètement qu'on paramètre les différents services dont on a besoin avec un fichier de configuration propre à chaque hébergeur. Ensuite, le déploiement est lancé par un simple git push, qui, grâce à un hook côté hébergeur, exécutera un script pour déployer l'application. Fini donc les déploiements basés sur FTP, rsync, ansible, chef...

Mais un push du code est loin d'être suffisant, car il faut aussi installer les dépendances composer, (re)générer les caches, installer les assets... C'est le "build" et ce processus est également décrit dans la configuration. Les PaaS interdisent généralement d'écrire sur le disque pour des raisons de sécurité, mais également de scalabilité (selon les 12 principes https://12factor.net). Les nombreux caches fichier (classmap, etc.) de Symfony doivent donc être générés de manière statique lors de la phase de build, Symfony 3.2 apporte des améliorations à ce niveau. De même, les sessions PHP doivent être stockées dans un service de cache tel que memcache ou redis.

Concernant la base de données, les migrations SQL sont problématiques, surtout si on veut réduire le downtime au maximum. On peut passer par Doctrine ou pas un outil tiers, mais dans tous les cas il n'y a pas de solution miracle.

Slides : https://speakerdeck.com/nicofuma/symfony-live-paris-2017

Tout ce qu'un dev devrait savoir à propos d'Unicode (Nicolas Grekas)

Nicolas Grekas nous plonge tête la première dans le monde impitoyable des codages de caractère... Les développeurs qui pensent que le simple fait de traiter ses chaînes en UTF-8 résoud tous les problèmes en seront pour leurs frais !

L'auteur commence par un petit rappel historique :

  • Au début les processeurs avaient des registres de 7 bits, ce qui explique que le premier codage de caractères inventé, l'ASCII, est limité à 128 (2^7) caractères, anglophones naturellement. On y retrouve les caractères alphabétiques bien sûr mais aussi les caractères spéciaux comme les fameux CR/LF (retour chariot / nouvelle ligne).
  • L'apparition des processeurs 8 bits permet de compléter le jeu de caractères avec d'autres langues, mais pas assez pour les contenir toutes, donc de multiples standards apparaissent, bien sûr incompatibles entre eux (iso-8859-xx, windows-1152... ).

Pour pallier à cela, l'ISO a créé le standard Unicode, pouvant représenter 128 237 caractères de toutes les langues. Il existe plusieurs implémentations de ce standard :

  • UTF-8 : chaque caractère est représenté par 1, 2, 3 ou 4 octets --> c'est le grand gagnant ! Cette représentation dynamique implique donc la présence de bits de contrôle dans les caractères pour pouvoir se repérer dans une séquence. A noter que l'UTF-8 est rétro-compatible avec l'ASCII, qui est contenu dans ses 128 premiers caractères.
  • UTF-16 : un caractère est codé sur 2 ou 4 octets.
  • UTF-32 : un caractère est codé sur 4 octets.

Chaque caractère UTF-8 a son numéro, son nom et ses propriétés (catégorie, script, etc.).

Malgré tout, un certain nombre de problématiques demeurrent pour effectuer des opérations sur les chaînes; pour en citer quelques-unes :

  • Le folding : comparaison insensible à la casse. Certaines lettres comme le sigma en grec possède 2 symboles minuscules possibles, d'autres lettres majuscules s'écrivent en 2 lettre en minuscules...
  • L'égalité de chaîne suivant qu'elle est codée de manière composée (NFC) ou décomposée (NFD). Pour comprendre la différence, un caractère accentué peut être stocké comme un seul caractère (composé), mais aussi décomposé en le caractère non accentué d'une part et l'accent d'autre part !
  • Collation : comment effectuer un classement alphabétique quand toutes les langues n'ont pas les mêmes règles... Exemple : le lituanien place y entre i et k, "ch" en espagnol est considéré comme une lettre unique, etc.
  • Graphème (plus petite entité d'un système d'écriture). On pourrait penser qu'il s'agit des lettres, mais non, un graphème peut être constitué de plusieurs lettres, exemple : "au", "ch", "eau". On voit ici que c'est en relation étroite avec la phonétique. La problématique des "graphèmes clusters" réside dans le comptage des caractères, selon qu'ils sont codés en composé (NFC) ou décomposé (NFD)...

En pratique, IBM a créé une implémentation de référence, nommée ICU, utilisée en Java et en C/C++. En PHP, nous avons différentes possibilités... :

  • iconv_* : ces fonctions permettent de manipuler des chaînes en UTF-8.
  • mbstring_* : équivalent d'iconv + manipulation de la casse (mb_strtolower, etc.)
  • PCRE : permet de manipuler les chaînes UTF-8 avec le modificateur /u et aussi d'accéder aux propriétés Unicode.
  • grapheme_* : permet de manipuler les chaînes par graphème cluster !
  • Normalizer : permet de normaliser les chaînes en NFC/NFD pour pouvoir tester l'égalité.
  • Classes Intl* : ICU pour PHP
  • Nicolas Grekas a développé un composant universel pour traiter tous les cas de figure et a proposé de l'inclure dans Symfony.

Quant au compagnon traditionnel de nos applications PHP, à savoir mysql, il est important de noter les subtiles différences entre les normes UTF-8. La commande classique set names utf8 par exemple (utilisée à l'initialisation de la connexion pour préciser le codage de caractère utilisé lors des échanges client/serveur) est en fait un codage limité à 3 octets ! Pour utiliser toute l'étendue d'UTF-8, il convient d'utiliser plutôt set names utf8mb4. Pour les opérations sur les chaînes, il est important de savoir que mysql supporte différents types de collation : utf8_binary_ci (un caractère = un octet), utf8_general_ci (par défaut), utf8_unicode_ci (implémente certaines règles du style œ = oe), il faut donc bien choisir en connaissance de cause.

Slides : https://speakerdeck.com/nicolasgrekas/tout-ce-quun-dev-devrait-savoir-a-propos-dunicode

Découvrez les technologies d'alter way