<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Flex on Guillaume Delré</title><link>https://guillaumedelre.github.io/fr/tags/flex/</link><description>Recent content in Flex on Guillaume Delré</description><generator>Hugo</generator><language>fr-FR</language><lastBuildDate>Sun, 14 Jan 2018 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/fr/tags/flex/index.xml" rel="self" type="application/rss+xml"/><item><title>Symfony 4.0 : Flex et la fin de la Standard Edition</title><link>https://guillaumedelre.github.io/fr/2018/01/14/symfony-4.0-flex-et-la-fin-de-la-standard-edition/</link><pubDate>Sun, 14 Jan 2018 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2018/01/14/symfony-4.0-flex-et-la-fin-de-la-standard-edition/</guid><description>Part 3 of 11 in &amp;quot;Sorties Symfony&amp;quot;: Symfony 4.0 enterre la Standard Edition et introduit Flex : un microframework qui grandit exactement là où on en a besoin.</description><category>symfony-releases</category><content:encoded><![CDATA[<p>Symfony 4.0 est sorti le 30 novembre 2017, le même jour que la 3.4. La date de sortie commune est à peu près la seule chose qu&rsquo;ils ont en commun.</p>
<p>4.0, c&rsquo;est une philosophie différente. La Symfony Standard Edition, ce point de départ monolithique qui embarquait tout et vous laissait retirer ce dont vous n&rsquo;aviez pas besoin, a disparu. À sa place : un microframework qui grandit.</p>
<h2 id="flex">Flex</h2>
<p>Symfony Flex est un plugin Composer qui change la façon dont on installe les packages Symfony. Avant Flex, ajouter un bundle impliquait : l&rsquo;installer via Composer, l&rsquo;enregistrer dans <code>AppKernel.php</code>, ajouter la config dans <code>config/</code>, mettre à jour le routing si nécessaire. Quatre étapes, toutes manuelles.</p>
<p>Avec Flex, installer un package exécute une &ldquo;recette&rdquo; : un ensemble d&rsquo;étapes automatisées qui enregistre le bundle, génère un squelette de config et câble le routing. Installer Doctrine :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>composer require symfony/orm-pack
</span></span></code></pre></div><p>Cette commande installe les packages, crée <code>config/packages/doctrine.yaml</code>, ajoute les stubs de variables d&rsquo;environnement dans <code>.env</code>, et enregistre tout. Une commande, zéro étape manuelle.</p>
<p>Les recettes sont contribuées par la communauté et hébergées sur un serveur central. La qualité varie, mais pour les packages majeurs elles sont maintenues en parallèle des packages eux-mêmes.</p>
<h2 id="la-nouvelle-structure-de-projet">La nouvelle structure de projet</h2>
<p>Le layout de la Standard Edition (<code>app/</code>, <code>src/</code>, <code>web/</code>) est remplacé par une structure plus légère. La config se trouve dans <code>config/</code>, découpée par environnement. Le répertoire public s&rsquo;appelle désormais <code>public/</code>, plus <code>web/</code>. Le kernel est plus petit. Les controllers sont des classes ordinaires, plus besoin d&rsquo;<code>extends Controller</code>.</p>
<p>Plus important encore, le <code>services.yaml</code> par défaut utilise les conventions d&rsquo;autowiring de la 3.3 qui rendent la configuration explicite des services largement inutile. Les nouveaux projets démarrent minimaux et grossissent en ajoutant ce dont ils ont réellement besoin.</p>
<h2 id="services-privés-par-défaut">Services privés par défaut</h2>
<p>La plus grosse rupture de compatibilité de la 4.0 pour les apps existantes : tous les services sont privés par défaut. On ne peut plus récupérer un service directement depuis le container, il doit être injecté. C&rsquo;est le bon choix du point de vue de l&rsquo;injection de dépendances, mais ça casse tout ce qui utilisait <code>$this-&gt;get('service_id')</code> dans les controllers.</p>
<p>Le chemin de migration, c&rsquo;est <code>AbstractController</code>, qui fournit les mêmes méthodes pratiques via des service locators lazy plutôt qu&rsquo;un accès direct au container.</p>
<h2 id="ce-qui-a-été-supprimé">Ce qui a été supprimé</h2>
<p>4.0 est propre parce qu&rsquo;il supprime tout ce qui était déprécié en 3.4 :</p>
<ul>
<li>Les anciens événements de formulaire, les anciennes interfaces de sécurité, les anciens formats de configuration</li>
<li>Le support de PHP &lt; 7.1.3</li>
<li>Le composant ClassLoader</li>
<li>Le support ACL dans le SecurityBundle</li>
</ul>
<p>Les suppressions sont musclées. Les apps qui ont sauté la correction de leurs dépréciations 3.4 vont souffrir. Celles qui ont fait le ménage avant ont une migration tranquille.</p>
<p>Symfony 4.0, c&rsquo;est le reset dont le framework avait besoin. La Standard Edition avait accumulé des années de &ldquo;c&rsquo;est comme ça qu&rsquo;on fait&rdquo; que Flex balaie d&rsquo;un coup.</p>
<h2 id="des-variables-denvironnement-qui-connaissent-leur-type">Des variables d&rsquo;environnement qui connaissent leur type</h2>
<p>Avant 3.4 et 4.0, les variables d&rsquo;environnement étaient des chaînes. Toujours. Essayer d&rsquo;injecter <code>DATABASE_PORT</code> dans un paramètre de type <code>int</code> plantait silencieusement ou explosait avec une erreur de type. Le correctif était laid : caster en PHP ou éviter les paramètres typés.</p>
<p>4.0 embarque des processeurs de variables d&rsquo;environnement qui gèrent la conversion au niveau du container :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">parameters</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">app.connection.port</span>: <span style="color:#e6db74">&#39;%env(int:DATABASE_PORT)%&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">app.debug_mode</span>: <span style="color:#e6db74">&#39;%env(bool:APP_DEBUG)%&#39;</span>
</span></span></code></pre></div><p>Au-delà du casting, les processeurs peuvent décoder du base64, charger depuis des fichiers, parser du JSON, ou résoudre des paramètres du container dans une valeur. La combinaison <code>json:file:</code> est devenue un pattern propre pour charger des secrets depuis des fichiers montés dans des déploiements conteneurisés :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">parameters</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">env(SECRETS_FILE)</span>: <span style="color:#e6db74">&#39;/run/secrets/app.json&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">app.secrets</span>: <span style="color:#e6db74">&#39;%env(json:file:SECRETS_FILE)%&#39;</span>
</span></span></code></pre></div><p>Vous pouvez aussi écrire des processeurs personnalisés en implémentant <code>EnvVarProcessorInterface</code> et en taguant le service. Ça ressemble à de la sur-ingénierie jusqu&rsquo;au jour où vous en avez besoin.</p>
<h2 id="des-services-taggués-sans-boilerplate">Des services taggués sans boilerplate</h2>
<p>Avant 4.0, rassembler tous les services portant un tag donné dans un service signifiait écrire un compiler pass. Quarante lignes de PHP pour dire &ldquo;donne-moi tout ce qui est tagué <code>app.handler</code>.&rdquo;</p>
<p>3.4 a introduit le raccourci YAML <code>!tagged</code>, et 4.0 l&rsquo;emporte avec lui :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">App\HandlerCollection</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">arguments</span>: [!<span style="color:#ae81ff">tagged app.handler]</span>
</span></span></code></pre></div><p>La collection est lazy par défaut quand elle est type-hintée en <code>iterable</code>, donc les services ne sont pas instanciés tant qu&rsquo;on n&rsquo;itère pas dessus. Ça a remplacé toute une catégorie de compiler passes qui n&rsquo;existaient que pour construire des listes.</p>
<h2 id="php-comme-format-de-configuration">PHP comme format de configuration</h2>
<p>YAML est la valeur par défaut depuis si longtemps que ça semble obligatoire. Ce n&rsquo;est pas le cas. 4.0 embarque une configuration en PHP via une interface fluent :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#75715e">// config/services.php
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">return</span> <span style="color:#66d9ef">function</span> (<span style="color:#a6e22e">ContainerConfigurator</span> $container) {
</span></span><span style="display:flex;"><span>    $services <span style="color:#f92672">=</span> $container<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">services</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">defaults</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">autowire</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">autoconfigure</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    $services<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">load</span>(<span style="color:#e6db74">&#39;App\\&#39;</span>, <span style="color:#e6db74">&#39;../src/&#39;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">exclude</span>(<span style="color:#e6db74">&#39;../src/{Entity,Repository}&#39;</span>);
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>La même approche fonctionne pour les routes. L&rsquo;avantage pratique : autocomplétion de l&rsquo;IDE, vérification des types, et vraie logique PHP dans la configuration sans la syntaxe d&rsquo;interpolation <code>%</code>. YAML n&rsquo;est pas près de disparaître, mais maintenant vous avez le choix.</p>
<h2 id="argon2i-parce-que-bcrypt-vieillissait-déjà">Argon2i, parce que bcrypt vieillissait déjà</h2>
<p>Symfony 3.4/4.0 a ajouté le support d&rsquo;Argon2i, vainqueur du Password Hashing Competition 2015. La configuration tient en une ligne :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">security</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">encoders</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">App\Entity\User</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">algorithm</span>: <span style="color:#ae81ff">argon2i</span>
</span></span></code></pre></div><p>Argon2i est intégré à PHP 7.2+ et disponible via l&rsquo;extension sodium sur les versions antérieures. Comme bcrypt, il se sale lui-même, inutile de gérer des colonnes de sel. Contrairement à bcrypt, il est conçu pour résister aux attaques par GPU avec une utilisation mémoire configurable. Si vous démarrez un nouveau projet sur 4.0, il n&rsquo;y a vraiment aucune raison de choisir bcrypt.</p>
<h2 id="la-couche-formulaire-reçoit-un-thème-bootstrap-4">La couche formulaire reçoit un thème Bootstrap 4</h2>
<p>Le thème de formulaire Bootstrap 3 existant remonte à Symfony 2.x. Bootstrap 4 arrive comme option de premier ordre en 4.0 :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">twig</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">form_themes</span>: [<span style="color:#e6db74">&#39;bootstrap_4_layout.html.twig&#39;</span>]
</span></span></code></pre></div><p>Plus utile en pratique : les types d&rsquo;input HTML5 <code>tel</code> et <code>color</code> sont désormais disponibles comme types de formulaire <code>TelType</code> et <code>ColorType</code>. Avant, il fallait écrire des types personnalisés ou surcharger des widgets bruts pour ça.</p>
<h2 id="binding-de-service-local">Binding de service local</h2>
<p>Les bindings <code>_defaults</code> globaux s&rsquo;appliquent à tous les services. Parfois on a besoin d&rsquo;un binding limité à une classe ou un namespace spécifique, comme des instances de logger différentes pour des sous-systèmes différents.</p>
<p>4.0 supporte <code>bind</code> par service exactement pour ça :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">App\Service\OrderService</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">bind</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">Psr\Log\LoggerInterface</span>: <span style="color:#e6db74">&#39;@monolog.logger.orders&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">App\Service\PaymentService</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">bind</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">Psr\Log\LoggerInterface</span>: <span style="color:#e6db74">&#39;@monolog.logger.payments&#39;</span>
</span></span></code></pre></div><p>Même interface, deux implémentations différentes, pas de factory, pas de configuration supplémentaire. Petite fonctionnalité, mais elle élimine toute une catégorie de bidouilles bancales.</p>
]]></content:encoded></item></channel></rss>