<?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>Migration on Guillaume Delré</title><link>https://guillaumedelre.github.io/fr/tags/migration/</link><description>Recent content in Migration on Guillaume Delré</description><generator>Hugo</generator><language>fr-FR</language><lastBuildDate>Fri, 12 Jan 2018 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/fr/tags/migration/index.xml" rel="self" type="application/rss+xml"/><item><title>Symfony 3.4 LTS : le pont qu'on a vraiment envie de traverser</title><link>https://guillaumedelre.github.io/fr/2018/01/12/symfony-3.4-lts-le-pont-quon-a-vraiment-envie-de-traverser/</link><pubDate>Fri, 12 Jan 2018 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2018/01/12/symfony-3.4-lts-le-pont-quon-a-vraiment-envie-de-traverser/</guid><description>Part 2 of 11 in &amp;quot;Sorties Symfony&amp;quot;: Symfony 3.4 LTS est le pont de migration : mêmes fonctionnalités que 3.3 plus chaque avertissement de dépréciation que 4.0 va rendre obligatoire.</description><category>symfony-releases</category><content:encoded><![CDATA[<p>Symfony 3.4 et 4.0 sont sortis le même jour : le 30 novembre 2017. Ce n&rsquo;est pas une coïncidence, c&rsquo;est la stratégie.</p>
<p>3.4 n&rsquo;est pas une version de fonctionnalités. Elle livre exactement les mêmes fonctionnalités que 3.3, plus chaque avertissement de dépréciation que 4.0 va rendre obligatoire. Son seul objectif est d&rsquo;être l&rsquo;outil de migration : monter de 3.3 à 3.4, corriger ce qui apparaît dans les logs, puis passer à 4.0 proprement.</p>
<h2 id="pourquoi-les-versions-lts-comptent-dans-le-modèle-symfony">Pourquoi les versions LTS comptent dans le modèle Symfony</h2>
<p>Symfony publie une nouvelle version mineure tous les six mois. Ce rythme serait brutal pour les applications en production, donc le projet désigne chaque quatrième mineure comme LTS : trois ans de corrections de bugs, quatre de correctifs de sécurité. Ce qui signifie que les équipes peuvent cibler 3.4 et arrêter de penser aux mises à jour pendant un moment.</p>
<p>3.4 est la dernière LTS de la ligne 3.x. Si on est encore sur 2.x ou un 3.x ancien, c&rsquo;est la zone d&rsquo;atterrissage.</p>
<h2 id="la-couche-de-dépréciations">La couche de dépréciations</h2>
<p>Chaque fonctionnalité supprimée par 4.0 est dépréciée dans 3.4. Faire tourner son application sur 3.4 avec les notices de dépréciation activées transforme les logs en une liste de tâches. Les plus courantes :</p>
<ul>
<li>Les services sans visibilité explicite (public/private) génèrent des warnings — 4.0 rend tous les services privés par défaut</li>
<li><code>ControllerTrait</code> est déprécié au profit de <code>AbstractController</code></li>
<li>Les anciennes interfaces d&rsquo;authentificateur de sécurité sont marquées pour suppression</li>
<li>La configuration de services YAML seule sans annotations d&rsquo;autowiring déclenche des warnings</li>
</ul>
<p>Le workflow prévu : monter sur 3.4, faire tourner la suite de tests avec les notices de dépréciation comme erreurs (<code>SYMFONY_DEPRECATIONS_HELPER=max[self]=0</code> dans PHPUnit), corriger tout ce qui échoue. Après ça, la montée vers 4.0 est essentiellement mécanique.</p>
<h2 id="la-fenêtre-de-support">La fenêtre de support</h2>
<p>3.4 LTS reçoit des corrections de bugs jusqu&rsquo;en novembre 2020 et des correctifs de sécurité jusqu&rsquo;en novembre 2021. C&rsquo;est une marge confortable pour les applications qui ne peuvent pas suivre chaque version. Le coût : rester sur l&rsquo;architecture 3.x, sans Flex, sans structure micro-framework, sans autowiring zéro-config par défaut.</p>
<p>Le pont est là. Savoir si et quand on le traverse est une décision business, pas technique.</p>
<h2 id="les-services-passent-privés">Les services passent privés</h2>
<p>3.4 a inversé la visibilité par défaut des services de public à privé. Avant, <code>$container-&gt;get('app.my_service')</code> était du code parfaitement normal. Après, c&rsquo;est un anti-pattern qui génère un warning de dépréciation dans 3.4 et casse complètement dans 4.0.</p>
<p>La raison est simple : récupérer des services directement depuis le conteneur masque les dépendances et déjoue l&rsquo;analyse statique. En injectant via le constructeur, le conteneur peut optimiser le graphe, supprimer les services inutilisés, et détecter les erreurs à la compilation. En les récupérant à l&rsquo;exécution, il ne peut pas.</p>
<p>Pour les applications qui utilisent déjà l&rsquo;autowiring, la migration est généralement légère. Le point délicat ce sont les contrôleurs qui étendent <code>Controller</code> et appellent <code>$this-&gt;get('quelque-chose')</code>. La correction consiste à passer à <code>AbstractController</code>, qui fournit les mêmes raccourcis mais via des service locators paresseux plutôt que l&rsquo;accès direct au conteneur.</p>
<p>Pour les services qui ont vraiment besoin d&rsquo;être publics (accédés depuis du code legacy ou des tests fonctionnels), les marquer explicitement :</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\LegacyAdapter</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">public</span>: <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><h2 id="lier-les-arguments-scalaires-une-seule-fois">Lier les arguments scalaires une seule fois</h2>
<p>Un point de friction classique avec l&rsquo;autowiring : les arguments de constructeur scalaires. Si dix services ont tous besoin de <code>$projectDir</code>, il fallait configurer chacun individuellement. La clé <code>bind</code> sous <code>_defaults</code> règle ç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">_defaults</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">autowire</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">autoconfigure</span>: <span style="color:#66d9ef">true</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">$projectDir</span>: <span style="color:#e6db74">&#39;%kernel.project_dir%&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">$mailerDsn</span>: <span style="color:#e6db74">&#39;%env(MAILER_DSN)%&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">Psr\Log\LoggerInterface $auditLogger</span>: <span style="color:#e6db74">&#39;@monolog.logger.audit&#39;</span>
</span></span></code></pre></div><p>Tout service avec un paramètre de constructeur nommé <code>$projectDir</code> reçoit la valeur liée automatiquement. On peut aussi lier par type-hint, ce qui gère le cas courant où plusieurs canaux de logger existent et on en a besoin d&rsquo;un spécifique. Les liaisons dans <code>_defaults</code> s&rsquo;appliquent à tous les services du fichier ; on peut surcharger par service si nécessaire.</p>
<h2 id="injecter-les-services-taggués-sans-compiler-pass">Injecter les services taggués sans compiler pass</h2>
<p>Avant 3.4, collecter tous les services avec un tag donné nécessitait d&rsquo;écrire un compiler pass. Il y a maintenant un raccourci YAML :</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\Chain\TransformerChain</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">arguments</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">$transformers</span>: !<span style="color:#ae81ff">tagged app.transformer</span>
</span></span></code></pre></div><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:#66d9ef">class</span> <span style="color:#a6e22e">TransformerChain</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(<span style="color:#66d9ef">private</span> <span style="color:#a6e22e">iterable</span> $transformers) {}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>La notation <code>!tagged</code> crée un <code>IteratorArgument</code> : les services sont instanciés paresseusement au fil de l&rsquo;itération, donc les transformers non utilisés ne sont jamais construits. Pour l&rsquo;ordonnancement, ajouter un attribut <code>priority</code> à la définition du tag sur chaque service.</p>
<h2 id="un-logger-livré-avec-le-framework">Un logger livré avec le framework</h2>
<p>Pas de Monolog ? Pas de problème. Symfony 3.4 inclut un logger PSR-3 qui écrit sur <code>php://stderr</code> par défaut. On l&rsquo;injecte avec <code>Psr\Log\LoggerInterface</code> :</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:#66d9ef">use</span> <span style="color:#a6e22e">Psr\Log\LoggerInterface</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyService</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">__construct</span>(<span style="color:#66d9ef">private</span> <span style="color:#a6e22e">LoggerInterface</span> $logger) {}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">doSomething</span>()<span style="color:#f92672">:</span> <span style="color:#a6e22e">void</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        $this<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">logger</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">warning</span>(<span style="color:#e6db74">&#39;Quelque chose de douteux s\&#39;est produit&#39;</span>, [<span style="color:#e6db74">&#39;context&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;ici&#39;</span>]);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Le niveau minimum par défaut est <code>warning</code>. La cible est les workloads container et Kubernetes où stderr est le puits de logs naturel. C&rsquo;est délibérément minimal : pas de handlers, pas de processors, pas de channels. Quand on en a besoin, on installe Monolog.</p>
<h2 id="les-guard-authenticators-ont-reçu-une-méthode-supports">Les Guard authenticators ont reçu une méthode supports()</h2>
<p>La méthode <code>getCredentials()</code> du composant Guard jouait un double rôle : décider si l&rsquo;authentificateur devait gérer la requête, et extraire les credentials. Retourner <code>null</code> était le signal pour passer. Ça rendait le contrat confus.</p>
<p>3.4 a ajouté <code>supports()</code> pour séparer ces responsabilité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-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ApiTokenAuthenticator</span> <span style="color:#66d9ef">extends</span> <span style="color:#a6e22e">AbstractGuardAuthenticator</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">supports</span>(<span style="color:#a6e22e">Request</span> $request)<span style="color:#f92672">:</span> <span style="color:#a6e22e">bool</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> $request<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">headers</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">has</span>(<span style="color:#e6db74">&#39;X-API-TOKEN&#39;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">getCredentials</span>(<span style="color:#a6e22e">Request</span> $request)<span style="color:#f92672">:</span> <span style="color:#66d9ef">array</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// N&#39;est appelé que quand supports() retourne true.
</span></span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Doit toujours retourner des credentials maintenant.
</span></span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> [<span style="color:#e6db74">&#39;token&#39;</span> <span style="color:#f92672">=&gt;</span> $request<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">headers</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">get</span>(<span style="color:#e6db74">&#39;X-API-TOKEN&#39;</span>)];
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>L&rsquo;ancienne <code>GuardAuthenticatorInterface</code> est dépréciée. L&rsquo;avantage pratique : les classes de base peuvent implémenter la logique partagée <code>getUser()</code> et <code>checkCredentials()</code>, tandis que les sous-classes ne surchargent que <code>supports()</code> et <code>getCredentials()</code>. Une responsabilité chacune.</p>
<h2 id="deux-nouvelles-commandes-de-debug">Deux nouvelles commandes de debug</h2>
<p><code>debug:autowiring</code> remplace l&rsquo;ancien <code>debug:container --types</code> pour découvrir quels type-hints fonctionnent avec l&rsquo;autowiring :</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>$ bin/console debug:autowiring log
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Autowirable Services
</span></span><span style="display:flex;"><span><span style="color:#f92672">====================</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  Psr<span style="color:#ae81ff">\L</span>og<span style="color:#ae81ff">\L</span>oggerInterface
</span></span><span style="display:flex;"><span>      alias to monolog.logger
</span></span><span style="display:flex;"><span>  Psr<span style="color:#ae81ff">\L</span>og<span style="color:#ae81ff">\L</span>oggerInterface $auditLogger
</span></span><span style="display:flex;"><span>      alias to monolog.logger.audit
</span></span></code></pre></div><p>Passer un mot-clé pour filtrer. Fini de deviner si c&rsquo;est <code>LoggerInterface</code> ou <code>Logger</code>.</p>
<p><code>debug:form</code> donne la même capacité d&rsquo;introspection pour les types de formulaires :</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>$ bin/console debug:form App<span style="color:#ae81ff">\F</span>orm<span style="color:#ae81ff">\O</span>rderType label_attr
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Option: label_attr
</span></span><span style="display:flex;"><span>  Required: false
</span></span><span style="display:flex;"><span>  Default: <span style="color:#f92672">[]</span>
</span></span><span style="display:flex;"><span>  Allowed types: array
</span></span></code></pre></div><p>Sans arguments, il liste tous les types de formulaires enregistrés, extensions et guessers. Avec un nom de type et un nom d&rsquo;option, il montre toutes les contraintes sur cette option. Avant ça, on lisait le source ou on tâtonnait.</p>
<h2 id="les-sessions-sont-devenues-plus-strictes-par-défaut">Les sessions sont devenues plus strictes par défaut</h2>
<p>3.4 implémente <code>SessionUpdateTimestampHandlerInterface</code> de PHP 7.0, ce qui apporte deux choses : les écritures de session paresseuses (écrites seulement quand les données ont vraiment changé) et la validation stricte des ID de session (les IDs qui n&rsquo;existent pas dans le store sont rejetés plutôt que créés silencieusement, ce qui bloque une classe d&rsquo;attaques de fixation de session).</p>
<p>Les anciennes classes <code>WriteCheckSessionHandler</code>, <code>NativeSessionHandler</code> et <code>NativeProxy</code> sont dépréciées. Le <code>MemcacheSessionHandler</code> (note : pas Memcached) est supprimé, puisque l&rsquo;extension PECL sous-jacente a arrêté de recevoir des mises à jour pour PHP 7.</p>
<h2 id="les-thèmes-de-formulaires-twig-peuvent-maintenant-être-scopés">Les thèmes de formulaires Twig peuvent maintenant être scopés</h2>
<p>Les thèmes de formulaires globaux s&rsquo;appliquent à tous les formulaires dans l&rsquo;application. Si un formulaire a besoin d&rsquo;un look complètement différent, il n&rsquo;y avait pas de moyen propre de se désinscrire. Le mot-clé <code>only</code> gère ç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-twig" data-lang="twig"><span style="display:flex;"><span><span style="color:#75715e">{%</span> <span style="color:#66d9ef">raw</span> <span style="color:#75715e">%}</span>{% form_theme orderForm with [&#39;form/order_layout.html.twig&#39;] only %}<span style="color:#75715e">{%</span> <span style="color:#66d9ef">endraw</span> <span style="color:#75715e">%}</span>
</span></span></code></pre></div><p>Le mot-clé <code>only</code> désactive tous les thèmes globaux pour ce formulaire, y compris le <code>form_div_layout.html.twig</code> de base. Le thème personnalisé doit alors soit fournir tous les blocs qu&rsquo;il utilise, soit les importer explicitement avec <code>{% raw %}{% use 'form_div_layout.html.twig' %}{% endraw %}</code>.</p>
<h2 id="surcharger-les-templates-de-bundle-sans-boucles-infinies">Surcharger les templates de bundle sans boucles infinies</h2>
<p>Surcharger un template de bundle qu&rsquo;on avait aussi besoin d&rsquo;étendre causait autrefois une erreur de référence circulaire. Surcharger <code>@TwigBundle/Exception/error404.html.twig</code> et essayer aussi d&rsquo;en hériter ? L&rsquo;ancienne résolution de namespace suivait la surcharge et bouclait indéfiniment.</p>
<p>3.4 a introduit le préfixe <code>@!</code> pour référencer explicitement le template de bundle original, en contournant toutes les surcharges :</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-twig" data-lang="twig"><span style="display:flex;"><span><span style="color:#75715e">{%</span> <span style="color:#66d9ef">raw</span> <span style="color:#75715e">%}</span>{# templates/bundles/TwigBundle/Exception/error404.html.twig #}
</span></span><span style="display:flex;"><span>{% extends &#39;@!Twig/Exception/error404.html.twig&#39; %}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{% block title %}Page non trouvée{% endblock %}<span style="color:#75715e">{%</span> <span style="color:#66d9ef">endraw</span> <span style="color:#75715e">%}</span>
</span></span></code></pre></div><p><code>@TwigBundle</code> résout vers la surcharge si elle existe. <code>@!TwigBundle</code> résout toujours vers l&rsquo;original. Surcharger-et-étendre, sans les acrobaties.</p>
]]></content:encoded></item></channel></rss>