<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Frédéric Bouchery</title>
    <description>The latest articles on Forem by Frédéric Bouchery (@fredbouchery).</description>
    <link>https://forem.com/fredbouchery</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F129037%2Fd5c9d100-3ff6-4ecd-b02d-3d34e516ffee.jpg</url>
      <title>Forem: Frédéric Bouchery</title>
      <link>https://forem.com/fredbouchery</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/fredbouchery"/>
    <language>en</language>
    <item>
      <title>Chat : Test unitaire sur des méthodes privées</title>
      <dc:creator>Frédéric Bouchery</dc:creator>
      <pubDate>Thu, 26 Oct 2023 15:11:48 +0000</pubDate>
      <link>https://forem.com/fredbouchery/chat-test-unitaire-sur-des-methodes-privees-1a63</link>
      <guid>https://forem.com/fredbouchery/chat-test-unitaire-sur-des-methodes-privees-1a63</guid>
      <description>&lt;p&gt;(English translation: &lt;a href="https://dev.to/fredbouchery/chat-unit-test-of-private-methods-1ig2"&gt;Chat: Unit test of private methods&lt;/a&gt;)&lt;br&gt;
Je vous livre ici, l'échange que j'ai eu avec ChatGPT (version 4) au sujet des tests unitaires de méthodes privées, car je trouvais l'exercice intéressant.&lt;br&gt;
Les mises en gras sont de mon fait à posteriori pour mettre un peu de relief.&lt;/p&gt;

&lt;p&gt;Une phrase que je retiens et que j'aime beaucoup :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Le but des tests unitaires est de s'assurer que chaque partie de votre code &lt;strong&gt;fonctionne comme prévu&lt;/strong&gt;, et non de s'assurer que chaque méthode passe un test.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpj0bawoym16686f7vyip.png" alt="Humain débattant avec un robot"&gt;
&lt;/h2&gt;

&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Je souhaite mettre en place des tests unitaires avec PHPUnit. En PHP, que penses-tu des tests de &lt;strong&gt;méthodes privées&lt;/strong&gt; ?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Il est généralement recommandé de ne &lt;strong&gt;pas&lt;/strong&gt; tester directement les méthodes privées dans PHPUnit. Les méthodes privées sont considérées comme des détails d'implémentation de la classe et ne devraient être testées qu'&lt;strong&gt;indirectement&lt;/strong&gt; par le biais des méthodes publiques qui les utilisent. &lt;br&gt;
Si vous ressentez le besoin de tester une méthode privée directement, cela pourrait indiquer un &lt;strong&gt;problème de conception&lt;/strong&gt;. Peut-être que la classe fait trop de choses et qu'elle pourrait être refactorisée pour déplacer cette fonctionnalité vers une nouvelle classe où elle serait publique et pourrait être testée directement.&lt;br&gt;
Cependant, si vous devez &lt;strong&gt;absolument&lt;/strong&gt; tester une méthode privée pour une raison ou une autre, vous pouvez utiliser la réflexion en PHP pour rendre une méthode privée accessible et ensuite la tester. Mais encore une fois, cela devrait être &lt;strong&gt;l’exception&lt;/strong&gt; plutôt que la règle dans la plupart des cas.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;J'ai une partie du code avec du traitement algorithmique que j'aimerais bien valider, mais je &lt;strong&gt;ne souhaite pas&lt;/strong&gt; qu'il soit accessible de l'extérieur. Si je plaçais ce code dans une classe à part, cela reviendrait à le rendre accessible, hors il n'a &lt;strong&gt;aucun&lt;/strong&gt; intérêt en dehors du contexte de ma classe. Quant-aux tests uniquement des méthodes publiques, cela me &lt;strong&gt;force à mocker beaucoup&lt;/strong&gt; de choses, et les tests deviennent &lt;strong&gt;complexes&lt;/strong&gt;. En testant  ma méthode privée, j'ai des tests très simples&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dans ce cas, si vous pensez que tester vos méthodes privées directement vous &lt;strong&gt;apporte de la valeur&lt;/strong&gt; et améliore la qualité de votre code, alors vous devriez le faire. &lt;br&gt;
En fin de compte, l'objectif des tests unitaires est de s'assurer que votre code &lt;strong&gt;fait ce qu'il est censé faire&lt;/strong&gt; et de vous aider à identifier et corriger les bugs. Si tester directement une méthode privée vous aide à atteindre ces objectifs, alors il n'y a pas de raison de ne pas le faire.&lt;br&gt;
Comme mentionné précédemment, vous pouvez utiliser la réflexion pour accéder aux méthodes privées et les tester. Voici un exemple de comment vous pouvez faire cela en PHP :&lt;/p&gt;


&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;YourClass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$reflector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReflectionClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$reflector&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'yourPrivateMethod'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setAccessible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;invokeArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$arg1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$arg2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;// maintenant vous pouvez faire des assertions sur $result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Cependant, cela peut rendre vos tests un peu plus complexes et plus difficiles à maintenir. Vous devriez également être conscient que c'est une sorte de "&lt;em&gt;hack&lt;/em&gt;" et qu'il y a une raison pour laquelle les méthodes privées ne sont pas normalement accessibles : il s'agit de maintenir l'encapsulation et de masquer &lt;strong&gt;les détails d'implémentation&lt;/strong&gt;, qui pourraient changer. En testant directement les méthodes privées, vous couplez vos tests à ces détails d'implémentation spécifiques et tout changement dans ces méthodes pourrait nécessiter une réécriture de vos tests.&lt;br&gt;
En fin de compte, c'est à vous de décider si les avantages l'emportent sur les inconvénients dans votre cas spécifique.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Une bonne solution serait d'avoir un concept de &lt;strong&gt;classe amie&lt;/strong&gt; en PHP, hors, cela n'existe pas&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Effectivement, PHP n'a pas de concept de "classe amie" comme le fait C++. Dans C++, une classe amie peut &lt;strong&gt;accéder aux méthodes privées et protégées&lt;/strong&gt; de la classe qui l'a déclarée comme amie, ce qui pourrait être utilisé pour tester ces méthodes.&lt;br&gt;
Sans cette fonctionnalité en PHP, vous devez trouver d'autres moyens de tester vos méthodes privées si vous décidez que c'est nécessaire. C'est une des raisons pour lesquelles certains optent pour la réflexion malgré ses inconvénients.&lt;br&gt;
Si vous avez un cas où vous avez des méthodes privées complexes que vous voulez tester directement, une autre option est de les rendre publiques juste pour des raisons de test et de documenter clairement qu'elles sont destinées à être utilisées à des fins de test uniquement. C'est loin d'être idéal, mais c'est une autre option à considérer.&lt;/p&gt;

&lt;p&gt;En fin de compte, il est important de se rappeler que le but des tests unitaires est de s'assurer que chaque partie de votre code &lt;strong&gt;fonctionne comme prévu&lt;/strong&gt;, et non de s'assurer que chaque méthode passe un test. Si vous pouvez tester le comportement de votre classe d'une manière qui a du sens sans avoir besoin de tester chaque méthode privée, cela pourrait être l'approche la plus efficace.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Je me demande si des méthodes &lt;strong&gt;protégées&lt;/strong&gt; n'auraient pas plus de sens&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Utiliser des méthodes protégées en PHP peut effectivement être une alternative pour des raisons de testabilité. Les méthodes protégées peuvent être appelées par la classe elle-même, ainsi que par toutes les classes qui héritent de celle-ci. &lt;br&gt;
Ainsi, en cas de complexité algorithmique qui ne doit pas être exposée en public tout en souhaitant pouvoir la tester unitairement, créer une sous-classe &lt;strong&gt;uniquement&lt;/strong&gt; pour les tests pour tester ces algorithmes spécifiques pourrait être une solution. &lt;br&gt;
Voici un exemple d'à quoi cela pourrait ressembler :&lt;/p&gt;


&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;methodToTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Votre algorithme complexe ici&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClassTestVersion&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;methodToTestWrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;methodToTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Et voici comment vous pourriez utiliser cela dans vos tests PHPUnit :&lt;/p&gt;


&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testMethodToTest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$testObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyClassTestVersion&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$testObject&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;methodToTestWrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'input'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Vos assertions ici&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;C'est un moyen d'accéder à la méthode sans l'exposer en tant que publique et sans utiliser la réflexion PHP. Cependant, cette solution a ses propres limites et inconvénients. L'un d'eux est que si les méthodes protégées sont &lt;strong&gt;trop nombreuses&lt;/strong&gt;, cela peut alourdir la classe test et la rendre plus difficile à gérer. De plus, si la logique des méthodes protégées change, les tests doivent être ajustés de la même manière. Et si une méthode privée est utilisée par plusieurs méthodes protégées, alors elle sera testée de manière répétée, ce qui rend les tests moins efficaces. L'équilibre approprié dépend donc de chaque cas particulier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Une méthode protégée ne devrait justement &lt;strong&gt;pas changer&lt;/strong&gt;, car c'est un &lt;strong&gt;contrat&lt;/strong&gt; avec les classes qui en héritent, et les tester, cela a beaucoup de sens en fait.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tout à fait, vous avez raison. Les méthodes protégées sont effectivement une &lt;strong&gt;partie du contrat de la classe&lt;/strong&gt; avec ses sous-classes, tout comme les méthodes publiques sont un contrat avec tout code externe qui utilise la classe.&lt;br&gt;
Tester les méthodes protégées a donc du sens, car cela permet de garantir que la classe respecte son contrat même lorsque celle-ci évolue. En toute rigueur, c'est même &lt;strong&gt;nécessaire&lt;/strong&gt; lorsque vous suivez une méthodologie de développement piloté par les tests (Test-Driven Development ou TDD), où chaque partie du code doit être couverte par des tests.&lt;br&gt;
C'est une bonne pratique de garder ce contrat aussi &lt;strong&gt;stable&lt;/strong&gt; que possible pour ne pas casser les sous-classes qui dépendent de ces méthodes. Si vous devez changer le comportement de ces méthodes, il est important de le faire de manière à ne pas casser ce contrat. C'est là que les tests unitaires des méthodes protégées peuvent être &lt;strong&gt;très utiles&lt;/strong&gt;, pour vérifier que vous n'avez pas introduit de régressions lors de la modification de ces méthodes.&lt;br&gt;
Il est donc justifié de tester vos méthodes protégées si cela correspond à votre logique d'organisation pour le code et à votre stratégie de test. Gardez cependant à l'esprit qu'il peut toujours être bénéfique de travailler au &lt;strong&gt;découplage et à la simplification de vos classes&lt;/strong&gt;, en évitant autant que possible un grand nombre de méthodes protégées, conformément aux principes du &lt;strong&gt;SOLID&lt;/strong&gt; et du développement orienté objet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sur les méthodes privées, un développeur peut facilement se dire : "&lt;em&gt;je peux changer cette méthode, voir la supprimer, c'est pas grave, elle est privée&lt;/em&gt;". Mais en faisant ça, il va &lt;strong&gt;casser&lt;/strong&gt; mes tests et peut-être que cela le conduira à constater certains cas d'usage qu'il n'avait pas identifié. Peut-être qu'une annotation sur ma méthode permettrait de &lt;strong&gt;prévenir&lt;/strong&gt; le développeur qu'il y a des tests associés, avant qu'il ne s'en rende compte plus tard. Une annotation "&lt;a class="mentioned-user" href="https://dev.to/see"&gt;@see&lt;/a&gt;" par exemple&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Votre suggestion d'utiliser une annotation "&lt;a class="mentioned-user" href="https://dev.to/see"&gt;@see&lt;/a&gt;" est une excellente idée pour signaler qu'une méthode privée a des tests associés. Cela peut aider à renforcer la communication au sein de l'équipe de développement et à &lt;strong&gt;faire comprendre&lt;/strong&gt; à quelqu'un qui modifie cette méthode qu'il doit aussi vérifier et potentiellement modifier les tests associés.&lt;br&gt;
Cela pourrait ressembler à ceci dans votre code:&lt;/p&gt;


&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cd"&gt;/**
 * @see \Tests\Unit\YourClassTest::testYourPrivateMethod
 */&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;yourPrivateMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Cependant, notez que cette annotation doit être &lt;strong&gt;entretenue&lt;/strong&gt; : si le test est renommé ou déplacé, l'annotation doit être mise à jour.&lt;br&gt;
Il est également important de noter qu'une &lt;strong&gt;bonne couverture de tests des méthodes publiques&lt;/strong&gt; devrait également attraper les cas où une modification d'une méthode privée engendre des erreurs.&lt;br&gt;
Dans tous les cas, la décision de tester ou non les méthodes privées doit être prise en fonction du contexte spécifique du projet, de l'équipe, et du code lui-même. L'idée est de tester ce qui est nécessaire pour avoir &lt;strong&gt;confiance&lt;/strong&gt; dans le fonctionnement correct de votre système, tout en &lt;strong&gt;équilibrant&lt;/strong&gt; cet effort avec le temps de développement et la maintenabilité du code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Oui, je suis d'accord, cela doit rester à la marge, et favoriser les tests de méthodes publiques. Je m'interrogeais sur cette pratique pour &lt;strong&gt;décomplexifier&lt;/strong&gt; les tests et éventuellement faciliter l'ajout de tests sur du &lt;strong&gt;code legacy mal conçu&lt;/strong&gt;. Je préfère des tests sur méthodes privées que &lt;strong&gt;pas de test du tout&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tout à fait, je suis d'accord avec vous et vous avez une très bonne réflexion sur ce sujet. &lt;br&gt;
Effectivement, lorsque vous travaillez sur du code legacy, vous n'avez &lt;strong&gt;pas toujours&lt;/strong&gt; la possibilité de refactoriser tout ce que vous voudriez. Il est donc crucial d'avoir des tests pour garantir le bon fonctionnement de votre application, même si cela implique de tester des méthodes privées. &lt;br&gt;
Et comme vous l'avez bien dit, dans certains cas le test d'une méthode privée peut être &lt;strong&gt;moins complexe et plus rapide à mettre en place&lt;/strong&gt; que le test de la méthode publique qui l'utilise, notamment si cela évite de devoir mocker de nombreux objets.&lt;br&gt;
En résumé, tester des méthodes privées peut avoir du sens dans &lt;strong&gt;certaines situations&lt;/strong&gt;, notamment dans le cadre de code legacy et quand cela permet de rendre les tests plus simples et plus fiables. C'est une pratique à utiliser avec &lt;strong&gt;parcimonie&lt;/strong&gt; et &lt;strong&gt;discernement&lt;/strong&gt;, en gardant en tête l'objectif principal des tests unitaires : &lt;strong&gt;s'assurer de la fiabilité et de la qualité de votre code&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>php</category>
      <category>phpunit</category>
      <category>unittest</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>Chat: Unit test of private methods</title>
      <dc:creator>Frédéric Bouchery</dc:creator>
      <pubDate>Thu, 26 Oct 2023 15:11:23 +0000</pubDate>
      <link>https://forem.com/fredbouchery/chat-unit-test-of-private-methods-1ig2</link>
      <guid>https://forem.com/fredbouchery/chat-unit-test-of-private-methods-1ig2</guid>
      <description>&lt;p&gt;(Traduction française: &lt;a href="https://dev.to/fredbouchery/chat-test-unitaire-sur-des-methodes-privees-1a63"&gt;Chat : Test unitaire sur des méthodes privées&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I present to you here the conversation I had with ChatGPT (version 4) regarding unit testing of private methods, as I found the exercise interesting.&lt;br&gt;
The text in bold was added by me afterward to add some emphasis.&lt;/p&gt;

&lt;p&gt;A sentence that I really like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The goal of unit tests is to make sure each part of your code works as expected, not to ensure each method passes a test&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpj0bawoym16686f7vyip.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpj0bawoym16686f7vyip.png" alt="Human debating with a robot"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I want to setup unit tests with PHPUnit. In PHP, what do you think about &lt;strong&gt;testing private methods&lt;/strong&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Typically, it is &lt;strong&gt;not recommended&lt;/strong&gt; to directly test private methods in PHPUnit. Private methods are considered implementation details of the class and should only be &lt;strong&gt;indirectly&lt;/strong&gt; tested through the public methods that use them. &lt;br&gt;
If you feel the need to test a private method directly, it could indicate a &lt;strong&gt;design problem&lt;/strong&gt;. Maybe the class is doing too much and could be refactored to move this functionality to a new class where it would be public and could be tested directly. &lt;br&gt;
However, if you absolutely must test a private method for some reason, you can use &lt;strong&gt;reflection&lt;/strong&gt; in PHP to make a private method accessible and then test it. But again, this should be an &lt;strong&gt;exception rather than the rule&lt;/strong&gt; in most cases.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There's a portion of the algorithmic code that I would like to validate, but I &lt;strong&gt;don't want&lt;/strong&gt; it to be accessible from the outside. If I place this code in a separate class, it would be like making it accessible, even though it has no advantages outside the context of my class. The tests are only for the public methods, forcing me to build &lt;strong&gt;mock&lt;/strong&gt; objects and &lt;strong&gt;making the tests complex&lt;/strong&gt;. If I test my private method, I will have very &lt;strong&gt;simple&lt;/strong&gt; tests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In this case, if you think that testing your private methods directly brings you &lt;strong&gt;value&lt;/strong&gt; and &lt;strong&gt;improves the quality&lt;/strong&gt; of your code, then you should do it. &lt;br&gt;
  After all, the goal of unit tests is to ensure that your code does what it's supposed to do and help you identify and fix bugs. If testing a private method directly helps you achieve these goals, then there is no reason not to do it.&lt;br&gt;
  As mentioned before, you can use reflection to access private methods and test them. Here is an example of how you can do it in PHP:&lt;/p&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;YourClass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$reflector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReflectionClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$reflector&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'yourPrivateMethod'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setAccessible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;invokeArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$arg1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$arg2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;// now you can make assertions on $result&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, this can make your tests a bit more complex and harder to maintain. You should also be aware that this is a kind of "&lt;strong&gt;hack&lt;/strong&gt;" and there's a reason why private methods are not normally accessible - it's about maintaining encapsulation and &lt;strong&gt;hiding implementation details&lt;/strong&gt; which could change. By directly testing private methods, you're &lt;strong&gt;coupling&lt;/strong&gt; your tests to these specific implementation details and any changes to these methods could require a rewrite of your tests.&lt;br&gt;
  Ultimately, it's up to you to decide if the benefits outweigh the drawbacks in your &lt;strong&gt;specific&lt;/strong&gt; case.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A good solution would be to have a concept of &lt;strong&gt;friend classes&lt;/strong&gt; in PHP, but it doesn't exist.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Indeed, PHP does not have a concept of "friend classes" as C++ does. In C++, &lt;strong&gt;a friend class can access private and protected methods&lt;/strong&gt; of the class that declares it as a friend, which could be used to test these methods.&lt;br&gt;
Without this feature in PHP, you have to find other ways to test your private methods if you decide that's necessary. That's why some people choose reflection despite its drawbacks. &lt;br&gt;
If you have a case where you have complex private methods you want to test directly, another option is to make them public just for testing purposes and clearly document that they are intended for testing only. This is far from ideal but it's another option to consider. &lt;br&gt;
In the end, it's important to remember that the goal of unit tests is to make sure each part of your code &lt;strong&gt;works as expected&lt;/strong&gt;, not to ensure each method &lt;strong&gt;passes a test&lt;/strong&gt;. If you can test the behavior of your class in a way that makes sense without needing to test every private method, that might be the most efficient approach.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I wonder if protected methods wouldn't make more sense.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Using protected methods in PHP can indeed be an &lt;strong&gt;alternative&lt;/strong&gt; for testability reasons. Protected methods can be called by the class itself, and by all the classes that inherit from it. &lt;br&gt;
 So in cases of algorithmic complexity that should not be publicly exposed while wishing to be able to unit test it, creating a subclass just for testing to test these specific algorithms could be a solution. &lt;br&gt;
  This is what it could look like:&lt;/p&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;methodToTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Your complex algorithm here&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClassTestVersion&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;methodToTestWrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;methodToTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this is how you could use it in your PHPUnit tests:&lt;/p&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testMethodToTest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$testObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyClassTestVersion&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$testObject&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;methodToTestWrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'input'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Your assertions here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a way to access the method without exposing it as public and without using PHP reflection. However, this solution has its own limitations and drawbacks. One of them is that if the protected methods are &lt;strong&gt;too many&lt;/strong&gt;, it can burden the test class and make it &lt;strong&gt;more difficult to manage&lt;/strong&gt;. Furthermore, if the logic of protected methods changes, the tests must be adjusted in the same way. And if a private method is used by several protected methods, then it will be tested repeatedly, which makes the tests less effective. The appropriate &lt;strong&gt;balance&lt;/strong&gt; therefore depends on each particular case.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A protected method should not change because it's a &lt;strong&gt;contract with the classes&lt;/strong&gt; that inherit from it, and testing them makes a lot of sense, in fact.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Absolutely, you're right. Protected methods are indeed part of the contract of the class with its subclasses, just like public methods are a &lt;strong&gt;contract with any external&lt;/strong&gt; code that uses the class.&lt;br&gt;
Testing protected methods, therefore, makes sense, as it ensures that the class fulfills its contract even as it evolves. Strictly speaking, it's even necessary when you follow a Test-Driven Development (TDD) methodology where every part of the code must be &lt;strong&gt;covered&lt;/strong&gt; by tests.&lt;br&gt;
It's important to keep this contract as stable as possible so as not to break the subclasses that depend on her methods. If you need to change the behavior of these methods, it's important to do so in a way that doesn't break this contract. This is where unit testing of the protected methods can be &lt;strong&gt;very useful&lt;/strong&gt; - to verify that you haven't introduced &lt;strong&gt;regressions&lt;/strong&gt; when modifying these methods.&lt;br&gt;
So it's justified to test your protected methods if it matches your logic of code organization and your testing strategy. However, bear in mind that it is always beneficial to work on &lt;strong&gt;decoupling and simplifying&lt;/strong&gt; your classes, avoiding as much as possible a great number of protected methods, in accordance with the principles of &lt;strong&gt;SOLID&lt;/strong&gt; and object-oriented development.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On private methods, a developer can easily tell himself: "&lt;em&gt;I can change this method, or even delete it, it's not a big deal&lt;/em&gt;", it's private. But by doing that, it's going to break my tests and maybe that will &lt;strong&gt;lead him&lt;/strong&gt; to notice certain cases of use that he had &lt;strong&gt;not identified&lt;/strong&gt;. Maybe an &lt;strong&gt;annotation&lt;/strong&gt; on my method would warn the developer that there are associated tests before he finds out &lt;strong&gt;later&lt;/strong&gt;. An "&lt;a class="mentioned-user" href="https://dev.to/see"&gt;@see&lt;/a&gt;" annotation for example.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your suggestion to use an "&lt;a class="mentioned-user" href="https://dev.to/see"&gt;@see&lt;/a&gt;" annotation is an excellent idea to signal that a private method has &lt;strong&gt;associated tests&lt;/strong&gt;. This can help reinforce communication within the development team and make someone who modifies this method understand that they need to also check and potentially modify the associated tests.&lt;br&gt;
  This could look like this in your code:&lt;/p&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;

&lt;span class="cd"&gt;/**
 * @see \Tests\Unit\YourClassTest::testYourPrivateMethod
 */&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;yourPrivateMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, note that this annotation must be &lt;strong&gt;maintained&lt;/strong&gt;: if the test is renamed or moved, the annotation must be updated.&lt;br&gt;
  It's also important to note that good test coverage of public methods should also catch cases where a modification of a private method leads to errors.&lt;br&gt;
  In any case, the decision to test or not test private methods should be made based on the &lt;strong&gt;specific context&lt;/strong&gt; of the project, the team, and the code itself. The idea is to test what is necessary to have &lt;strong&gt;confidence&lt;/strong&gt; in the correct functioning of your system, while &lt;strong&gt;balancing&lt;/strong&gt; this effort with development time and code maintainability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fred&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yes, I agree, this should remain &lt;strong&gt;marginal&lt;/strong&gt;, and prioritize tests of public methods. I was wondering about this practice to &lt;strong&gt;simplify tests&lt;/strong&gt; and possibly facilitate the addition of tests on &lt;strong&gt;poorly designed legacy code&lt;/strong&gt;. I prefer tests on private methods than &lt;strong&gt;no test at all&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Quite right, I agree with you and you have very good thinking on this subject.&lt;br&gt;
  Indeed, when working with legacy code, you don't always have the opportunity to refactor everything you would like. It is therefore crucial to have tests to &lt;strong&gt;ensure the proper functioning&lt;/strong&gt; of your application, even if it means testing private methods.&lt;br&gt;
  And as you rightly said, in certain cases the test of a private method can be &lt;strong&gt;less complex and quicker&lt;/strong&gt; to set up than the testing of the public method that uses it, notably if it avoids having to mock numerous objects.&lt;br&gt;
  In summary, testing private methods can make sense in &lt;strong&gt;certain&lt;/strong&gt; situations, notably in the framework of legacy code and when this allows the tests to be &lt;strong&gt;simpler and more reliable&lt;/strong&gt;. It is a practice to use sparingly and discerningly, keeping in mind the main objective of unit tests: to ensure the &lt;strong&gt;reliability and quality&lt;/strong&gt; of your code.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>php</category>
      <category>phpunit</category>
      <category>unittest</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>DX : setter, wither and mutants</title>
      <dc:creator>Frédéric Bouchery</dc:creator>
      <pubDate>Tue, 04 Apr 2023 13:20:01 +0000</pubDate>
      <link>https://forem.com/fredbouchery/dx-setter-wither-and-mutants-4b44</link>
      <guid>https://forem.com/fredbouchery/dx-setter-wither-and-mutants-4b44</guid>
      <description>&lt;p&gt;(English version: &lt;a href="https://dev.to/fredbouchery/dx-setter-wither-and-mutants-29e6"&gt;https://dev.to/fredbouchery/dx-setter-wither-and-mutants-29e6&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Quand je conçois un code, je passe beaucoup de temps à réfléchir à sa DX (Developer eXperience), c'est d'ailleurs pour cela que j'apprécie les "test before", car cela me permet de réfléchir à la façon d'utiliser le code avant de l'implémenter.&lt;/p&gt;

&lt;p&gt;Dernièrement, je concevais une classe de configuration pour une couche d'accès à une BDD et voici la façon dont j'avais initialement imaginé son usage :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DriverInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DRIVER_MYSQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3307&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Au niveau implémentation, c'est une interface chaînée (&lt;a href="https://en.wikipedia.org/wiki/Fluent_interface"&gt;fluent interface&lt;/a&gt;) assez simple : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;connection(string $name)&lt;/code&gt; vient définir une propriété "&lt;code&gt;$currentConnectionName&lt;/code&gt;"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;primary()&lt;/code&gt; ou &lt;code&gt;replica()&lt;/code&gt; viennent définir une propriété &lt;code&gt;$currentReplicationServer&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ensuite, quand on appelle &lt;code&gt;host()&lt;/code&gt; ou &lt;code&gt;credentials()&lt;/code&gt;, j'utilise les propriétés "&lt;code&gt;$currentXxxx&lt;/code&gt;" pour stocker l'information.&lt;/p&gt;

&lt;p&gt;Puis, j'ai voulu définir comment j'allais lire ces informations, et j'ai commencé à écrire ça :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'login'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCredentials&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Premier constat : Pffff ... j'ai eu la flemme de définir un objet "ServerCredentials" pour y stocker le login et le pass, alors qu'un tableau associatif faisait bien l'affaire. Ne me regardez pas avec cette expression sévère, on l'a tous fait ... les jours de flemme, et comme le dirait &lt;a href="https://fr.wikipedia.org/wiki/Kr%C3%A4n"&gt;Krän&lt;/a&gt; : "Y'a des jours, faut pas m'chercher ! Et y'a des jours tous les jours !". Mais c'est pas le débat aujourd'hui, voyons le problème suivant que je voudrais mettre en valeur.&lt;/p&gt;

&lt;p&gt;Pour lire l'identité, j'ai utilisé un getter &lt;code&gt;getCredentials()&lt;/code&gt; alors que pour la définir, j'ai utilisé la simple méthode &lt;code&gt;credentials($login, $pass)&lt;/code&gt;, car je trouvais ça plus &lt;em&gt;cool&lt;/em&gt; pour ma DX.&lt;/p&gt;

&lt;p&gt;Simplement, quand on se place du point de vue de la personne qui va récupérer la définition, déjà, ce n'est pas habituel d'avoir un getter et une méthode d'assignation qui ne commence pas par "set", et en plus, cela peut être confusant quand l'IDE va autocompléter et proposer ces deux méthodes.&lt;/p&gt;

&lt;p&gt;Bon, certes, il n'y a pas trop d'ambiguïté, car une méthode avec deux paramètres &lt;code&gt;$login&lt;/code&gt; et &lt;code&gt;$pass&lt;/code&gt;, il est probable qu'elle permette de les assigner. Mais, j'ai quand même préféré changer ça avec des setters.&lt;/p&gt;

&lt;p&gt;Oui, car quand on conçoit une bonne DX, il faut éviter de trop changer les habitudes des développeuses et des développeurs ! Même si votre idée semble révolutionnaire, innovante, jamais faite auparavant, cela peut desservir votre DX drastiquement.&lt;/p&gt;

&lt;p&gt;Me voilà donc à changer la définition :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setDriver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DriverInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DRIVER_MYSQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3307&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et là, une nouvelle difficulté s'est présenté à moi : les mutants !&lt;/p&gt;

&lt;p&gt;Regardez ce code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$serverIp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$replica&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Define hosts&lt;/span&gt;
&lt;span class="nv"&gt;$primary&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$serverIp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$replica&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$serverIp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3307&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Define credentials&lt;/span&gt;
&lt;span class="nv"&gt;$primary&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$replica&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oui, on imagine une façon d'utiliser un code et la personne va le faire d'une toute autre manière, c'est son droit.&lt;/p&gt;

&lt;p&gt;Seulement, dans le cas présent, il y a un bug assez gênant : le serveur primaire n'est jamais défini. Si vous ne voyez pas pourquoi, je vous laisse essayer de comprendre avant de lire l'explication qui va suivre.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
Comme je l'ai indiqué au début, quand on appelle &lt;code&gt;primary()&lt;/code&gt; ou &lt;code&gt;replica()&lt;/code&gt;, on vient définir une propriété &lt;code&gt;$currentReplicationServer&lt;/code&gt;, et comme j'utilise une interface "fluent", les variables &lt;code&gt;$primary&lt;/code&gt; et &lt;code&gt;$replica&lt;/code&gt; contiennent toutes les deux la même instance d'objet. Lors de l'appel à &lt;code&gt;-&amp;gt;replica()&lt;/code&gt; la propriété &lt;code&gt;$currentReplicationServer&lt;/code&gt; va donc être modifiée pour indiquer que l'on configure le réplica, ce qui concerne également l'instance dans la variable &lt;code&gt;$primary&lt;/code&gt; (je vous laisse relire si vous n'avez pas bien compris).&lt;/p&gt;

&lt;p&gt;On tombe dans un grand classique en PHP (et d'autres langages) : la mutabilité des objets.&lt;/p&gt;

&lt;p&gt;Pour résoudre cela, on utilise l'auto-clonage, on change la propriété et on retourne la nouvelle instance. On passe donc de :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;currentReplicationConfiguration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'primary'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;À :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;clone&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;currentReplicationConfiguration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'primary'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et on va appliquer cette technique sur l'ensemble des méthodes, sauf que ... ça ne fonctionne pas. Car dans le cas :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$replica&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ce que je souhaite, c'est que $conf contienne tous les éléments de configuration, pour la connexion "main", avec le serveur primaire et le réplica, et au final, on a 3 instances de la classe de configuration avec des valeurs dissociées.&lt;/p&gt;

&lt;p&gt;Et c'est bien tout le problème du design "&lt;em&gt;fluent&lt;/em&gt;" couplé à un changement d'état (stateful vs stateless) : Si le comportement d'une méthode dépend de l'appel d'une autre, vous êtes dans le pétrin !&lt;/p&gt;

&lt;p&gt;Il y a une possibilité pour résoudre partiellement ça, c'est en faisant comprendre au développeur, ou à la développeuse, que la méthode retourne une nouvelle instance : on va remplacer les "setters" par des "withers" :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withPrimary&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En utilisant le préfixe "&lt;code&gt;with&lt;/code&gt;" cela indique que le retour sera une nouvelle instance de l'objet, donc &lt;code&gt;$conf&lt;/code&gt; n'est pas la même instance que &lt;code&gt;$primary&lt;/code&gt;. Le développeur ou la développeuse va donc devoir chaîner ses appels, sinon le résultat final ne contiendra pas ce qu'il souhaite.&lt;/p&gt;

&lt;p&gt;En réalité, on n'a pas résolu la question par du code supplémentaire, mais juste par un simple renommage, car, oui, la conception d'un code, et d'une bonne DX, passe aussi par son nommage.&lt;/p&gt;

&lt;p&gt;Le code final serait :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setDriver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DriverInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DRIVER_MYSQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withPrimary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withReplica&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3307&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et, si on revient à l'accès, cela donne :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'login'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withPrimary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCredentials&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Franchement, je trouve pas ça dingue comme DX, car dans mon petit cerveau, j'ai tendance à associer les "withers" à de la construction et non de l'accès.&lt;/p&gt;

&lt;p&gt;C'est la quadrature du cercle, vous ne trouvez pas ?&lt;/p&gt;

&lt;p&gt;En fait non, c'est tout simplement parce que j'ai fait fausse route depuis le début, et cela a commencé quand j'ai écrit que j'avais eu la flemme !&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ma classe aurait dû s'appeler "&lt;code&gt;ConnectionsConfiguration&lt;/code&gt;" avec un "s" et un mot complet (Arrêtez les raccourcis, c'est pas 7 caractères de plus qui vont impacter votre productivité)&lt;/li&gt;
&lt;li&gt;Il ne devrait y avoir qu'une seule méthode "&lt;code&gt;connection(string $name)&lt;/code&gt;" qui retourne un objet "&lt;code&gt;ConnectionConfiguration&lt;/code&gt;" (sans le "s" donc)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ConnectionConfiguration&lt;/code&gt; dispose de trois méthodes &lt;code&gt;setDriver()&lt;/code&gt;, &lt;code&gt;primary()&lt;/code&gt; et &lt;code&gt;replica()&lt;/code&gt;, dont les deux dernières retournent des instances &lt;strong&gt;mutables&lt;/strong&gt; de &lt;code&gt;ServerConfiguration&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ServerConfiguration&lt;/code&gt; contient quatres méthodes &lt;code&gt;setHost()&lt;/code&gt;, &lt;code&gt;setCredentials()&lt;/code&gt;, &lt;code&gt;getHost()&lt;/code&gt; et &lt;code&gt;getCredentials()&lt;/code&gt; gérant des objets &lt;code&gt;ServerHost&lt;/code&gt; et &lt;code&gt;ServerCredentials&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Au final, le code sera :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionsConfiguration&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setDriver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DriverInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DRIVER_MYSQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3307&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sauf qu'avec ce code, je n'ai plus une interface fluent et ... j'ai une erreur fatale !&lt;br&gt;
Si vous regardez attentivement, après le &lt;code&gt;setCredentials()&lt;/code&gt;, j'appelle &lt;code&gt;replica()&lt;/code&gt;, mais cette méthode n'existe pas, car &lt;code&gt;setCredentials()&lt;/code&gt; ne retourne pas un &lt;code&gt;ConnectionConfiguration&lt;/code&gt; mais un &lt;code&gt;ServerCredentials&lt;/code&gt;. De même, la variable "&lt;code&gt;$conf&lt;/code&gt;" ne contient pas un objet &lt;code&gt;ConnectionsConfiguration&lt;/code&gt;. Bref, sans interface fluent, il faudrait écrire comme ceci :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionsConfiguration&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setDriver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DriverInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DRIVER_MYSQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3307&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;C'est tout de suite moins &lt;em&gt;joli&lt;/em&gt; que les enchaînements d'appels d'une fluent, mais on aura beaucoup moins de risque de bug.&lt;/p&gt;

&lt;p&gt;Pour l'accès à la configuration, cela donne :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;
   &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
   &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCredentials&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les plus attentifs auront noté que mes classes sont mutables, et pour certains, c'est une hérésie, un scandale, une honte qui devrait me conduire au bûcher ! À ceux là, je répondrais :&lt;br&gt;
Oui, mais cela générerait beaucoup plus de problèmes qu'il n'en résout et casserait ma DX. Alors, je vous renvois au mantra de Krän : "Y'a des jours, faut pas m'chercher ! Et y'a des jours tous les jours  !".&lt;/p&gt;

&lt;p&gt;En résumé :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Placez-vous du point de vue de l'utilisation et non de l'implémentation&lt;/li&gt;
&lt;li&gt;N'innovez pas trop, les gens ont des habitudes&lt;/li&gt;
&lt;li&gt;Ne soyez jamais flemmards&lt;/li&gt;
&lt;li&gt;Utilisez l'immutabilité avec parcimonie&lt;/li&gt;
&lt;li&gt;Attention aux interfaces fluents sur les objets stateful&lt;/li&gt;
&lt;li&gt;Lisez Krän, c'est une BD déjantée à souhait&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>php</category>
      <category>programming</category>
      <category>architecture</category>
      <category>dx</category>
    </item>
    <item>
      <title>DX: setter, wither and mutants</title>
      <dc:creator>Frédéric Bouchery</dc:creator>
      <pubDate>Tue, 04 Apr 2023 13:18:56 +0000</pubDate>
      <link>https://forem.com/fredbouchery/dx-setter-wither-and-mutants-29e6</link>
      <guid>https://forem.com/fredbouchery/dx-setter-wither-and-mutants-29e6</guid>
      <description>&lt;p&gt;(Version française: &lt;a href="https://dev.to/fredbouchery/dx-setter-wither-and-mutants-4b44"&gt;https://dev.to/fredbouchery/dx-setter-wither-and-mutants-4b44&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;When I design my PHP code, I spend a lot of time thinking about its DX (Developer eXperience), which is why I appreciate "test before" because it allows me to think about how to use the code before implementing it.&lt;/p&gt;

&lt;p&gt;Recently, I was designing a configuration class for a database access layer and here's how I initially planned to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DriverInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DRIVER_MYSQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;host&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3307&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation is a fairly simple fluent interface:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;connection(string $name)&lt;/code&gt; sets the "&lt;code&gt;$currentConnectionName&lt;/code&gt;" property.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;primary()&lt;/code&gt; or &lt;code&gt;replica()&lt;/code&gt; sets the "&lt;code&gt;$currentReplicationServer&lt;/code&gt;" property.&lt;/li&gt;
&lt;li&gt;Then, when &lt;code&gt;host()&lt;/code&gt; or &lt;code&gt;credentials()&lt;/code&gt; is called, I use the "&lt;code&gt;$currentXxxx&lt;/code&gt;" properties to store the value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, I wanted to define how I would read these values, and I started writing this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'login'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCredentials&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First observation: Ouch... I was too lazy to define a "&lt;code&gt;ServerCredentials&lt;/code&gt;" object to store the login and password, even though an associative array would do just fine. Don't look at me with that stern expression, we've all done it... on lazy days, and as &lt;a href="https://fr.wikipedia.org/wiki/Kr%C3%A4n"&gt;Krän&lt;/a&gt; would say: "There are days you shouldn't mess with me! And there are days every day!" But that's not the debate today; let's look at the next problem I want to highlight.&lt;/p&gt;

&lt;p&gt;To read the credentials, I used a &lt;code&gt;getCredentials()&lt;/code&gt; getter, while to define it, I used the simple &lt;code&gt;credentials($login, $pass)&lt;/code&gt; method because I found it cooler for my DX.&lt;/p&gt;

&lt;p&gt;Actually, from the point of view of the person who will retrieve the definition, it's not usual to have a getter and an assignment method that doesn't start with "set," and it can be confusing when the IDE autocompletes will suggest these two methods.&lt;/p&gt;

&lt;p&gt;Obviously, a method with two parameters &lt;code&gt;$login&lt;/code&gt; and &lt;code&gt;$pass&lt;/code&gt; should be the method which assigned the value, but, I still preferred to change that with setters.&lt;/p&gt;

&lt;p&gt;Yes, because when you design a good DX, you should not change too much the way developers do things! Even if your idea seems revolutionary, innovative, never done before, it can drastically harm your DX.&lt;/p&gt;

&lt;p&gt;Here how I changed the definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setDriver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DriverInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DRIVER_MYSQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3307&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then, a new difficulty came to me: mutants!&lt;/p&gt;

&lt;p&gt;Look at this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$serverIp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$replica&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Define hosts&lt;/span&gt;
&lt;span class="nv"&gt;$primary&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$serverIp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$replica&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$serverIp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3307&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Define credentials&lt;/span&gt;
&lt;span class="nv"&gt;$primary&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$replica&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, we designed a way to use our code and developer will do it in a completely different way, it's his right.&lt;/p&gt;

&lt;p&gt;However, in this case, there is a rather embarrassing bug: the primary server configuration is never defined. If you don't see why, I'll let you try to understand before reading the explanation that follows.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
As I mentioned at the beginning, when we call &lt;code&gt;primary()&lt;/code&gt; or &lt;code&gt;replica()&lt;/code&gt;, we are setting a &lt;code&gt;$currentReplicationServer&lt;/code&gt; property, and since I am using a fluent interface, the &lt;code&gt;$primary&lt;/code&gt; and &lt;code&gt;$replica&lt;/code&gt; variables both contain the same object instance. When calling &lt;code&gt;-&amp;gt;replica()&lt;/code&gt;, the &lt;code&gt;$currentReplicationServer&lt;/code&gt; property will therefore be modified to indicate that we are configuring the replica, which also affects the instance in the &lt;code&gt;$primary&lt;/code&gt; variable (I'll let you reread if you didn't understand it well).&lt;/p&gt;

&lt;p&gt;Now, we fall into a classic pitfall in PHP (and other languages): object mutability.&lt;/p&gt;

&lt;p&gt;To solve this, auto-cloning is used, the property is changed, and the new instance is returned. So, we go from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;currentReplicationConfiguration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'primary'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;clone&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;currentReplicationConfiguration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'primary'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this technique is applied to all methods, but... it doesn't work. Because in this case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$replica&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I want is that &lt;code&gt;$conf&lt;/code&gt; contains all the configuration elements, for the "&lt;code&gt;main&lt;/code&gt;" connection, with the primary server and the replica, and finally, we have 3 instances of the configuration class with dissociated values.&lt;/p&gt;

&lt;p&gt;And that's the problem with fluent design coupled with stateful/stateless design: If the behavior of a method depends on the call of another one, you are in trouble!&lt;/p&gt;

&lt;p&gt;There is a way to partially solve this, by making the developer understand that the method returns a new instance: we will replace the setters by withers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withPrimary&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the prefix "&lt;code&gt;with&lt;/code&gt;" indicates that the return will be a new instance of the object, so &lt;code&gt;$conf&lt;/code&gt; is not the same instance as &lt;code&gt;$primary&lt;/code&gt;. The developer must chain his calls otherwise he will not get the result he expects.&lt;/p&gt;

&lt;p&gt;In reality, we haven't solved the problem with additional code, but simply by renaming it, because yes, naming is part of the design and DX.&lt;/p&gt;

&lt;p&gt;The final code would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionConfig&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setDriver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DriverInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DRIVER_MYSQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withPrimary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withReplica&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3307&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, if we go back to the access, it would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'login'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withPrimary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCredentials&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Honestly, I don't think this DX is amazing because in my small brain, I tend to associate "withers" with building rather than access.&lt;/p&gt;

&lt;p&gt;It's like squaring the circle, don't you think?&lt;/p&gt;

&lt;p&gt;Actually, no, it's simply because I went the wrong way from the start, and it started when I wrote that I was lazy!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;My class should have been called "&lt;code&gt;ConnectionsConfiguration&lt;/code&gt;" with an "s" and a complete word (Stop taking shortcuts, it's not 7 more characters that will impact your productivity)&lt;/li&gt;
&lt;li&gt;There should only be one method "&lt;code&gt;connection(string $name)&lt;/code&gt;" that returns a "&lt;code&gt;ConnectionConfiguration&lt;/code&gt;" object (without the "s")&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ConnectionConfiguration&lt;/code&gt; has three methods &lt;code&gt;setDriver()&lt;/code&gt;, &lt;code&gt;primary()&lt;/code&gt;, and &lt;code&gt;replica()&lt;/code&gt;, the latter two returning mutable instances of &lt;code&gt;ServerConfiguration&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ServerConfiguration&lt;/code&gt; contains four methods &lt;code&gt;setHost()&lt;/code&gt;, &lt;code&gt;setCredentials()&lt;/code&gt;, &lt;code&gt;getHost()&lt;/code&gt;, and &lt;code&gt;getCredentials()&lt;/code&gt; managing &lt;code&gt;ServerHost&lt;/code&gt; and &lt;code&gt;ServerCredentials&lt;/code&gt; objects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the end, the code will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionsConfiguration&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setDriver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DriverInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DRIVER_MYSQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3307&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Except that with this code, I don't have a fluent interface anymore and ... I have a fatal error!&lt;/p&gt;

&lt;p&gt;If you look carefully, after the &lt;code&gt;setCredentials()&lt;/code&gt;, I call &lt;code&gt;replica()&lt;/code&gt;, but this method does not exist, because &lt;code&gt;setCredentials()&lt;/code&gt; does not return a &lt;code&gt;ConnectionConfiguration&lt;/code&gt; but a &lt;code&gt;ServerCredentials&lt;/code&gt;. Also, the variable &lt;code&gt;$conf&lt;/code&gt; does not contain a &lt;code&gt;ConnectionsConfiguration&lt;/code&gt; object. In short, without a fluent interface, we would have to write like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectionsConfiguration&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setDriver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DriverInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DRIVER_MYSQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'root'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$conf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3307&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'readonly'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's less &lt;em&gt;beautiful&lt;/em&gt; than a fluent call chain, but there is less risk of a bug.&lt;/p&gt;

&lt;p&gt;For the access to the configuration, it gives :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$conf&lt;/span&gt;
   &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCredentials&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most attentive may have noticed that my classes are mutable, and for some, it's heresy, scandal, a shame that should lead me to the stake! To those, I would reply:&lt;br&gt;
Yes, but it would generate more problems than it solves and break my DX. So, I refer you to Krän's mantra: "There are days when you shouldn't mess with me! And there are days every day!".&lt;/p&gt;

&lt;p&gt;In summary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Place yourself from the user's point of view and not the implementation's&lt;/li&gt;
&lt;li&gt;Don't innovate too much, people have patterns&lt;/li&gt;
&lt;li&gt;Don't be lazy&lt;/li&gt;
&lt;li&gt;Use immutability sparingly&lt;/li&gt;
&lt;li&gt;Be careful with fluent interfaces on stateful objects&lt;/li&gt;
&lt;li&gt;Read &lt;a href="https://fr.wikipedia.org/wiki/Kr%C3%A4n"&gt;Krän&lt;/a&gt;, it's a delightfully insane comic book.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>php</category>
      <category>programming</category>
      <category>dx</category>
    </item>
    <item>
      <title>Why your class must be final</title>
      <dc:creator>Frédéric Bouchery</dc:creator>
      <pubDate>Sat, 01 Apr 2023 19:00:00 +0000</pubDate>
      <link>https://forem.com/fredbouchery/why-your-class-must-be-final-149c</link>
      <guid>https://forem.com/fredbouchery/why-your-class-must-be-final-149c</guid>
      <description>&lt;p&gt;(Version Française: &lt;a href="https://dev.to/fredbouchery/pourquoi-il-faut-declarer-ses-classes-final-5e3l/"&gt;Pourquoi il faut déclarer ses classes "final"&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;When coding with other developers, such as in peer or mob programming, they often ask me: "Why do you declare all your classes as &lt;code&gt;final&lt;/code&gt;?". My response has always been that it's a &lt;em&gt;good practice&lt;/em&gt; for me, inherited from my far experience with Delphi, where all classes are "final" by default. However, when asked why it's a good practice, I realized I needed to be more specific.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F3cd2Q9b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tn1pubwj0wuikryaiq2e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F3cd2Q9b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tn1pubwj0wuikryaiq2e.jpg" alt="Image description" width="880" height="525"&gt;&lt;/a&gt;&lt;br&gt;
Using the &lt;code&gt;final&lt;/code&gt; keyword can help avoid &lt;em&gt;inheritance hell&lt;/em&gt; which refers to large and complex class hierarchies that are difficult to navigate and understand. Inheritance can make testing and debugging more difficult, especially when dealing with complex inheritance chains and dependencies. In other words, inheritance can lead to tight coupling, increased complexity, and code brittleness.&lt;/p&gt;

&lt;p&gt;Another issue with inheritance is that it creates a strong coupling between the base class and the derived class, making it harder to change the base class without affecting the derived classes. This can make the code harder to maintain over time.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K3ArLlGc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gknyk7ie9na5zynxoyou.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K3ArLlGc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gknyk7ie9na5zynxoyou.jpg" alt="Image description" width="880" height="587"&gt;&lt;/a&gt;&lt;br&gt;
Recently, I was making changes to a PHP library that use database terminology &lt;em&gt;master/slave&lt;/em&gt; and I wanted to make theses modifications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rename methods "master" to "primary" and "slave" to "replica"&lt;/li&gt;
&lt;li&gt;add deprecated methods "master" and "slave" that call "primary" and "replica" methods&lt;/li&gt;
&lt;li&gt;add a comment to the  "slave" deprecated method to explain the reasons for the change (due to negative connotations).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However the class define a property called &lt;code&gt;$slaveConnection&lt;/code&gt; which was declared as &lt;code&gt;protected&lt;/code&gt;. I would have liked to change also its name, but couldn't do so because it could be used by a child class.&lt;br&gt;
I considered declaring the property as &lt;code&gt;private&lt;/code&gt; and using magic getter and setter to allow child class to write and read the deprecated property name, but felt that this added unnecessary complexity to solve something I'm pretty sure it doesn't exists. Although this library is open-sourced, it's not very popular, and the probability that this class is overrided by someone, lonely in a dark place, is very close to zero.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t32qLkWo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hx94zc6gyrpr4g6el05.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t32qLkWo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hx94zc6gyrpr4g6el05.jpg" alt="Image description" width="880" height="479"&gt;&lt;/a&gt;&lt;br&gt;
To avoid introducing breaking changes to the code, I kept this offensive name, and added a comment explaining that it was being kept for compatibility reasons, but I didn't approve of the name.&lt;/p&gt;

&lt;p&gt;As you understand, because the class is not &lt;code&gt;final&lt;/code&gt;, and even though I was sure that it wouldn't be overridden, I can't make my change.&lt;br&gt;
If the developer who designed this class had wrote all his classes as &lt;code&gt;final&lt;/code&gt; by default, I wouldn't have encountered this issue, and it's safer to remove the &lt;code&gt;final&lt;/code&gt; keyword only when necessary to override a class.&lt;/p&gt;

&lt;p&gt;In conclusion, making all classes &lt;code&gt;final&lt;/code&gt; by default can save time for future generations of developers who will maintain the code.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rzYFZQGH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8690clpqqinq6psfvjv9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rzYFZQGH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8690clpqqinq6psfvjv9.jpg" alt="Image description" width="880" height="586"&gt;&lt;/a&gt;&lt;br&gt;
(source of photos: pexels.com)&lt;/p&gt;

</description>
      <category>php</category>
      <category>design</category>
      <category>compatibility</category>
    </item>
    <item>
      <title>Pourquoi il faut déclarer ses classes "final"</title>
      <dc:creator>Frédéric Bouchery</dc:creator>
      <pubDate>Sat, 01 Apr 2023 17:51:58 +0000</pubDate>
      <link>https://forem.com/fredbouchery/pourquoi-il-faut-declarer-ses-classes-final-5e3l</link>
      <guid>https://forem.com/fredbouchery/pourquoi-il-faut-declarer-ses-classes-final-5e3l</guid>
      <description>&lt;p&gt;(English version: &lt;a href="https://dev.to/fredbouchery/why-your-class-must-be-final-149c"&gt;Why your class must be final&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Lorsque je code avec d'autres développeuses ou développeurs, en pair programming ou en groupe, on me demande souvent : "Pourquoi déclares-tu toutes tes classes "&lt;code&gt;final&lt;/code&gt;?".&lt;/p&gt;

&lt;p&gt;Ma réponse a toujours été : C'est une bonne pratique pour moi, héritée de mon expérience avec Delphi, où toutes les classes sont "&lt;code&gt;final&lt;/code&gt;" par défaut.&lt;br&gt;
Cependant, lorsque l'on me demande pourquoi c'est une bonne pratique, j'ai réalisé que je devais être plus précis.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_T2JDsud--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ghu6ndnuz0rao20z5nac.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_T2JDsud--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ghu6ndnuz0rao20z5nac.jpg" alt="Image description" width="880" height="525"&gt;&lt;/a&gt;&lt;br&gt;
L'utilisation du mot-clé "&lt;code&gt;final&lt;/code&gt;" peut aider à éviter le &lt;em&gt;inheritance hell&lt;/em&gt;, en référence aux interminables et complexes hiérarchies de classes, dans laquelle il est difficiles de se retrouve, et de comprendre.&lt;br&gt;
L'héritage peut rendre les tests et le débogage plus difficiles, en particulier lorsqu'il s'agit de chaînes d'héritage complexes avec des dépendances dans tous les sens. En d'autres termes, l'héritage peut entraîner un code fragile et une complexité accrue.&lt;/p&gt;

&lt;p&gt;Un autre problème avec l'héritage est qu'il crée un couplage fort entre la classe de base et la classe dérivée, rendant plus difficile le changement de la classe de base sans affecter les classes dérivées et cela peut rendre le code plus difficile à maintenir dans le temps.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K3ArLlGc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gknyk7ie9na5zynxoyou.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K3ArLlGc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gknyk7ie9na5zynxoyou.jpg" alt="Image description" width="880" height="587"&gt;&lt;/a&gt;&lt;br&gt;
Récemment, j'ai apporté des modifications à une bibliothèque PHP qui utilise la terminologie "maître/esclave" et je voulais effectuer les modifications suivantes :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Renommer les méthodes "&lt;code&gt;master()&lt;/code&gt;" en "&lt;code&gt;primary()&lt;/code&gt;" et "&lt;code&gt;slave()&lt;/code&gt;" en "&lt;code&gt;replica()&lt;/code&gt;"&lt;/li&gt;
&lt;li&gt;Ajouter des méthodes dépréciées "&lt;code&gt;master()&lt;/code&gt;" et "&lt;code&gt;slave()&lt;/code&gt;" qui appellent les méthodes "&lt;code&gt;primary()&lt;/code&gt;" et "&lt;code&gt;replica()&lt;/code&gt;"&lt;/li&gt;
&lt;li&gt;Ajouter un commentaire à la méthode dépréciée "&lt;code&gt;slave()&lt;/code&gt;" pour expliquer les raisons du changement (connotations négatives).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Le problème, c'est que la classe définissait également une propriété appelée "&lt;code&gt;$slaveConnection&lt;/code&gt;" qui était déclarée comme &lt;code&gt;protected&lt;/code&gt;.&lt;br&gt;
J'aurais aimé changer également son nom, mais je ne pouvais pas le faire car elle était peut-être utilisée par une classe enfant.&lt;br&gt;
J'ai envisagé de déclarer la propriété comme &lt;code&gt;private&lt;/code&gt; et utiliser des getters et des setters magiques pour permettre aux classes enfants d'écrire et de lire le nom de la propriété déprécié, mais j'ai estimé que cela ajoutait une complexité inutile pour résoudre quelque chose dont je suis sûr que ce n'est pas utilisé. Bien que cette bibliothèque soit open source, elle n'est pas très populaire, et la probabilité que cette classe soit redéfinie, dans un obscure bout de code, fait par une personne dans un coin sombre d'internet, est proche de zéro.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t32qLkWo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hx94zc6gyrpr4g6el05.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t32qLkWo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hx94zc6gyrpr4g6el05.jpg" alt="Image description" width="880" height="479"&gt;&lt;/a&gt;&lt;br&gt;
Pour éviter d'introduire des changements qui casserait la compatibilité du code, j'ai conservé ce nom offensant et ajouté un commentaire expliquant qu'il était conservé pour des raisons de compatibilité, mais je n'approuvais pas le nom.&lt;/p&gt;

&lt;p&gt;Comme vous le comprenez, parce que la classe n'est pas &lt;code&gt;final&lt;/code&gt;, et même si je suis sûr qu'elle n'est pas redéfinie, je ne peux pas faire mon changement. Si le développeur ou la développeuse qui avait conçu cette classe, avait le réflexe de déclarer &lt;code&gt;final&lt;/code&gt; toutes ses classes, je n'aurais jamais rencontré ce problème. D'ailleurs, si on veut redéfinir la classe plus tard, il est beaucoup plus simple de supprimer le mot clé &lt;code&gt;final&lt;/code&gt; sans générer de casse de compatibilité.&lt;/p&gt;

&lt;p&gt;En conclusion, déclarer toutes vos classes final par défaut peut faire gagner du temps aux générations futures de développeurs qui maintiendront votre code.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rzYFZQGH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8690clpqqinq6psfvjv9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rzYFZQGH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8690clpqqinq6psfvjv9.jpg" alt="Image description" width="880" height="586"&gt;&lt;/a&gt;&lt;br&gt;
(source des photos: pexels.com)&lt;/p&gt;

</description>
      <category>php</category>
      <category>programming</category>
      <category>softwardesign</category>
    </item>
    <item>
      <title>Performance of UUIDs</title>
      <dc:creator>Frédéric Bouchery</dc:creator>
      <pubDate>Sun, 22 Mar 2020 22:55:11 +0000</pubDate>
      <link>https://forem.com/fredbouchery/performance-of-uuids-3i40</link>
      <guid>https://forem.com/fredbouchery/performance-of-uuids-3i40</guid>
      <description>&lt;p&gt;(&lt;a href="https://dev.to/fredbouchery/performance-des-uuids-29dj/"&gt;version francaise&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Recently, while analyzing the performance of an application using &lt;a href="https://blackfire.io/"&gt;blackfire&lt;/a&gt;, we found that a significant amount of time was spent transforming binary UUIDs into strings and vice-versa.&lt;/p&gt;

&lt;p&gt;It has to be mentioned that the UUIDs are stored in the database using binary format, and the application having a Rest API, the UUIDs are therefore always converted into strings as &lt;code&gt;"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxx-xxxxxxxx"&lt;/code&gt;, either to return the JSON response or to process requests with UUIDs.&lt;/p&gt;

&lt;p&gt;Like most developers, the application’s developers have relied on the "&lt;a href="https://github.com/ramsey/uuid"&gt;Ramsey/UUID&lt;/a&gt;" package, which seems to be the solution commonly used for handling UUIDs. The excellent reputation of this component has conducted us to look for optimization in the application without challenging the relevance of Ramsey/UUID.&lt;/p&gt;

&lt;p&gt;Recently, while reading JoliCode's article "&lt;a href="https://jolicode.com/blog/uuid-generation-in-php"&gt;UUID generation in PHP&lt;/a&gt;" and the discussion about a pull request on &lt;a href="https://twitter.com/matthieunapoli/status/1238541015381835778"&gt;Symfony UID&lt;/a&gt;, we looked a bit more into the way our application manipulates UUIDs, because as &lt;a href="https://jolicode.com/equipe/gregoire-pineau"&gt;Grégoire Pineau&lt;/a&gt; says, if Symfony's polyfill is as efficient, we might have to reconsider the use of Ramsey/UUID.&lt;/p&gt;

&lt;p&gt;We did some profiling of the application, and we found that we spend a lot of time encoding/decoding UUIDs rather than generating them. This might be a common situation for many applications.&lt;/p&gt;

&lt;p&gt;In this context, is JoliCode's benchmark still relevant, since it compares the generation of UUIDs?&lt;/p&gt;

&lt;p&gt;So we've did other benchmarks, but this time we did it by comparing an operation that converts a binary UUID into a string and a string into a binary.&lt;/p&gt;

&lt;p&gt;With Ramsey/UUID, this is done when you create a Uuid object from a binary and then convert it back to a binary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Ramsey\Uuid\Uuid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getBytes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We created a &lt;code&gt;Uuid&lt;/code&gt; class that uses the PHP extension "&lt;a href="https://pecl.php.net/package/uuid"&gt;uuid&lt;/a&gt;", as a &lt;a href="https://en.wikipedia.org/wiki/Value_object"&gt;ValueObject&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Uuid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @var string
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;uuid_is_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Wrong UUID format"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Uuid&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;fromBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$bytes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;uuid_unparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bytes&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getBytes&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;uuid_parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even before running the benchmark for &lt;code&gt;Uuid::fromBytes($bytes)-&amp;gt;getBytes()&lt;/code&gt;, we already knew that it would outperform Ramsey/UUID because there are no calls to factories and other codecs. Moreover, internally, Ramsey/UUID keeps the UUID in a decomposed field structure, while in the previous code, the UUID is stored as a string.&lt;/p&gt;

&lt;p&gt;So we looked at how the &lt;a href="https://github.com/symfony/polyfill-uuid"&gt;polyfill Symfony&lt;/a&gt; is implemented, which allows to replace the functions of the UUID extension when it is not available, and we figured out that it could be optimized, because some greedy processing was done in uuid_unparse() to validate the UUID structure.&lt;/p&gt;

&lt;p&gt;Here's how we could optimize it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Uuid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @var string
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'`^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$`Di'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Wrong UUID format"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Uuid&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;fromBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$bytes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Invalid binary UUID. Length is not 16 bytes"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$hex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;substr_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;bin2hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bytes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$hex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;substr_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$hex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;substr_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;substr_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getBytes&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;hex2bin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The execution time for 1 million iterations is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ramsey =&amp;gt; 2583 ms
ext-uuid =&amp;gt; 1260 ms
polyfill-symfony =&amp;gt; 4020 ms
custom =&amp;gt; 642 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*(the environment doesn't really matter, just know that the test was done on Ubuntu 19.10 with PHP 7.3.11).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N0Eoj0H---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/q0urzzy1j0209lok217g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N0Eoj0H---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/q0urzzy1j0209lok217g.jpg" alt="What?" width="640" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We ran this benchmark many times to be sure, but yes, the code is twice as fast as the UUID extension!&lt;/p&gt;

&lt;p&gt;Regarding the generation of UUIDs, as this is an unusual operation for us, we kept using Ramsey/UUID for this.&lt;/p&gt;

&lt;p&gt;In our context, we found that Ramsey/UUID is not the best option. But the most surprising thing is that our PHP code is more efficient in encoding/decoding UUIDs than the extension.&lt;br&gt;
So, if you don't have this extension, it's not a big deal, because it's less efficient than the PHP version.&lt;/p&gt;

&lt;p&gt;Update: Since writing this article, &lt;a href="https://twitter.com/nicolasgrekas"&gt;Nicolas Grekas&lt;/a&gt; has submitted a &lt;a href="https://github.com/symfony/polyfill/pull/244"&gt;PR&lt;/a&gt; to optimize Symfony's polyfill.&lt;br&gt;
Here is the benchmark result with this PR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ramsey =&amp;gt; 2583 ms
ext-uuid =&amp;gt; 1260 ms
polyfill-symfony =&amp;gt; 4020 ms
polyfill-symfony-optim =&amp;gt; 1145 ms
custom =&amp;gt; 642 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Many thanks to &lt;a href="https://twitter.com/nicolasgrekas"&gt;Nicolas Grekas&lt;/a&gt; for reviewing this English translation.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Here is the code used for the benchmark (+ an additional test of the impact on the check in the constructor)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/f2r/4f21279732cc1ba81dddc05ef042a1f5"&gt;https://gist.github.com/f2r/4f21279732cc1ba81dddc05ef042a1f5&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>programming</category>
      <category>performance</category>
    </item>
    <item>
      <title>Performance des UUIDs</title>
      <dc:creator>Frédéric Bouchery</dc:creator>
      <pubDate>Fri, 20 Mar 2020 16:54:45 +0000</pubDate>
      <link>https://forem.com/fredbouchery/performance-des-uuids-29dj</link>
      <guid>https://forem.com/fredbouchery/performance-des-uuids-29dj</guid>
      <description>&lt;p&gt;(&lt;a href="https://dev.to/fredbouchery/performance-of-uuids-3i40"&gt;English version&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Dernièrement, en analysant les performances d'une application au moyen de l'excellent outil &lt;a href="https://blackfire.io/"&gt;blackfire&lt;/a&gt;, nous avons constaté un temps non-négligeable passé à transformer des UUIDs binaires en chaînes et vice-versa.&lt;/p&gt;

&lt;p&gt;Il faut dire que le stockage des UUIDs dans la base de données est au format binaire, et l'application disposant d'une API Rest, les UUIDS sont donc tout le temps convertis en chaîne de caractères sous la forme &lt;code&gt;"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx"&lt;/code&gt;, que ce soit pour retourner la réponse en JSON ou pour traiter des requêtes avec des UUIDs.&lt;/p&gt;

&lt;p&gt;Comme beaucoup, les développeurs de l'application se sont tournés vers le paquet "&lt;a href="https://github.com/ramsey/uuid"&gt;Ramsey/UUID&lt;/a&gt;", qui semble être la solution majoritairement exploitée dans le cadre de la manipulation d'UUIDs. L'excellente réputation de ce composant, nous a conduit à chercher des solutions d'optimisation dans l'application, sans remettre en doute la pertinence de Ramsey/UUID.&lt;/p&gt;

&lt;p&gt;Récemment, à la lecture de l'article de Jolicode "&lt;a href="https://jolicode.com/blog/uuid-generation-in-php"&gt;UUID generation in PHP&lt;/a&gt;" et du débat autour d'une pull request sur &lt;a href="https://twitter.com/matthieunapoli/status/1238541015381835778"&gt;Symfony UID&lt;/a&gt;, nous nous sommes penchés un peu plus sur la façon dont l'application manipule les UUIDs, car comme le dit &lt;a href="https://jolicode.com/equipe/gregoire-pineau"&gt;Grégoire Pineau&lt;/a&gt;, si le polyfill de Symfony est aussi performant, il faudrait peut-être remettre en cause l'utilisation de Ramsey/UUID.&lt;/p&gt;

&lt;p&gt;Seulement, quand on fait quelques statistiques sur l'application, on passe surtout beaucoup de temps à coder/décoder les UUIDs plutôt qu'à en générer, et ça doit être le cas de beaucoup d'applications.&lt;/p&gt;

&lt;p&gt;Mais dans ce cas, le benchmark de Jolicode est-il toujours pertinent, dans la mesure où celui-ci fait une comparaison sur la génération d'UUID ?&lt;/p&gt;

&lt;p&gt;Nous avons donc fait d'autres benchmarks, mais cette fois-ci, en comparant une opération qui converti un UUID binaire en chaîne et une chaîne en binaire.&lt;/p&gt;

&lt;p&gt;Avec Ramsey/UUID, cela est fait quand on crée un objet Uuid depuis un binaire, puis que l'on retransforme cet objet en binaire :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Ramsey\Uuid\Uuid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getBytes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous avons créé une classe &lt;code&gt;Uuid&lt;/code&gt; qui utilise l'extension PHP "&lt;a href="https://pecl.php.net/package/uuid"&gt;uuid&lt;/a&gt;", sous la forme d'un &lt;a href="https://en.wikipedia.org/wiki/Value_object"&gt;ValueObject&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Uuid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @var string
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;uuid_is_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Wrong UUID format"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Uuid&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;fromBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$bytes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;uuid_unparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bytes&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getBytes&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;uuid_parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Avant même de benchmarker un &lt;code&gt;Uuid::fromBytes($bytes)-&amp;gt;getBytes()&lt;/code&gt;, on sait déjà que ça sera plus performant que Ramsey car il n'y a pas d'appel sur des factories et autres codecs. De plus, en interne, Ramsey conserve l'UUID sous une forme décomposée en plusieurs champs, alors que dans notre code précédent, l'UUID est mémorisé sous sa forme chaîne de caractères.&lt;/p&gt;

&lt;p&gt;Nous avons du coup regardé comment était réalisé le &lt;a href="https://github.com/symfony/polyfill-uuid"&gt;polyfill Symfony&lt;/a&gt; qui permet de remplacer les fonctions de l'extension UUID quand elles n'existent pas, et nous nous sommes dit qu'il était possible de l'optimiser, car un traitement un peu gourmand était fait lors du uuid_unparse pour valider la structure de l'UUID.&lt;/p&gt;

&lt;p&gt;Voici comment nous pourrions réaliser cette optimisation :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Uuid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @var string
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'`^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$`Di'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Wrong UUID format"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Uuid&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$other&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;fromBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$bytes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Invalid binary UUID. Length is not 16 bytes"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$hex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;substr_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;bin2hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bytes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$hex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;substr_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$hex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;substr_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;substr_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getBytes&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;hex2bin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le temps d’exécution pour 1 million d'itérations nous donne :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ramsey =&amp;gt; 2583 ms
ext-uuid =&amp;gt; 1260 ms
polyfill-symfony =&amp;gt; 4020 ms
custom =&amp;gt; 642 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(l'environnement n'a pas beaucoup d'importance, sachez juste que le test a été fait sur Ubuntu 19.10 avec PHP 7.3.11)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N0Eoj0H---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/q0urzzy1j0209lok217g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N0Eoj0H---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/q0urzzy1j0209lok217g.jpg" alt="What ?" width="640" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nous avons ré-exécuté ce benchmark plusieurs fois pour en être sûr, mais oui, le code est deux fois plus rapide que l'extension UUID !&lt;/p&gt;

&lt;p&gt;Quant à la génération des UUIDs, comme c'est une opération peu courante pour nous, nous avons continué à utiliser Ramsey pour cela.&lt;/p&gt;

&lt;p&gt;Dans notre contexte, on s'aperçoit que Ramsey/UUID n'est pas forcement pertinent, mais ce qui est le plus surprenant, c'est que notre code PHP est plus performant dans l'encodage/décodage d'UUID que l'extension.&lt;br&gt;
Donc, si vous n'avez pas cette extension, ce n'est pas bien grave, car elle est moins performante que sa version en PHP.&lt;/p&gt;

&lt;p&gt;Update: Depuis l'écriture de cet article, &lt;a href="https://twitter.com/nicolasgrekas"&gt;Nicolas Grekas&lt;/a&gt; a soumis une &lt;a href="https://github.com/symfony/polyfill/pull/244"&gt;PR&lt;/a&gt; pour optimiser le polyfill de Symfony.&lt;br&gt;
Voici le benchmark avec le résultat de sa PR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ramsey =&amp;gt; 2583 ms
ext-uuid =&amp;gt; 1260 ms
polyfill-symfony =&amp;gt; 4020 ms
polyfill-symfony-optim =&amp;gt; 1145 ms
custom =&amp;gt; 642 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Voici le code utilisé pour le benchmark (+ un test complémentaire de l'impact sur le check à la construction)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/f2r/4f21279732cc1ba81dddc05ef042a1f5"&gt;https://gist.github.com/f2r/4f21279732cc1ba81dddc05ef042a1f5&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>programming</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
