<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet href="https://jolicode.com/feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xml:lang="fr-FR">
    <id>https://jolicode.com/blog</id>
    <link type="text/html" rel="alternate" href="https://jolicode.com/blog"/>
    <link type="application/rss+xml" rel="self" href="https://jolicode.com/feed" />

            <title>JoliCode blog - les derniers articles</title>
        <updated>2025-02-05T14:10:18+01:00</updated>    <entry>
        <id>https://jolicode.com/blog/notre-outillage-front-end-en-2025</id>
        <published>2025-02-04T10:42:00+01:00</published>
        <updated>2025-02-04T10:42:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/notre-outillage-front-end-en-2025"/>
        <title>Notre outillage front-end en 2025</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="developpement" />            <category term="frontend" />        <summary><![CDATA[Nous sommes désormais 3 intégrateurs et intégratrices chez JoliCode. Afin d’harmoniser nos façons de faire, nous avons décidé de créer un projet vide, une sorte de « front-end starter » qui nous permettrait…]]></summary>
        <content type="html">
            &lt;p&gt;Nous sommes désormais 3 intégrateurs et intégratrices chez JoliCode. Afin d’harmoniser nos façons de faire, nous avons décidé de créer un projet vide, une sorte de « front-end starter » qui nous permettrait de démarrer une intégration. Il se base sur Symfony, le framework pour lequel nous sommes experts et intègre l’ensemble des outils, librairies et configurations que nous souhaitons utiliser côté front-end.&lt;/p&gt;
&lt;p&gt;Voici un tour d’horizon de notre front-end starter et des différents choix que nous avons fait pour optimiser la qualité de nos intégrations.&lt;/p&gt;
&lt;h2&gt;Gestionnaire de paquets : passage à pnpm&lt;/h2&gt;
&lt;p&gt;Face à l’engouement autour de pnpm (pour &lt;em&gt;&lt;strong&gt;P&lt;/strong&gt;erformant npm&lt;/em&gt;), nous nous sommes intéressés à ce gestionnaire de dépendances dont la promesse est d’être beaucoup plus rapide et performant que ses concurrents. Il repose sur le principe de liens symboliques afin de partager les dépendances entre plusieurs projets à un seul endroit. Concrètement, si une même librairie est utilisée sur deux projets différents, celle-ci ne sera stockée qu’à un endroit et le dossier &lt;code&gt;node_modules&lt;/code&gt; contiendra un lien symbolique vers cette ressource. On peut enfin se débarrasser de ce dossier toujours plus lourd que l’on se coltine sur chaque projet.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; lang=&quot;en&quot; src=&quot;https://jolicode.com//media/original/2025/frontend-starter/heaviest-objects.webp&quot; alt=&quot;Heaviest objects in the universe : Sun, Neutron star, Black hole, node_modules&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Avec sa version 4, Yarn a rattrapé son retard avec sa stratégie &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://yarnpkg.com/features/pnp/#initializing-pnp&quot;&gt;Plug’n’Play&lt;/a&gt;. Ayant expérimenté pnpm au préalable, nous avons toutefois décidé de rester sur cet outil. Il a, pour le moment, répondu à nos attentes en nous offrant un confort de développement supplémentaire grâce à sa vitesse.&lt;/p&gt;
&lt;h2&gt;Maintien des standards avec les linters&lt;/h2&gt;
&lt;p&gt;L’utilisation de linters nous permet d’assurer une cohérence dans le code de nos projets. Le fait de l’intégrer à notre starter garantit le partage des standards à l’ensemble de ceux-ci. Nous ne faisons pas dans l’originalité à ce niveau-là puisque nous utilisons &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://eslint.org/&quot;&gt;eslint&lt;/a&gt; pour le code JavaScript et &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://stylelint.io/&quot;&gt;stylelint&lt;/a&gt; pour le css.&lt;/p&gt;
&lt;p&gt;Pas de configuration vraiment particulière pour eslint, nous nous basons sur &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.npmjs.com/package/@eslint/js&quot;&gt;@eslint/js&lt;/a&gt; et utilisons la configuration &lt;code&gt;recommended&lt;/code&gt;. Pour stylelint, en plus de la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/stylelint/stylelint-config-standard&quot;&gt;configuration standard&lt;/a&gt;, nous nous assurons de respecter un certain ordre pour nos règles CSS en surchargeant quelques règles comme &lt;code&gt;selector-class-pattern&lt;/code&gt;  pour nous permettre d’utiliser la notation BEM. Dans les deux cas, nous utilisons &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://prettier.io/&quot;&gt;prettier&lt;/a&gt; pour formater plus facilement notre code selon les règles définies.&lt;/p&gt;
&lt;h2&gt;Les outils côté CSS&lt;/h2&gt;
&lt;h3&gt;La fin de SASS&lt;/h3&gt;
&lt;p&gt;Comme nous en avions parlé dans un &lt;a href=&quot;https://jolicode.com/blog/passer-a-postcss-pour-un-projet-sans-sass&quot;&gt;précédent article&lt;/a&gt;, nous avons décidé de ne plus utiliser le préprocesseur SASS et de nous baser uniquement sur PostCSS. Agrémenté de plugins, il nous permet de retrouver nos automatismes avec l’utilisation d’un préprocesseur (imbrication, mixins…), en supprimant toutefois un outil de notre stack. Nous avons évidemment ajouté le plugin &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/postcss/autoprefixer&quot;&gt;autoprefixer&lt;/a&gt; afin de gérer automatiquement l’ajout de préfixes CSS.&lt;/p&gt;
&lt;h3&gt;Tailwind&lt;/h3&gt;
&lt;p&gt;Côté framework, nous n’avons pas échappé à la tendance Tailwind. Nous avons décidé de l’intégrer à nos projets, mais pas n’importe comment. Nous utilisons une approche &amp;quot;hybride&amp;quot; : nous n’avons pas supprimé l’utilisation de feuilles de style, nous créons toujours des composants et des classes sémantiques. Cependant, nous les agrémentons des classes utilitaires de tailwind pour nous simplifier la vie. Concrètement, nous créons une classe &lt;code&gt;.btn&lt;/code&gt; pour nos boutons. Mais si nous souhaitons ajouter une marge à un bouton en particulier, nous pouvons le faire simplement avec les classes de tailwind. Jonathan a présenté une &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=NG-6aL4LClc&quot;&gt;conférence&lt;/a&gt; à Codeurs en Seine pour expliquer notre approche et comment nous l’implémentons.&lt;/p&gt;
&lt;p&gt;Nous utilisons actuellement la version 3 du framework mais la dernière version étant sortie cette semaine, nous prévoyons une mise à niveau très rapidement.&lt;/p&gt;
&lt;h2&gt;L’écosystème Symfony UX&lt;/h2&gt;
&lt;h3&gt;Stimulus&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://ux.symfony.com/&quot;&gt;Symfony UX&lt;/a&gt; est un ensemble de librairies permettant d’intégrer des outils JavaScript dans nos applications. L’une des librairies principales de Symfony UX est Stimulus, un framework JavaScript minimaliste. C’est une librairie que j’affectionne particulièrement à titre personnel et que j’ai commencé à utiliser il y a plusieurs années au sein de projets Ruby on rails. Elle permet d’organiser son code JavaScript en controlleurs et fonctionne directement sur du HTML (Twig) existant. Pour nos projets sans React, l’utilisation de Stimulus nous permet donc d’avoir un code propre et organisé malgré tout, mais aussi de gagner du temps.&lt;/p&gt;
&lt;p&gt;Nous l’avons agrémenté de &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/stimulus-use/stimulus-use&quot;&gt;Stimulus Use&lt;/a&gt; qui permet d’utiliser de nouvelles fonctions de cycle de vie au sein d’un controlleur (comme appear, mouseEnter, ou resize).&lt;/p&gt;
&lt;h3&gt;Les composants Twig&lt;/h3&gt;
&lt;p&gt;Au sein de Symfony UX, les &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://ux.symfony.com/twig-component&quot;&gt;Twig Components&lt;/a&gt; nous permettent de séparer nos éléments en composants réutilisables. Un composant est constitué le plus souvent d’une classe PHP et d’un template Twig associé (il est également possible de créer des composants anonymes qui ne sont constitués que d’un template). Cela nous permet de créer un template pour chaque élément réutilisable (un bouton, une modale…). Nous avions l’habitude d’utiliser le tag &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://twig.symfony.com/doc/3.x/tags/embed.html&quot;&gt;embed&lt;/a&gt; sur certains projets pour reproduire ce principe de composants Twig mais cela produisait du code peu lisible.&lt;/p&gt;
&lt;p&gt;Pour ajouter de l’interactivité à nos composants, nous avons également choisi d’inclure la librairie &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://ux.symfony.com/live-component&quot;&gt;Live Components&lt;/a&gt;. Elle permet de mettre à jour un composant en Ajax lorsque l’utilisateur interagit avec, le tout sans écrire une ligne de JavaScript. C’est une librairie bien pratique dans le cas de composants interactifs, qu’on doit mettre à jour sans rechargement de page et qui nous fait gagner beaucoup de temps.&lt;/p&gt;
&lt;p&gt;Certaines voix au sein de Jolicode s’élèvent &lt;a href=&quot;https://jolicode.com/blog/making-a-single-page-application-with-htmx-and-symfony&quot;&gt;pour l’utilisation d’HTMX&lt;/a&gt;. Nous avons privilégié pour l’instant l’utilisation des outils officiels recommandés par Symfony mais il n’est pas exclu que nous étudions une autre possibilité. Sur un projet récent les Live Components nous ont semblé en effet un peu difficiles à prendre en main et pas forcément adaptés pour répondre à toutes les situations.&lt;/p&gt;
&lt;p&gt;Enfin, pour présenter nos composants nous avons décidé d’intégrer Storybook grâce au &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/sensiolabs/StorybookBundle&quot;&gt;bundle de SensioLabs&lt;/a&gt;. Nous pouvons ainsi visualiser nos différents composants et leurs variantes facilement et ainsi constituer un design system. Auparavant, le design system était un simple template dans lequel nous ajoutions, à la main, les différents composants d’un site. Storybook nous offre une interface complète pour documenter nos composants front.&lt;/p&gt;
&lt;h2&gt;Gestion des assets&lt;/h2&gt;
&lt;p&gt;Pour faire fonctionner tout ce petit monde et gérer nos assets, nous utilisons Webpack intégré dans &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://symfony.com/doc/current/frontend/encore/index.html&quot;&gt;Webpack Encore&lt;/a&gt;. Nous avons expérimenté l’utilisation d’&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://symfony.com/doc/current/frontend/asset_mapper.html&quot;&gt;Asset Mapper&lt;/a&gt; mais malheureusement, au vu des outils que nous utilisons actuellement, nous ne sommes pas prêts à nous passer d’un bundler. Rien de révolutionnaire concernant sa configuration, nous y ajoutons nos linters, Stimulus, PostCSS et de quoi optimiser nos images.&lt;/p&gt;
&lt;p&gt;Côté JavaScript enfin, nous ajoutons la librairie &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://babeljs.io/&quot;&gt;Babel&lt;/a&gt; pour s’assurer de la compatibilité de notre code.&lt;/p&gt;
&lt;h2&gt;Quelques librairies JavaScript utiles&lt;/h2&gt;
&lt;p&gt;Nous avons décidé de n’inclure que peu de librairies JavaScript par défaut dans notre projet starter. Le nombre de librairies qui se retrouvent sur la quasi-totalité de nos projets étant peu nombreux. Nous avons inclus &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://a11y-dialog.netlify.app/&quot;&gt;a11y-dialog&lt;/a&gt;, une librairie légère créée par Kitty Giraudel qui permet de créer des modales accessibles. Comme elle ne contient pas de style par défaut, nous avons ajouté une feuille de style toute simple qui gère simplement l’affichage d’une modale avec overlay.&lt;/p&gt;
&lt;p&gt;Enfin nous échappons rarement à la présence de sliders sur les sites que nous implémentons. Sur ce point, nous avons choisi d’intégrer la libraire &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://swiperjs.com/&quot;&gt;Swiper&lt;/a&gt;, l’une des rares à répondre à l’ensemble de nos cas d’usage.&lt;/p&gt;
&lt;h2&gt;Nos outils en image&lt;/h2&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2025/frontend-starter/frontend-starter-schema.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2025/frontend-starter/frontend-starter-schema.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; aria-described-by=&quot;front-end-tools-description&quot; style=&quot;aspect-ratio: calc(986 / 538)&quot; src=&quot;https://jolicode.com//media/original/2025/frontend-starter/frontend-starter-schema.png&quot; alt=&quot;Schéma de nos outils front-end&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;ul class=&quot;sr-only&quot; id=&quot;front-end-tools-description&quot;&gt;
&lt;li&gt;Gestionnaire de dépendances, builder, outils : pnpm, Webpack Encore, eslint, stylelint, prettier.&lt;/li&gt;
&lt;li&gt;Eco-système CSS : PostCSS, autoprefixer, Tailwind&lt;/li&gt;
&lt;li&gt;Eco-système JavaScript : Symfony UX (Stimulus, Stimulus Use, Twig Components, Live Components), Babel&lt;/li&gt;
&lt;li&gt;Librairies JavaScript : a11y-dialog, Swiper&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Et pour la suite ?&lt;/h2&gt;
&lt;p&gt;Ce projet n’est bien sûr pas figé et son utilisation au fil du temps pourrait nous amener à effectuer des ajustements. Nous pourrions challenger l’utilisation des live components et pnpm, ajuster notre configuration PostCSS au cours des avancées de CSS ou bien ajouter un linter Twig.&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/mcp-the-open-protocol-that-turns-llm-chatbots-into-intelligent-agents</id>
        <published>2025-01-30T09:42:00+01:00</published>
        <updated>2025-01-30T09:42:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/mcp-the-open-protocol-that-turns-llm-chatbots-into-intelligent-agents"/>
        <title>MCP: The Open Protocol That Turns LLM Chatbots into Intelligent Agents</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="tool" />            <category term="ia" />            <category term="llm" />            <category term="mcp" />        <summary><![CDATA[LLMs have started to become widely known. They are used to generate text, answer questions, translate texts, and more. These models are becoming increasingly powerful and are employed across diverse fields.…]]></summary>
        <content type="html">
            &lt;p&gt;&lt;abbr title=&quot;Large Language Models&quot;&gt;LLMs&lt;/abbr&gt; have started to become widely known. They are used to generate text, answer questions, translate texts, and more. These models are becoming increasingly powerful and are employed across diverse fields.&lt;/p&gt;
&lt;p&gt;LLMs powers all the fancy IA you use like GPT, BERT, Claude, LLaMA, Deepseek, or Mistral.&lt;/p&gt;
&lt;p&gt;We are reaching a point where LLMs have become smart enough. The next goal is enabling them to perform actions. For example, we want to be able to book a train or order a pizza directly from a chatbot.&lt;/p&gt;
&lt;p&gt;The next step is to transform the LLM into a real assistant, an &lt;strong&gt;agent&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is where &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://modelcontextprotocol.io&quot;&gt;MCP&lt;/a&gt; comes in. It is a protocol that allows a client to interact with a server capable of performing actions. The client sends a request to the server, which executes the action and returns the result.&lt;/p&gt;
&lt;h2&gt;Genesis of the Protocol&lt;/h2&gt;
&lt;p&gt;Some LLMs already support &lt;em&gt;tools&lt;/em&gt;, but these are usually proprietary solutions. MCP aims to standardize how LLMs interact with &lt;em&gt;tools&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;On November 25, 2024, &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.anthropic.com/news/model-context-protocol&quot;&gt;Anthropic announced the release of MCP&lt;/a&gt;. The protocol is open-source and free to use, designed to be simple and easy to implement.&lt;/p&gt;
&lt;p&gt;On the official website, they describe MCP as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;MCP is an open protocol that standardizes how applications provide context to LLMs. Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;How It Works&lt;/h2&gt;
&lt;h3&gt;The Big Picture&lt;/h3&gt;
&lt;p&gt;At its core, MCP follows a client-server architecture where a host application connects to multiple servers:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2025/mcp/mcp-big-picture-2.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2025/mcp/mcp-big-picture-2.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(1016 / 576)&quot; src=&quot;https://jolicode.com//media/original/2025/mcp/mcp-big-picture-2.png&quot; alt=&quot;MCP big picture&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MCP Hosts&lt;/strong&gt;: Programs like Claude Desktop, IDEs, or AI tools that want to access data through MCP. Hosts can run multiple clients;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP Clients&lt;/strong&gt;: Protocol clients that maintain 1:1 connections with servers;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP Servers&lt;/strong&gt;: Lightweight programs exposing specific capabilities through the standardized Model Context Protocol — for example, a pizza delivery API or a train booking service.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For instance, when you use &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://claude.ai/download&quot;&gt;Claude Desktop&lt;/a&gt; &lt;em&gt;(the host)&lt;/em&gt;, you can configure it to interact with your application &lt;em&gt;(the server)&lt;/em&gt;. Claude Desktop may have many &lt;em&gt;clients&lt;/em&gt; interacting with your application.&lt;/p&gt;
&lt;p&gt;In this article, we&#039;ll focus on the server side, where the most interesting things happen because it&#039;s your application — the space for creativity! 🚀&lt;/p&gt;
&lt;h3&gt;Transports&lt;/h3&gt;
&lt;p&gt;How does the client communicate with the server? There are two methods:&lt;/p&gt;
&lt;h4&gt;STDIO&lt;/h4&gt;
&lt;p&gt;The client spawns the server as a child process and communicates through standard input/output (&lt;code&gt;STDIN&lt;/code&gt;/&lt;code&gt;STDOUT&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2025/mcp/mcp-stdio.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2025/mcp/mcp-stdio.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;aspect-ratio: calc(699 / 655)&quot; src=&quot;https://jolicode.com//media/original/2025/mcp/mcp-stdio.png&quot; alt=&quot;MCP - transport - stdio&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h4&gt;HTTP with Server-Sent Events&lt;/h4&gt;
&lt;p&gt;The client connects to the server via HTTP, and the server sends events back:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2025/mcp/mcp-http.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2025/mcp/mcp-http.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;aspect-ratio: calc(578 / 669)&quot; src=&quot;https://jolicode.com//media/original/2025/mcp/mcp-http.png&quot; alt=&quot;MCP - transport - HTTP&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h3&gt;Messaging&lt;/h3&gt;
&lt;p&gt;The client-server connection is &lt;strong&gt;stateful and persistent&lt;/strong&gt;. While this approach might not align with serverless architecture, it enables interesting use cases.&lt;/p&gt;
&lt;p&gt;Once the &lt;em&gt;client&lt;/em&gt; connects to the &lt;em&gt;server&lt;/em&gt;, it can send &lt;em&gt;requests&lt;/em&gt;. The &lt;em&gt;server&lt;/em&gt; processes the &lt;em&gt;request&lt;/em&gt; and sends back a &lt;em&gt;response&lt;/em&gt;. The client may then send another request. Additionally, the server can send &lt;em&gt;notifications&lt;/em&gt;, maintaining the persistent connection.&lt;/p&gt;
&lt;p&gt;Messages follow the &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.jsonrpc.org/specification&quot;&gt;JSON-RPC 2.0&lt;/a&gt; standard:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;--&gt; {&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;&quot;jsonrpc&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;&quot;method&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;subtract&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;&quot;params&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;], &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;&amp;#x3C;-- {&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;&quot;jsonrpc&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;details&gt;
&lt;summary&gt;Learn more about types of messages ⬇&lt;/summary&gt;
&lt;h3&gt;Types of Messages&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Request&lt;/strong&gt;: The client sends a request to the server.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    jsonrpc: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    id: string &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; number;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    method: string;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    params&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;?:&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        [key: string]: unknown;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Response&lt;/strong&gt;: The server sends a response to the client.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    jsonrpc: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    id: string &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; number;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    result&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;?:&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        [key: string]: unknown;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    error&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;?:&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        code: number;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        message: string;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        data&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;?:&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; unknown;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Notification&lt;/strong&gt;: The server pushes a notification to the client.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    jsonrpc: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    method: string;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    params&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;?:&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        [key: string]: unknown;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/details&gt;
&lt;h3&gt;Lifecycle&lt;/h3&gt;
&lt;p&gt;The connection lifecycle:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2025/mcp/mcp-lifecycle.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2025/mcp/mcp-lifecycle.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;aspect-ratio: calc(519 / 814)&quot; src=&quot;https://jolicode.com//media/original/2025/mcp/mcp-lifecycle.png&quot; alt=&quot;MCP - transport - lifecycle&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h4&gt;Initialization Phase&lt;/h4&gt;
&lt;p&gt;During this phase:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Protocol version compatibility is established;&lt;/li&gt;
&lt;li&gt;Capabilities are exchanged and negotiated;&lt;/li&gt;
&lt;li&gt;Implementation details are shared.&lt;/li&gt;
&lt;/ul&gt;
&lt;details&gt;
&lt;summary&gt;Learn more about request / response shape ⬇&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;Client &lt;code&gt;initialize&lt;/code&gt; request example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;jsonrpc&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;id&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;method&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;initialize&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;params&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;protocolVersion&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;2024-11-05&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;capabilities&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;roots&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;listChanged&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;sampling&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;clientInfo&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;name&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;ExampleClient&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;version&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Server &lt;code&gt;initialize&lt;/code&gt; response example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;jsonrpc&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;id&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;result&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;protocolVersion&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;2024-11-05&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;capabilities&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;logging&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;prompts&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;listChanged&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;resources&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;subscribe&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;listChanged&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;tools&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;listChanged&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;serverInfo&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;name&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;ExampleServer&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;version&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h4&gt;Operation Phase&lt;/h4&gt;
&lt;p&gt;During this phase, the client executes methods exposed by the server. The server may also send notifications.&lt;/p&gt;
&lt;h4&gt;Shutdown&lt;/h4&gt;
&lt;p&gt;The client can disconnect, closing the connection.&lt;/p&gt;
&lt;h3&gt;What Can a Server Do?&lt;/h3&gt;
&lt;p&gt;Servers provide the foundational building blocks for adding context to language models via MCP. These primitives enable rich interactions between clients, servers, and language models:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prompts&lt;/strong&gt;: Predefined templates or instructions that guide language model interactions. These are initiated by the user. Examples include slash commands and menu options;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resources&lt;/strong&gt;: Structured data or content that provides additional context to the model. Examples include file contents and git history;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tools&lt;/strong&gt;: Executable functions that allow models to perform actions or retrieve information.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Detailing all of these would make the article too lengthy. Hence, we’ll focus on &lt;strong&gt;Tools&lt;/strong&gt;, the most intriguing and powerful aspect of the protocol.&lt;/p&gt;
&lt;h2&gt;How to Implement Tools in an MCP Server&lt;/h2&gt;
&lt;p&gt;At JoliCode, we primarily use PHP and Symfony, so we’ll demonstrate how to implement an MCP server in PHP using Symfony. For simplicity, we’ll utilize the stdio transport.&lt;/p&gt;
&lt;p&gt;Our server will support two actions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Listing all current Symfony commands;&lt;/li&gt;
&lt;li&gt;Executing a Symfony command.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As of now, no official PHP library exists for MCP. We attempted to use &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/logiscape/mcp-sdk-php&quot;&gt;logiscape/mcp-sdk-php&lt;/a&gt; but without success. Therefore, we’ll implement the protocol ourselves.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This is a proof of concept (POC) and not production-ready code ⚠&lt;/p&gt;
&lt;p&gt;We’ll use a Symfony command to start the server:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;App\Command&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Attribute\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AsCommand&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Command\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Command&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Input\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ArrayInput&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Input\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;InputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Output\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;BufferedOutput&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Output\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;OutputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[AsCommand(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    name: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;mcp&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    description: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Starts an MCP server&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;McpCommand&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;Command&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    protected&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; execute&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;InputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $input, &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;OutputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $output)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Reading Input from STDIN&lt;/h3&gt;
&lt;p&gt;The first step is to read the content from STDIN, buffer it, and decode it:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;protected&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; execute&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;InputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $input, &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;OutputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $output)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $buffer &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    while&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $line &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; fgets&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;STDIN&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; ===&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $line) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;            usleep&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            continue&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $buffer &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $line;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;str_contains&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($buffer, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $lines &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; explode&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $buffer);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $buffer &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; array_pop&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($lines);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($lines &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $line) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;                $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;processLine&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($output, $line);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    return&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Command&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SUCCESS&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Processing the Input&lt;/h3&gt;
&lt;p&gt;We decode the JSON payload, invoke the appropriate method, and return the response:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; processLine&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;OutputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $output, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $line)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    try&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $payload &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; json_decode&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($line, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;JSON_THROW_ON_ERROR&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $method &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $payload[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;method&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $response &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; match&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($method) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;initialize&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;sendInitialize&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;tools/list&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;sendToolsList&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;tools/call&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;callTool&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($payload[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;params&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;notifications/initialized&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            default&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;sendProtocolError&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Method &quot;%s&quot; not found&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $method)),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    } &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Throwable&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $response &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;sendApplicationError&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($e);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$response) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $response[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;id&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $payload[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;id&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $response[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;jsonrpc&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;2.0&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $output&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;writeln&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;json_encode&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($response));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Listing Tools&lt;/h3&gt;
&lt;p&gt;Here is the &lt;code&gt;tools/list&lt;/code&gt; method, which provides a list of available tools:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; sendToolsList&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;result&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;tools&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                    &#039;name&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;list_commands&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                    &#039;description&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;List all Symfony commands.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                    &#039;inputSchema&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                        &#039;type&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;object&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                        &#039;$schema&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;http://json-schema.org/draft-07/schema#&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                    ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                    &#039;name&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;call_command&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                    &#039;description&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;Call a Symfony command.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                    &#039;inputSchema&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                        &#039;type&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;object&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                        &#039;properties&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                            &#039;command_name&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;type&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;string&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                        ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                        &#039;required&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;command_name&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                        &#039;additionalProperties&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                        &#039;$schema&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;http://json-schema.org/draft-07/schema#&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                    ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we have two tools: &lt;code&gt;list_commands&lt;/code&gt; and &lt;code&gt;call_command&lt;/code&gt;. The &lt;code&gt;inputSchema&lt;/code&gt; is a JSON schema that describes the tool&#039;s input. It&#039;ll document how the LLM can use it.&lt;/p&gt;
&lt;p&gt;The more descriptive you are, the better it is for the LLM! It&#039;s like the SEO 😎! (Here, descriptions are really too shorts 🙈)&lt;/p&gt;
&lt;p&gt;You can add as many tools as you want. By the way, instead of these two tools, we could have added one tool per Symfony command (just noticed that while writing this article 🤯). I think it would have been better, because we could have typed all command arguments / options. Anyway, let&#039;s continue.&lt;/p&gt;
&lt;p&gt;Typically, the request would be:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;jsonrpc&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;id&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;method&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;tools/list&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;params&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the response would be:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;result&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;tools&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;name&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;list_commands&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;description&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;List all symfony commands.&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;inputSchema&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;          &quot;type&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;          &quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;name&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;call_command&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;description&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;Call a symfony command.&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;inputSchema&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;          &quot;type&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;          &quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;            &quot;command_name&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;              &quot;type&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;          },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;          &quot;required&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-15&quot;&gt;            &quot;command_name&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;          ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;          &quot;additionalProperties&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;          &quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;http://json-schema.org/draft-07/schema#&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;id&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;jsonrpc&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Handling Tool Requests&lt;/h3&gt;
&lt;p&gt;Next, we define how to handle a tool call:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; callTool&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $params)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $name &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $params[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;name&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $arguments &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $params[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;arguments&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    return&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; match&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($name) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;list_commands&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;  $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;callCommand&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;list&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;--format&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;md&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;call_command&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;callCommand&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($arguments[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;command_name&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        default&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;sendProtocolError&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Tool &quot;%s&quot; not found&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $name)),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; callCommand&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $commandName, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $parameters &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [])&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $command &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getApplication&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($commandName);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $input &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ArrayInput&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;command&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $command, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$parameters]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $output &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; BufferedOutput&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $command&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($input, $output);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;result&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;content&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                    &#039;type&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;text&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                    &#039;text&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $output&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we go, we have implemented the two tools. The &lt;code&gt;list_commands&lt;/code&gt; tool will list all Symfony commands, and the &lt;code&gt;call_command&lt;/code&gt; tool will call a Symfony command.&lt;/p&gt;
&lt;p&gt;If the LLM decided to clear the cache, the request would be:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;jsonrpc&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;id&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;method&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;tools/call&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;params&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;_meta&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;progressToken&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;name&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;call_command&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;arguments&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;command_name&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;cache:clear&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the response would be:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;result&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;content&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;type&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        &quot;text&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt; // Clearing the cache for the dev environment with debug true                  &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt; [OK] Cache for the &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt; environment (debug=true) was successfully cleared.    &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;id&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;jsonrpc&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Demo time!&lt;/h2&gt;
&lt;p&gt;To use our new server, we need to configure it in a host. We&#039;ll use Claude Desktop.&lt;/p&gt;
&lt;p&gt;In Claude Desktop&#039;s configuration, we can add as many servers as we want:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// ~/.config/Claude/claude_desktop_config.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  &quot;mcpServers&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;symfony_app&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;      // We don’t run PHP directly to have better debugging. We’ll talk about it later&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;command&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;/home/gregoire/dev/labs/symfony/mcp/bin/mcp.sh&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // Another server, for example a filesystem server to explore a folder&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    &quot;filesystem&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;command&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-15&quot;&gt;&quot;npx&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;      &quot;args&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-15&quot;&gt;        &quot;-y&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-15&quot;&gt;        &quot;@modelcontextprotocol/server-filesystem&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-15&quot;&gt;        &quot;/home/gregoire/Downloads/video&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you can chat with it like usual:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2025/mcp/demo-list-all-commands.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2025/mcp/demo-list-all-commands.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;aspect-ratio: calc(743 / 774)&quot; src=&quot;https://jolicode.com//media/original/2025/mcp/demo-list-all-commands.png&quot; alt=&quot;MCP - demo - list all commands&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2025/mcp/demo-clear-cache.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2025/mcp/demo-clear-cache.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;aspect-ratio: calc(747 / 613)&quot; src=&quot;https://jolicode.com//media/original/2025/mcp/demo-clear-cache.png&quot; alt=&quot;MCP - demo - clear cache&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2025/mcp/demo-about-2.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2025/mcp/demo-about-2.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;aspect-ratio: calc(744 / 805)&quot; src=&quot;https://jolicode.com//media/original/2025/mcp/demo-about-2.png&quot; alt=&quot;MCP - demo - about&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;Debugging&lt;/h2&gt;
&lt;p&gt;Debugging can be tricky without the right tooling! Here are some tips:&lt;/p&gt;
&lt;p&gt;You can use the &lt;code&gt;stderr&lt;/code&gt; to log errors but this is not useful. You can also use the &lt;code&gt;logging&lt;/code&gt; capability to send logs to the client. But when nothing works, well, logging does not work either.&lt;/p&gt;
&lt;p&gt;I created this small bash wrapper to log everything (&lt;code&gt;STDIN&lt;/code&gt;, &lt;code&gt;STDOUT&lt;/code&gt;, &lt;code&gt;STERR&lt;/code&gt;) to some files to see what&#039;s going on:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;#mcp.sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -e&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -o&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; pipefail&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;BASE&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;syntax-12&quot;&gt; $0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;/..&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; &gt;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$BASE&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;/run.log&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;stdin_log&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$BASE&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;/stdin.log&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;stdout_log&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$BASE&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;/stdout.log&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;stderr_log&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$BASE&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;/stderr.log&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;tee&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -a&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$stdin_log&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  $BASE&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;/bin/console&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; mcp&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; &gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &gt;(&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;tee&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -a&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$stdout_log&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; 2&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &gt;(&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;tee&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -a&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$stderr_log&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot; &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&amp;#x26;2&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Additionally, the &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/modelcontextprotocol/inspector&quot;&gt;MCP Inspector&lt;/a&gt; is invaluable:&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;npx&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; @modelcontextprotocol/inspector&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2025/mcp/inspector.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2025/mcp/inspector.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(1711 / 1319)&quot; src=&quot;https://jolicode.com//media/original/2025/mcp/inspector.png&quot; alt=&quot;MCP inspector&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;Security Considerations&lt;/h2&gt;
&lt;p&gt;Each time Claude Desktop connects to a server to execute a tool, it prompts the user to accept the connection. While this is a great security measure, exposing tools like database management requires caution to avoid unintended consequences.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I do think building an LLM agent is the next big step in generative IA. The agent must be able to exchange information with an external system, and perform some actions with them such as pushing code to GitHub, booking a train, getting the weather forecast, or even controlling your house.&lt;/p&gt;
&lt;p&gt;I also believe in standard and open source, and I really hope this standard will be adopted by all major actors such as OpenAI, Google, etc.&lt;/p&gt;
&lt;p&gt;If all conditions are reunited, we will be able to do amazing things. At some point we could even avoid building a frontend. An API over MCP would be enough.&lt;/p&gt;
&lt;p&gt;But we should not forget about skynet! 😀&lt;/p&gt;
&lt;p&gt;Finally, since there is no official PHP SDK on the market, &lt;strong&gt;we would like to build one. Would it be interesting to try it?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;(Read the &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://gist.github.com/lyrixx/8670cb089b34a7e510cae0eb637ea15b&quot;&gt;full McpCommand code on github.com&lt;/a&gt;)&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/ce-que-vous-devez-savoir-sur-les-chaa-r-nes-de-caracta-res</id>
        <published>2024-12-04T14:42:00+01:00</published>
        <updated>2024-12-04T14:42:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/ce-que-vous-devez-savoir-sur-les-chaa-r-nes-de-caracta-res"/>
        <title>Ce que vous devez savoir sur les chaÃ®nes de caractÃ¨res</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="php" />            <category term="emoji" />            <category term="encodage" />            <category term="utf8" />        <summary><![CDATA[Jouer avec les chaînes de caractères est certainement la première chose que l’on fait en tant que développeur. Qui n’a pas commencé sa carrière de dev par un “Hello World” ? Et pourtant, elles sont encore…]]></summary>
        <content type="html">
            &lt;p&gt;Jouer avec les chaînes de caractères est certainement la première chose que l’on fait en tant que développeur. Qui n’a pas commencé sa carrière de dev par un “Hello World” ? Et pourtant, elles sont encore trop mal connues malgré tous les outils que nous possédons. Nous vous proposons aujourd’hui un récapitulatif de ce qu’est une chaîne de caractères, et des outils fournis par Symfony pour les manipuler.&lt;/p&gt;
&lt;h2&gt;Qu’est ce qu’une chaîne de caractères ? 🧐&lt;/h2&gt;
&lt;p&gt;Tout ce que vous lisez actuellement sont des chaînes de caractères. Tout ce que vous dites et entendez, et bien plus, se traduisent en chaînes de caractères. En revanche, pour l’ordinateur, il ne s’agit que d’une suite de 1 et de 0. Et nous allons nous intéresser à ce qu’il se passe entre les deux.&lt;/p&gt;
&lt;h3&gt;Un peu de vocabulaire…&lt;/h3&gt;
&lt;p&gt;Un &lt;strong&gt;glyphe&lt;/strong&gt; est une représentation visuelle d’un élément d’écriture. Par exemple, &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt; ou &lt;code&gt;µ&lt;/code&gt; sont des glyphes, tout comme &lt;code&gt;~&lt;/code&gt;, &lt;code&gt;n&lt;/code&gt;, &lt;code&gt;ñ&lt;/code&gt; et même les emojis.&lt;/p&gt;
&lt;p&gt;Un &lt;strong&gt;graphème&lt;/strong&gt; est un symbole qui ne peut pas être divisé en unité plus petite, comme &lt;code&gt;~&lt;/code&gt; ou &lt;code&gt;n&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Un &lt;strong&gt;groupe de graphèmes&lt;/strong&gt; rassemble plusieurs graphèmes et peut être vu comme un caractère. Par exemple, &lt;code&gt;ñ&lt;/code&gt; est un groupe de graphèmes : c’est l’association de &lt;code&gt;~&lt;/code&gt; et &lt;code&gt;n&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Au passage, un graphème qui ne peut pas être utilisé seul, comme &lt;code&gt;~&lt;/code&gt;, des accents, ou encore une cédille, est appelé un &lt;strong&gt;diacritique&lt;/strong&gt; (ça ne nous servira à rien ici, mais vous pourrez peut-être briller en société grâce à ça).&lt;/p&gt;
&lt;h3&gt;…et un peu d’histoire&lt;/h3&gt;
&lt;p&gt;Au commencement il y avait le binaire. Certains constructeurs décidèrent qu’une certaine séquence de 1 et de 0 signifiait “a”, mais cela n’était vrai que pour ce constructeur. Et comme au début de l’informatique, un document ne quittait pas le système sur lequel il était créé, cela ne posait pas de problème.&lt;/p&gt;
&lt;p&gt;En 1961, l’&lt;abbr title=&quot;American National Standards Institute&quot;&gt;ANSI&lt;/abbr&gt; se réunit et publie deux ans plus tard l’American Standard Code for Information Interchange, le Standard Américain pour l’Échange d’Information, ou ASCII pour les intimes, que nous retrouvons sur la fameuse table ASCII :&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/strings/USASCII_code_chart.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/strings/USASCII_code_chart.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(1055 / 720)&quot; src=&quot;https://jolicode.com//media/original/2024/strings/USASCII_code_chart.png&quot; alt=&quot;USASCII code chart&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Les lettres étaient encodées sur 7 bits, ce qui offrait les 127 combinaisons ci-dessus. Notons qu’il n’y a en réalité que 95 caractères, les autres étant de la ponctuation, ou des caractères de contrôle. Il y en a même un, EM, pour prévenir s’il n’y a plus de bande magnétique !&lt;/p&gt;
&lt;p&gt;Fort pratique pour les anglophones, ce choix est moins sympathique pour le reste du monde qui écrit avec de nombreux accents et lettres différentes.&lt;/p&gt;
&lt;p&gt;En 1972, les processeurs 8 bits débarquent – merci IBM – et avec eux, la possibilité d’encoder non plus 127 caractères, mais 255 ! Pour rappel, un octet (byte en anglais) est un bloc de 8 bits, et chaque caractère est encodé sur un octet. Et 2 puissance 8, ça donne 256 ! Donc tout le monde fait un peu sa sauce avec les 128 caractères supplémentaires selon ses besoins et de très nombreuses variantes apparaissent.&lt;/p&gt;
&lt;p&gt;Puis dans les années 90, le World Wide Web apparaît et des documents sont échangés à travers le monde… et à travers différents encodages. Puisque tout texte n’est qu’un ensemble de 0 et de 1, il n’y a absolument aucun moyen de deviner l’encodage du texte, ce qui rend vraiment visible toute erreur de décodage.&lt;/p&gt;
&lt;p&gt;Par exemple, le &lt;code&gt;é&lt;/code&gt;, en ASCII, sera encodé &lt;code&gt;11101001&lt;/code&gt; mais en UTF-8, il sera encodé &lt;code&gt;11000011 10101001&lt;/code&gt;. Mais cette chaîne décodée en ASCII donnera… &lt;code&gt;Ã©&lt;/code&gt; ! Ça ne vous rappelle rien ? Eh oui, le &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://upload.wikimedia.org/wikipedia/commons/1/19/Mojibakevector.png&quot;&gt;mojibake&lt;/a&gt; est né.&lt;/p&gt;
&lt;p&gt;En 1991, la norme Unicode V1.0 fait son apparition ! Son nom est tiré de ses trois objectifs :
Universel : correspond aux besoins linguistiques de toutes les langues et dialectes du monde;
Uniforme : encodé sur une longueur fixe pour un accès plus efficace;
Unique : chaque séquence de bits ne correspond qu’à une interprétation en caractères.
Ce standard, encodé sur 16 bits, permet d’encoder 65 536 caractères ! Cette flexibilité le rend vite adopté par de nombreux langages de programmation tels que JavaScript, C, ou Java.&lt;/p&gt;
&lt;h3&gt;Point de code&lt;/h3&gt;
&lt;p&gt;Ou “Code point” en anglais. Il s’agit de la position numérique d’un caractère – glyphe, graphème, ou même diacritique – au sein d’un espace de codage bien défini. Mais il ne s’agit &lt;strong&gt;pas&lt;/strong&gt; de sa valeur hexadécimale une fois encodé. Par exemple, le caractère &lt;code&gt;é&lt;/code&gt; dans Unicode se situe au point de code U+000E9, mais sa valeur hexadécimale une fois encodé est C3A9. Si vous êtes perdus, n’hésitez pas à jouer avec des convertisseurs &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.rapidtables.com/convert/number/ascii-to-binary.html&quot;&gt;texte/binaire&lt;/a&gt; et &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.rapidtables.com/convert/number/binary-to-decimal.html?x=1100001110101001&quot;&gt;binaire/décimal/hexadécimal&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Dans le standard Unicode, les points de code s’écrivent &lt;code&gt;U&lt;/code&gt; (pour Unicode) suivis d’un &lt;code&gt;+&lt;/code&gt; puis d’un numéro hexadécimal. Par exemple, le point de code de &lt;code&gt;ñ&lt;/code&gt; est &lt;code&gt;U+000F1&lt;/code&gt;. Mais &lt;code&gt;~&lt;/code&gt; a aussi un point de code qui est le &lt;code&gt;U+00303&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Wait, what? Le tilde a son propre point de code ?&lt;/p&gt;
&lt;p&gt;Eh oui, il est possible de combiner plusieurs caractères. Par exemple &lt;code&gt;~ (U+00303)&lt;/code&gt; + &lt;code&gt;n (U+0006E)&lt;/code&gt; donnera un caractère composé, &lt;code&gt;ñ&lt;/code&gt;, mais qui sera différent de &lt;code&gt;ñ (U+000F1)&lt;/code&gt;, qui est un caractère à part entière.&lt;/p&gt;
&lt;h3&gt;L’arrivée d’UTF&lt;/h3&gt;
&lt;p&gt;Unicode introduit plusieurs encodages pour ces points de code : d’abord l’&lt;strong&gt;UTF-16&lt;/strong&gt;, sur 16 bits, donc 2 octets. Mais on s’est rendu compte que 16 bits, c’était encore trop peu pour encoder tous les caractères du monde ! &lt;strong&gt;UTF-32&lt;/strong&gt; a donc été créé : un encodage sur 32 bits, donc 4 octets. Mais pour de nombreux caractères, les trois premiers octets n’étaient remplis que de zéros, ce qui crée une énorme perte de place en mémoire.
Arrive alors &lt;strong&gt;UTF-8&lt;/strong&gt; dont chaque caractère peut être encodé sur 1 à 4 octets ! Le concept d’une taille variable est introduit, et cela permet de ne prendre que la taille nécessaire en mémoire.&lt;/p&gt;
&lt;p&gt;À noter : UTF-8 assure une rétrocompatibilité avec l’ASCII pour tous les caractères dont le point de code est avant 128.&lt;/p&gt;
&lt;p&gt;UTF-8 est maintenant largement adopté sur le Web, 98,3% quand j’écris ces lignes, et nous vous encourageons à n’utiliser que lui. Aujourd’hui, 149 186 caractères et environ 245 000 points de code peuvent être assignés sur un espace qui peut contenir jusqu’à 1 114 112 points de code différents.&lt;/p&gt;
&lt;h3&gt;Pour résumer ☕&lt;/h3&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/strings/cafe.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/strings/cafe.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(2408 / 838)&quot; src=&quot;https://jolicode.com//media/original/2024/strings/cafe.png&quot; alt=&quot;Traduction du mot café en graphèmes, glyphes, points de codes, et en différents encodages&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Une chaîne de caractères peut être vue de trois manières :
Sous forme d’un ensemble de graphèmes, de points de code ou d’octets.&lt;/p&gt;
&lt;h2&gt;L’approche du composant String de Symfony 🧩&lt;/h2&gt;
&lt;p&gt;Afin de pouvoir gérer ces trois différents aspects, le composant String nous propose de créer une chaîne de trois manières différentes :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; UnicodeString&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Å&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);    	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// ou u(&#039;Å&#039;)	// graphèmes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; CodePointString&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Å&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);                	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// points de code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ByteString&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Å&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);    	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// ou b(&#039;Å&#039;)	// octets&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://symfony.com/doc/current/string.html#method-reference&quot;&gt;documentation de ce composant&lt;/a&gt; est très complète, n’hésitez pas à la consulter pour découvrir toutes les manipulations qu’il est possible d’effectuer avec le composant string !&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$text &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; UnicodeString&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;This is a déjà-vu situation.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;	-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;trimEnd&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;	-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;déjà-vu&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;jamais-vu&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;	-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;!&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// $text = &#039;This is a jamais-vu situation!&#039;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;foo BAR&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;upper&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(); &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;FOO BAR&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;FOO Bar&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;lower&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();  &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;foo bar&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Die O&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\&#039;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;Brian Straße&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;folded&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(); &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &quot;die o&#039;brian strasse&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Foo: Bar-baz.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;camel&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(); &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;fooBarBaz&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Foo: Bar-baz.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;snake&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(); &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;foo_bar_baz&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Foo: Bar-baz.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;camel&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(); &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;FooBarBaz&#039;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;abc&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;B&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);           	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;abc&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;ignoreCase&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;B&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;); &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// 1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;hello&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;world&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);  	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;helloworld&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;hello&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039; &#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;world&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;); &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;hello world&#039;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;User&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;ensureEnd&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Controller&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);       	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;UserController&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;UserController&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;ensureEnd&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Controller&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;); &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;UserController&#039;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039; Lorem Ipsum &#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;padBoth&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;-&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;--- Lorem Ipsum ----&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;_.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);			&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;_._._._._._._._._._.&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;   Lorem Ipsum   &#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();			&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;Lorem Ipsum&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;http://symfony.com&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;http://&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;https://&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;https://symfony.com&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Symfony is great&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;); 		&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;Symfony&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;template_name.html.twig&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// [&#039;template_name&#039;, &#039;html&#039;, &#039;twig&#039;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Certaines méthodes sont spécifiques à l’objet ByteString :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// returns TRUE if the string contents are valid UTF-8 contents&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Lorem Ipsum&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;isUtf8&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\xc3\x28&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;isUtf8&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;D’autres sont spécifiques aux objets CodePointString et UnicodeString, comme récupérer un point de code positionné à une certaine position dans la chaîne, ou convertir du texte en ascii :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;नमस्ते&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;codePointsAt&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;); &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// न [2344]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;नमस्ते&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;codePointsAt&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;); &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// म [2350]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;नमस्ते&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;codePointsAt&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;); &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// स्ते [2360, 2381, 2340, 2375]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;नमस्ते&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;ascii&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;namaste&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;さよなら&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;ascii&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;sayonara&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;спасибо&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;ascii&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;spasibo&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;🦄&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;ascii&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();		&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &#039;?&#039;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La méthode &lt;code&gt;-&amp;gt;ascii()&lt;/code&gt; utilise la translittération – dont nous parlerons juste après – mais si vous avez besoin d’un slugger, utilisez plutôt ceci :&lt;/p&gt;
&lt;h3&gt;AsciiSlugger&lt;/h3&gt;
&lt;p&gt;L’AsciiSlugger de Symfony est un outil très utile qui permet simplement de transformer n’importe quelle chaîne en chaîne valide pour des URLs ou encore des noms de fichiers. Il s’utilise comme ceci :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$slugger &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; AsciiSlugger&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$slug &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $slugger&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Wôrķšƥáçè ~~sèťtïñğš~~&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// $slug = &#039;Workspace/settings&#039;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mais si vous voulez un peu plus de paillettes dans votre code, vous pouvez transformer ce slugger en EmojiSlugger ! Ajoutez simplement l’option &lt;code&gt;-&amp;gt;withEmoji()&lt;/code&gt;, choisissez une locale, et amusez-vous :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$slugger &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $slugger→withEmoji();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$slug &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $slugger→slug(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;a 😺, and a 🦁 go to 🏞️&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;-&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;en&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// $slug = &#039;a-grinning-cat-and-a-lion-go-to-national-park&#039;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$slug &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $slugger→slug(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;un 😺, et un 🦁 vont au 🏞️&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;-&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;fr&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// $slug = &#039;un-chat-qui-sourit-et-un-tete-de-lion-vont-au-parc-national&#039;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Le concept de translittération 🌎&lt;/h2&gt;
&lt;p&gt;Avec l’arrivée d’internet, la question de l’internationalisation s’est rapidement posée. Tous les claviers ne possèdent pas les mêmes caractères, et pourtant, lorsque je recherche des artistes comme Eivør ou Rebūke dans ma playlist Spotify, je m’attends à les trouver en tapant simplement “Eivor” ou “Rebuke”. Convertir une chaîne en une autre similaire visuellement ou phonétiquement, c’est ce qu’on appelle la translittération.&lt;/p&gt;
&lt;p&gt;Cette opération est particulièrement pratique avant d’effectuer un tri ou une opération d’indexation. En PHP natif, elle s’effectue comme ceci :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;transliterator_transliterate&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Any-Latin; Latin-ASCII; Lower()&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;A æ Übérmensch på høyeste nivå! И я люблю PHP! ﬁ&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &quot;a ae ubermensch pa hoyeste niva! i a lublu php! fi&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le premier argument est une série d’instructions séparées par un point virgule. Ici on demande à traduire par n’importe quel caractère latin, en ASCII, et on passe tout en minuscules.&lt;/p&gt;
&lt;p&gt;Le site decodeunicode.org, que j’apprécie beaucoup pour la quantité d’informations qu’il fournit, indique parfois les autres glyphes qui entrent dans la composition d’un graphème. Plus tôt, je vous parlais de &lt;code&gt;ñ&lt;/code&gt;; &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://decodeunicode.org/en/u+000F1&quot;&gt;sa page sur le site&lt;/a&gt; indique, entre autres, sa décomposition, c’est-à-dire les autres caractères qui le composent.&lt;/p&gt;
&lt;h3&gt;Homoglyphes&lt;/h3&gt;
&lt;p&gt;J’en profite pour aborder le concept d’homoglyphes : il s’agit de caractères particulièrement similaires visuellement ! Ceux-ci sont aussi visibles sur le site de decodeunicode. Le principe de la &lt;strong&gt;normalisation&lt;/strong&gt; de &lt;strong&gt;composition&lt;/strong&gt; ou de &lt;strong&gt;décomposition&lt;/strong&gt; (passer de &lt;code&gt;ñ&lt;/code&gt; à la combinaison de &lt;code&gt;n&lt;/code&gt; et &lt;code&gt;~&lt;/code&gt; et vice versa) permet de transformer des homoglyphes en caractères par défaut.&lt;/p&gt;
&lt;p&gt;Cela a laissé la place à une faille de sécurité survenue chez Github il y a quelques années : il était possible de voler le compte de quelqu’un simplement en utilisant adresse mail visuellement similaire à la sienne ! Cela était dû au fait que l’adresse mail entrée dans le champ “mot de passe oublié” était normalisée avant d’être recherchée dans la base de données. Ainsi, pour attaquer &lt;code&gt;mike@example.org&lt;/code&gt;, il suffisait d’entrer &lt;code&gt;ｍｉᏦᎬ@example.org&lt;/code&gt; dans le champ. Le compte de &lt;code&gt;mike&lt;/code&gt; était bien retrouvé, mais le token de récupération de mot de passe était ensuite généré et envoyé à &lt;code&gt;ｍｉᏦᎬ&lt;/code&gt; ! L’attaquant n’avait alors plus qu’à se connecter avec le compte de &lt;code&gt;mike&lt;/code&gt; et son nouveau mot de passe. Alors soyez vigilant.e.s avant d’utiliser la normalisation à tout va !&lt;/p&gt;
&lt;p&gt;Si votre base de données est encodée en UTF-8, alors ce problème ne devrait pas se poser.&lt;/p&gt;
&lt;p&gt;Un autre exemple d’attaque par homoglyphe est celui d’utilisation de noms de domaine visuellement similaires. Par exemple, certains attaquants ont créé le domaine &lt;code&gt;www.airfrạnce.com&lt;/code&gt;. Si vous regardez bien, le &lt;code&gt;a&lt;/code&gt; de &lt;code&gt;France&lt;/code&gt; est en fait un a avec un point dessous (U+01EA1) ! Les attaquants envoyaient donc ce lien avec la promesse d’un billet d’avion à gagner et il était très compliqué pour les utilisateurs de voir la différence.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/strings/homoglyph.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/strings/homoglyph.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;aspect-ratio: calc(304 / 91)&quot; src=&quot;https://jolicode.com//media/original/2024/strings/homoglyph.png&quot; alt=&quot;Un homoglyphe du site de Air France vu par mon navigateur&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Aujourd’hui, ces URLs sont régulièrement traduit en “punycode” par les navigateurs et applications. Il s’agit d’une version sans accents et précédée de &lt;code&gt;xn--&lt;/code&gt; et dont les caractères spéciaux sont supprimés. Vous pouvez trouver plus de détails dans &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.systonic.fr/attaques-homographes-homoglyphes&quot;&gt;cet article&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;En pratique : la taille d’une chaîne 🧮&lt;/h2&gt;
&lt;p&gt;Vous pensez que c’est facile ? Spoiler : oui, mais pas comme vous le pensiez !&lt;/p&gt;
&lt;p&gt;Compter le nombre de caractères sur son site Web est une action très courante, qu’il s’agisse de vérifier la longueur d’un mot de passe, ou de compter les caractères d’un champ texte par exemple.&lt;/p&gt;
&lt;p&gt;Pour un article très détaillé et technique sur le sujet, n’hésitez pas à lire &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://hsivonen.fi/string-length/&quot;&gt;It’s Not Wrong that &amp;quot;🤦🏼‍♂️&amp;quot;.length == 7&lt;/a&gt; de Henri Sivonen ; mais si vous avez la flemme, en voici un résumé qui ne s’intéresse qu’à la partie PHP.&lt;/p&gt;
&lt;p&gt;En effet, d’un langage à l’autre, la taille de la chaîne de caractères &lt;code&gt;🤦🏼‍♂️&lt;/code&gt; peut être 1, 5, 7 ou encore 17. Rien qu’en PHP, vous obtiendrez 17 avec &lt;code&gt;strlen(&#039;🤦🏼‍♂️&#039;);&lt;/code&gt; et 5 avec &lt;code&gt;mb_strlen(&#039;🤦🏼‍♂️&#039;, &#039;UTF-8&#039;);&lt;/code&gt;. Vous vous en doutez, cela dépend de ce qu’on compte : des octets ? Des points de code ? Des graphèmes ?&lt;/p&gt;
&lt;p&gt;Décomposons cet emoji pour comprendre ce qu’il se passe. Il est constitué de 5 caractères distincts : l’emoji facepalm, un modificateur de couleur de peau, ainsi qu’un modificateur de genre. À ces caractères s’ajoutent le “zero width joiner”, ou “jointure de taille zéro”, qui permet d’indiquer que les caractères se superposent, ainsi que le “variation selector” ou “sélecteur de variation” qui explicite le fait que l’on souhaite afficher un emoji coloré plutôt qu’un &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://fr.wikipedia.org/wiki/Casseau_(typographie)&quot;&gt;dingbat&lt;/a&gt; monochrome.&lt;/p&gt;
&lt;p&gt;D’un langage à l’autre, les différents caractères qui composent un graphème sont enregistrés dans des “code units” ou &lt;strong&gt;unités de code&lt;/strong&gt; qui contiennent plus ou moins d’octets.&lt;/p&gt;
&lt;p&gt;Le tableau suivant décompose cet emoji, et pour chaque part, nous donne sa taille en unités de code et en octets.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scalaire Unicode&lt;/th&gt;
&lt;th&gt;Unités de code UTF-32&lt;/th&gt;
&lt;th&gt;Unités de code UTF-16&lt;/th&gt;
&lt;th&gt;Unités de code UTF-8&lt;/th&gt;
&lt;th&gt;UTF-32 code octets&lt;/th&gt;
&lt;th&gt;UTF-16 code octets&lt;/th&gt;
&lt;th&gt;UTF-8 code octets&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;U+1F926 FACE PALM 🤦&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;U+1F3FC EMOJI MODIFIER FITZPATRICK TYPE-3 🏼&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;U+200D ZERO WIDTH JOINER&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;U+2642 MALE SIGN ♂&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;U+FE0F VARIATION SELECTOR-16&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Dans le cas d’un emoji, on s&#039;attendra bien souvent à ce que son nombre de caractères soit égal à 1. On veut donc compter les graphèmes. Eh bien le composant String nous permet cette granularité :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ByteString&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;🤦🏼‍♂️&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();    	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// 17 octets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; CodePointString&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;🤦🏼‍♂️&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// 5 points de code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; UnicodeString&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;🤦🏼‍♂️&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();	&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// 1 graphème&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Et dans la base de données ? 💾&lt;/h2&gt;
&lt;h3&gt;Petit point vocabulaire&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Character set&lt;/strong&gt; : Un set de caractères. Il définit l’encodage de chaque glyphe. Par exemple, &lt;code&gt;utfmb4&lt;/code&gt; est un set de caractères correspondant à l’UTF-8 dans les BDD MySQL.
&lt;strong&gt;Collation&lt;/strong&gt; : Une règle de tri des caractères les uns par rapport aux autres. Est-ce que &lt;code&gt;a&lt;/code&gt; doit être rangé entre &lt;code&gt;A&lt;/code&gt; et &lt;code&gt;B&lt;/code&gt; ou plutôt après &lt;code&gt;Z&lt;/code&gt; ? Par exemple, avec la collation &lt;code&gt;utf8mb4_unicode_ci&lt;/code&gt; de MySQL, le &lt;code&gt;ß&lt;/code&gt;, le double s allemand, sera considéré comme &lt;code&gt;ss&lt;/code&gt; et &lt;code&gt;œ&lt;/code&gt; est considéré comme &lt;code&gt;oe&lt;/code&gt;. En revanche, avec la collation &lt;code&gt;utf8mb4_general_ci&lt;/code&gt;, le &lt;code&gt;ß&lt;/code&gt; est considéré comme un &lt;code&gt;s&lt;/code&gt; simple et le &lt;code&gt;œ&lt;/code&gt; est considéré comme un &lt;code&gt;e&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Les différents sets de cractères&lt;/h3&gt;
&lt;p&gt;PostgeSQL c’est le bon élève de cette histoire : son set de caractère correspondant à UTF-8 se nomme sobrement &lt;code&gt;UTF8&lt;/code&gt;, et se comporte bien comme tel : il est encodé sur 1 à 4 octets de 8 bits.&lt;/p&gt;
&lt;p&gt;Chez Oracle, il faudra utiliser le set de caractères nommé &lt;code&gt;AL32UTF8&lt;/code&gt;. Leur &lt;code&gt;UTF8&lt;/code&gt; correspond à la norme Unicode 3.0 mais n’est plus à jour aujourd’hui.&lt;/p&gt;
&lt;p&gt;Chez MariaDB et MySQL, il faut utiliser &lt;code&gt;utfmb4&lt;/code&gt;, et pas &lt;code&gt;utf8&lt;/code&gt; qui est encodé sur 3 octets. Vous pouvez lire &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://adamhooper.medium.com/in-mysql-never-use-utf8-use-utf8mb4-11761243e434&quot;&gt;cet article&lt;/a&gt; très intéressant, si vous souhaitez en apprendre plus sur l’existence de cet étrange set de caractères.&lt;/p&gt;
&lt;p&gt;En terme de sécurité, ici aussi l’encodage a été source de problèmes. Lorsqu’on veut insérer un caractère dont la valeur est supérieure à &lt;code&gt;0xFFFF&lt;/code&gt; dans une colonne dont le charset est &lt;code&gt;utf8&lt;/code&gt;, MySQL va tronquer la chaîne au niveau du caractère en question. Par exemple, si je veux insérer “Hello 👋 world 🌍 !”, seul “Hello ” sera inséré puisque l’emoji a pour valeur &lt;code&gt;0xF09F918B&lt;/code&gt;, qui est donc bien supérieure à &lt;code&gt;0xFFFF&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ainsi, un attaquant peut s’inscrire sur un site dont l’inscription est restreinte par domaine simplement en ajoutant un emoji à la fin de la chaîne qu’il veut voir insérer en base. Par exemple, l’attaquant s’inscrit avec “attacker@gmail.com🍕@allowed-domain.com”. Le site effectue un check sur “@allowed-domain.com” et autorise l’inscription, mais seulement “attacker@gmail.com” !&lt;/p&gt;
&lt;p&gt;C’est ici un exemple assez naïf, mais j’espère que vous faites des vérifications plus solides sur les mails de vos utilisateurs !&lt;/p&gt;
&lt;p&gt;En tous cas nous vous encourageons à faire attention aux sets de caractères que vous utilisez.&lt;/p&gt;
&lt;h2&gt;Pour conclure&lt;/h2&gt;
&lt;p&gt;Éviter les erreurs d’encoding est particulièrement simple à notre époque, du moment que tout est configuré avec UTF-8. Symfony nous propose également des outils dédiés afin que nous puissions développer sans nous poser de questions. Alors plus d’excuses pour laisser traîner du mojibake !&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/comment-heberger-de-maniere-securisee-du-contenu-statique</id>
        <published>2024-11-28T14:42:00+01:00</published>
        <updated>2024-11-28T14:42:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/comment-heberger-de-maniere-securisee-du-contenu-statique"/>
        <title>Comment héberger de manière sécurisée du contenu statique ?</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="statique" />            <category term="hébergement" />            <category term="html" />            <category term="github" />            <category term="cloudflare" />        <summary><![CDATA[Introduction
Je pars en voyage, tout est prêt : valises bouclées, papiers rangés, téléphone chargé. Mais une question me titille : que se passe-t-il si je perds mon téléphone ou que l&#039;on me vole mes affaires…]]></summary>
        <content type="html">
            &lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Je pars en voyage, tout est prêt : valises bouclées, papiers rangés, téléphone chargé. Mais une question me titille : que se passe-t-il si je perds mon téléphone ou que l&#039;on me vole mes affaires ? Comment accéder à mes e-mails, mon compte Dropbox, mes codes de récupération pour les services protégés par une double authentification ?&lt;/p&gt;
&lt;p&gt;Cette problématique, bien que rarement urgente, mérite d’être anticipée. Car si vos mots de passe suffisaient autrefois, la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://fr.wikipedia.org/wiki/Double_authentification&quot;&gt;double authentification&lt;/a&gt; rend cette dépendance plus complexe. Alors, comment protéger efficacement ces informations sensibles tout en s&#039;assurant qu&#039;elles restent accessibles en cas de coup dur ?&lt;/p&gt;
&lt;p&gt;Voyons ensemble comment créer une solution élégante et sécurisée pour répondre à ce besoin.&lt;/p&gt;
&lt;h2&gt;Une solution simple&lt;/h2&gt;
&lt;p&gt;Une première réponse pourrait consister à partager certains codes de récupération avec une personne de confiance. C&#039;est simple, mais cela pose des défis :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multiplicité des codes&lt;/strong&gt; : si vous avez de nombreux comptes, cette solution devient difficile à gérer ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fiabilité en cas d’urgence&lt;/strong&gt; : dans une situation stressante, il peut être compliqué de se souvenir à qui vous avez confié un code. De plus, les différences de fuseaux horaires peuvent rendre la communication difficile.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Une solution plus avancée&lt;/h2&gt;
&lt;p&gt;Une alternative serait de déployer une page HTML contenant tous vos codes de récupération. Vous pourriez l’héberger sur une plateforme gratuite comme GitHub Pages, Netlify ou Cloudflare. Mais cela soulève une question essentielle : &lt;strong&gt;comment sécuriser efficacement cette page ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/recovery-codes.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/recovery-codes.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;aspect-ratio: calc(937 / 824)&quot; src=&quot;https://jolicode.com//media/original/2024/recovery-codes.png&quot; alt=&quot;exemple de page qui affiche des recovery codes&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h3&gt;Chiffrer le contenu&lt;/h3&gt;
&lt;p&gt;La première idée est de &lt;strong&gt;chiffrer le contenu de la page HTML de manière symétrique&lt;/strong&gt; (avec un mot de passe). Une fois stocké, le contenu serait accessible uniquement après avoir saisi le mot de passe.&lt;/p&gt;
&lt;p&gt;C’est exactement ce que permet &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/robinmoisson/staticrypt&quot;&gt;staticrypt&lt;/a&gt;. Cet outil en ligne de commande chiffre vos fichiers statiques en utilisant l’algorithme AES-256 via WebCrypto.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/password.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/password.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;aspect-ratio: calc(937 / 441)&quot; src=&quot;https://jolicode.com//media/original/2024/password.png&quot; alt=&quot;exemple de page qui demande un mot de passe&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Le résultat est une page HTML qui :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;demande un mot de passe via un formulaire ;&lt;/li&gt;
&lt;li&gt;déchiffre le contenu directement dans le navigateur grâce à JavaScript.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Simple et robuste !&lt;/p&gt;
&lt;h3&gt;Héberger le contenu&lt;/h3&gt;
&lt;p&gt;Pour l’hébergement, GitHub Pages ou Netlify sont des choix évidents. Ces plateformes sont simples à utiliser et gratuites. Mais même si l’AES-256 est fiable, je reste inquiet à l’idée de stocker publiquement mes codes de récupération, même chiffrés. Est-ce que AES-256 sera toujours résistant aux attaques &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://fr.wikipedia.org/wiki/Cryptographie_post-quantique&quot;&gt;post-quantique&lt;/a&gt; ?&lt;/p&gt;
&lt;p&gt;Pourquoi ? Parce qu’une faille découverte dans le futur pourrait compromettre la sécurité de ces données sensibles. Il faut donc &lt;strong&gt;ajouter une couche de protection supplémentaire&lt;/strong&gt; : une authentification HTTP en amont de la page HTML chiffrée.&lt;/p&gt;
&lt;p&gt;Sur GitHub Pages, cette fonctionnalité n’est pas disponible (surtout sur le plan gratuit). Heureusement, &lt;strong&gt;Cloudflare Workers&lt;/strong&gt; offre une solution parfaite.&lt;/p&gt;
&lt;p&gt;Cloudflare permet de déployer du code JavaScript à travers son CDN, ce qui rend facile l’ajout d’une authentification avant même de servir la page HTML. Ce code JavaScript n&#039;est pas exécuté dans le navigateur, mais sur les serveurs de Cloudflare. Pour gagner du temps, je me suis inspiré de cet &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://dev.to/charca/password-protection-for-cloudflare-pages-8ma&quot;&gt;article de blog&lt;/a&gt;, qui détaille parfaitement le processus.&lt;/p&gt;
&lt;h2&gt;Automatisation avec Castor&lt;/h2&gt;
&lt;p&gt;Pour assembler ces briques, j’ai utilisé l’outil &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://castor.jolicode.com/&quot;&gt;Castor&lt;/a&gt; :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Task &lt;code&gt;castor build&lt;/code&gt;&lt;/strong&gt; : génère les fichiers nécessaires grâce à &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://twig.symfony.com/&quot;&gt;Twig&lt;/a&gt;, ce qui facilite la gestion du HTML. Voici un extrait de la tâche castor :
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$recoveryCodes &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $twig&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;recovery-codes.html.twig&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;    &#039;recovery_codes&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; yaml_parse&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;file_get_contents&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($recoveryCodesFilename)),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;file_put_contents&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;__DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/var/tmp/recovery-codes.html&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $recoveryCodes);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    command: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-3&quot;&gt;        __DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/node_modules/.bin/staticrypt&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;--template&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;__DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/var/tmp/staticrypt.html&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;--template-title&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Recovery codes&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;--config&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;false&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// No need to store the salt, remember me is disabled&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;--remember&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;false&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Since data are sensitive, we don&#039;t want to remember the password&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        ...&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;variable&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;defaultPassword&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;--short&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; []),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;-d&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;dist/public&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-3&quot;&gt;        __DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/var/tmp/recovery-codes.html&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    context: &lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;withEnvironment&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;STATICRYPT_PASSWORD&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; variable&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;PASSWORD&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Task &lt;code&gt;castor deploy&lt;/code&gt;&lt;/strong&gt; : déploie automatiquement le tout sur Cloudflare grâce à &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://developers.cloudflare.com/workers/wrangler/&quot;&gt;wrangler&lt;/a&gt;. Il vous faudra un compte au préalable et configurer deux variables d’environnement, ensuite castor s’occupe de tout.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;En quelques commandes, tout est prêt ! 🎉&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;J’ai désormais une solution simple, gratuite, sans maintenance, pérenne, et sécurisée pour stocker du contenu sensible sur internet.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Première section&lt;/strong&gt; : protégée par un mot de passe, elle peut contenir des documents importants (copie de passeport, permis de conduire, contacts d’urgence). Ce mot de passe peut être communiqué à mon entourage de confiance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deuxième section&lt;/strong&gt; : réservée à mes données sensibles (codes de récupération, etc.), elle est chiffrée avec un mot de passe distinct que je conserve pour moi seul. Ce nouveau mot de passe n&#039;est connu que de moi.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Parce que j’aime partager mes projets open source, celui-ci est déjà disponible sur &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/lyrixx/private-stuff&quot;&gt;GitHub&lt;/a&gt;, accompagné d’une &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://lyrixx.github.io/private-stuff/&quot;&gt;démonstration partielle&lt;/a&gt; hébergée sur GitHub Pages.&lt;/p&gt;
&lt;h2&gt;Vers l’avenir&lt;/h2&gt;
&lt;p&gt;À l’avenir, j’aimerais que cette solution évolue pour gérer des scénarios encore plus critiques, comme le transfert d’informations en cas de décès.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Astuce&lt;/strong&gt; : Si vous avez un compte GitHub, pensez à désigner un &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-access-to-your-personal-repositories/maintaining-ownership-continuity-of-your-personal-accounts-repositories&quot;&gt;successeur&lt;/a&gt;. GitHub y a pensé, mais ce n’est pas le cas de tous les services (banques, assurances, etc.).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Enfin, il existe des algorithmes comme &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://fr.wikipedia.org/wiki/Partage_de_cl%C3%A9_secr%C3%A8te_de_Shamir&quot;&gt;Shamir&#039;s Secret Sharing&lt;/a&gt;, permettant de chiffrer des documents et de les déchiffrer seulement si plusieurs personnes s’accordent. Je réfléchis à une intégration de cette technologie pour rendre le projet encore plus complet.&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/ecrire-des-assertions-phpunit-plus-simples-grace-au-vardumper</id>
        <published>2024-11-20T15:42:00+01:00</published>
        <updated>2024-11-20T15:42:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/ecrire-des-assertions-phpunit-plus-simples-grace-au-vardumper"/>
        <title>Écrire des assertions PHPUnit plus simples grâce au VarDumper</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="symfony" />            <category term="test" />            <category term="phpunit" />        <summary><![CDATA[Les tests unitaires sont une étape cruciale pour garantir la qualité de votre
code, mais parfois, les répétitions peuvent devenir lassantes. Avez-vous déjà soupiré en enchaînant des appels à $this-&amp;gt;assertXXX()…]]></summary>
        <content type="html">
            &lt;p&gt;Les tests unitaires sont une étape cruciale pour garantir la qualité de votre
code, mais parfois, les répétitions peuvent devenir lassantes. Avez-vous déjà soupiré en enchaînant des appels à &lt;code&gt;$this-&amp;gt;assertXXX()&lt;/code&gt; pour valider des structures complexes ? Heureusement, il existe une solution élégante pour simplifier tout cela : le composant &lt;strong&gt;Symfony/VarDumper&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Voyons ensemble comment l&#039;utiliser pour rendre vos tests plus concis et lisibles tout en conservant leur efficacité.&lt;/p&gt;
&lt;h2&gt;Un exemple concret&lt;/h2&gt;
&lt;p&gt;Imaginons que nous souhaitions tester certaines propriétés d’une classe comme celle-ci :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;MyObject&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $aPublicProperty &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;a public property&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        protected&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $aProtectedProperty &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;a protected property&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $aPrivateProperty &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;a private property&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $anotherPublicProperty &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;another public property&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        protected&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $anotherProtectedProperty &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;another protected property&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $anotherPrivateProperty &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;another private property&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lorsque vous &lt;em&gt;dumpez&lt;/em&gt; un objet instancié avec ses valeurs par défaut, voici le résultat obtenu :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PhpunitVarDumper\MyObject^ {#325
  +aPublicProperty: &amp;quot;a public property&amp;quot;
  #aProtectedProperty: &amp;quot;a protected property&amp;quot;
  -aPrivateProperty: &amp;quot;a private property&amp;quot;
  +anotherPublicProperty: &amp;quot;another public property&amp;quot;
  #anotherProtectedProperty: &amp;quot;another protected property&amp;quot;
  -anotherPrivateProperty: &amp;quot;another private property&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Simplifier les tests&lt;/h2&gt;
&lt;p&gt;Traditionnellement, on utiliserait plusieurs appels à &lt;code&gt;$this-&amp;gt;assertSame()&lt;/code&gt; pour valider ces propriétés. Cependant, grâce à &lt;strong&gt;Symfony/VarDumper&lt;/strong&gt;, nous pouvons alléger ce processus en utilisant une assertion sur le dump directement.&lt;/p&gt;
&lt;p&gt;Commencez par installer le composant en mode développement :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;composer&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; require&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --dev&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; symfony/var-dumper&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ensuite, importez le trait &lt;code&gt;VarDumperTestTrait&lt;/code&gt; et utilisez la méthode &lt;code&gt;assertDumpEquals()&lt;/code&gt; dans vos tests :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; PHPUnit\Framework\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; PhpunitVarDumper\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;MyObject&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\VarDumper\Test\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;VarDumperTestTrait&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;MyObjectTest&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;TestCase&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; VarDumperTestTrait&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; test&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $myObject &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; MyObject&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;assertDumpEquals&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &amp;#x3C;&amp;#x3C;&amp;#x3C;&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;DUMP&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            PhpunitVarDumper\MyObject {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;              +aPublicProperty: &quot;a public property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;              #aProtectedProperty: &quot;a protected property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;              -aPrivateProperty: &quot;a private property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;              +anotherPublicProperty: &quot;another public property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;              #anotherProtectedProperty: &quot;another protected property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;              -anotherPrivateProperty: &quot;another private property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            DUMP&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $myObject,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;N’est-ce pas plus clair et élégant ? 🎉&lt;/p&gt;
&lt;h2&gt;Aller plus loin avec les Casters&lt;/h2&gt;
&lt;p&gt;Dans certains cas, toutes les propriétés d’un objet ne sont pas pertinentes pour vos tests, ou vous ne souhaitez pas les mettre à jour à chaque modification de la classe. C’est ici que les &lt;strong&gt;casters&lt;/strong&gt; entrent en jeu.&lt;/p&gt;
&lt;p&gt;Un &lt;em&gt;caster&lt;/em&gt; est une fonction de rappel qui personnalise la représentation d’un objet &lt;em&gt;dumpé&lt;/em&gt;. Elle prend en paramètre :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;L’objet en cours de &lt;em&gt;dump&lt;/em&gt; ;&lt;/li&gt;
&lt;li&gt;Un tableau représentant l’état de l’objet ;&lt;/li&gt;
&lt;li&gt;(d&#039;autres paramètres que nous n&#039;utilisons pas ici).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Pour ajouter un &lt;em&gt;caster&lt;/em&gt;, utilisez la méthode &lt;code&gt;setUpVarDumper()&lt;/code&gt; dans votre test :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;MyObjectTest&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;TestCase&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    use&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; VarDumperTestTrait&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    protected&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; setUp&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setUpVarDumper&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;                MyObject&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; static&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;MyObject&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $myObject, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $a)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                    return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $a;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour le moment, notre &lt;em&gt;caster&lt;/em&gt; ne sert à rien. Observons donc ce que contient la variable &lt;code&gt;$a&lt;/code&gt; :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;  &quot;aPublicProperty&quot;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;a public property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;  &quot;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\x00&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\x00&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;aProtectedProperty&quot;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;a protected property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;  &quot;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\x00&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;PhpunitVarDumper\MyObject&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\x00&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;aPrivateProperty&quot;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;a private property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;  &quot;anotherPublicProperty&quot;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;another public property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;  &quot;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\x00&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\x00&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;anotherProtectedProperty&quot;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;another protected property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;  &quot;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\x00&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;PhpunitVarDumper\MyObject&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\x00&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;anotherPrivateProperty&quot;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;another private property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si vous avez déjà joué avec PHP et ses mécanismes de sérialisation, vous ne devriez pas être perdu. Sinon une petite explication s’impose :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Chaque clé représente une propriété de l’objet.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Les notations varient en fonction de la visibilité de la propriété :
&lt;ol&gt;
&lt;li&gt;Pour une propriété &lt;strong&gt;publique&lt;/strong&gt;, son nom apparaît directement en clair, comme &lt;code&gt;&amp;quot;aPublicProperty&amp;quot;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Pour une propriété &lt;strong&gt;protégée&lt;/strong&gt;, son nom est précédé par le préfixe spécial &lt;code&gt;\x00*\x00&lt;/code&gt;, qui signale sa visibilité restreinte.&lt;/li&gt;
&lt;li&gt;Pour une propriété &lt;strong&gt;privée&lt;/strong&gt;, le préfixe indique son contexte de déclaration : &lt;code&gt;\x00[NomDeLaClasse]\x00&lt;/code&gt;. Ici, cela donne par exemple &lt;code&gt;\x00PhpunitVarDumper\MyObject\x00aPrivateProperty&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ces notations un peu cryptiques peuvent sembler complexes à première vue. Heureusement, Symfony fournit des &lt;strong&gt;constantes&lt;/strong&gt; pour les manipuler plus facilement, comme &lt;code&gt;Caster::PREFIX_PROTECTED&lt;/code&gt; ou &lt;code&gt;Caster::PATTERN_PRIVATE&lt;/code&gt;. Utilisons-les pour simplifier notre code de &lt;em&gt;caster&lt;/em&gt; :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\VarDumper\Caster\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Caster&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;protected&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; setUp&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;    $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setUpVarDumper&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;            MyObject&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; static&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;MyObject&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $myObject, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $a) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;                unset&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                    $a[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;anotherPublicProperty&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                    $a[&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Caster&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;PREFIX_PROTECTED&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;anotherProtectedProperty&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                    $a[&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Caster&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;PATTERN_PRIVATE&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;MyObject&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;anotherPrivateProperty&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $a;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        AbstractDumper&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;DUMP_LIGHT_ARRAY&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce &lt;em&gt;caster&lt;/em&gt; permet de supprimer les propriétés non pertinentes. Le test devient alors :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; test&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $myObject &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; MyObject&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;    $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;assertDumpEquals&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &amp;#x3C;&amp;#x3C;&amp;#x3C;&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;DUMP&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        PhpunitVarDumper\MyObject {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            +aPublicProperty: &quot;a public property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            #aProtectedProperty: &quot;a protected property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            -aPrivateProperty: &quot;a private property&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        DUMP&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $myObject,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En cas d’erreur, PHPUnit affiche un diff très lisible, ce qui facilite le debug :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1) PhpunitVarDumper\Tests\MyObjectTest::test
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
 &#039;PhpunitVarDumper\MyObject {
-  +aPublicProperty: &amp;quot;Hello you!&amp;quot;
+  +aPublicProperty: &amp;quot;a public property&amp;quot;
   #aProtectedProperty: &amp;quot;a protected property&amp;quot;
   -aPrivateProperty: &amp;quot;a private property&amp;quot;
 }&#039;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Quelques astuces supplémentaires&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Affichage léger des tableaux&lt;/strong&gt; Utilisez &lt;code&gt;AbstractDumper::DUMP_LIGHT_ARRAY&lt;/code&gt; comme deuxième paramètre de &lt;code&gt;setUpVarDumper()&lt;/code&gt; pour des tableaux plus compacts.
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;-array:3 [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;-  0 =&gt; &quot;a&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;-  1 =&gt; &quot;b&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;-  2 =&gt; &quot;c&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;+[&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;+  &quot;a&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;+  &quot;b&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;+  &quot;c&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gestion des valeurs dynamiques&lt;/strong&gt; Préférez un &lt;em&gt;caster&lt;/em&gt; à &lt;code&gt;assertDumpMatchesFormat()&lt;/code&gt;, car les diff générés avec des formats sont souvent difficiles à lire.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Le composant &lt;strong&gt;VarDumper&lt;/strong&gt; est un allié précieux, tant pour le débogage que pour simplifier vos tests. Grâce à lui, vous gagnez en clarté et en efficacité.&lt;/p&gt;
&lt;p&gt;Alors, dites adieu aux assertions fastidieuses, et concentrez-vous sur ce qui compte vraiment : la qualité de votre code. 🎉&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/bdx-i-o-au-pays-du-vin-notre-retour-sur-ledition-2024</id>
        <published>2024-11-19T09:42:00+01:00</published>
        <updated>2024-11-19T09:42:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/bdx-i-o-au-pays-du-vin-notre-retour-sur-ledition-2024"/>
        <title>BDX I/O au pays du vin : notre retour sur l&#039;édition 2024</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="conférence" />            <category term="ia" />            <category term="htmx" />        <summary><![CDATA[Pour la première fois, nous nous sommes rendus à la conférence BDX I/O au Palais des Congrès de Bordeaux.
Cette conférence existe depuis 2014 et a rassemblé plus de 1000 participants pour cette 9ème édition.…]]></summary>
        <content type="html">
            &lt;p&gt;Pour la première fois, nous nous sommes rendus à la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://bdxio.fr/&quot;&gt;conférence BDX I/O&lt;/a&gt; au Palais des Congrès de Bordeaux.
Cette conférence existe depuis 2014 et a rassemblé plus de 1000 participants pour cette 9ème édition.&lt;/p&gt;
&lt;p&gt;Cette année, le thème était l&#039;IA. &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://mariealiceblete.com/&quot;&gt;Marie-Alice Blete&lt;/a&gt; a lancé la journée avec un talk très intéressant appelé : &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.youtube.com/live/uHx2lQ1Ku-s?feature=shared&amp;amp;t=3315&quot;&gt;&amp;quot;LLMs, entre fantasme et réalité - le point de vue d&#039;une dev passée de l&#039;autre côté&amp;quot;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;En posant un autre regard sur les chiffres annoncés en grande fanfare par des acteurs de l&#039;IA, elle remet la réalité au centre. Par exemple, il a été annoncé que les développeurs qui utilisent une IA pour coder font 25% de Pull Request en plus. Cependant, un chiffre moins communiqué est que la qualité globale de ces PRs a reculé, le code produit est considéré moins maintenable. Loin de l&#039;euphorie, son regard pragmatique sur l&#039;IAa été une introduction parfaite à cette journée.&lt;/p&gt;
&lt;p&gt;Tout au long de la journée nous avons assisté à différents talks autour de ce sujet. Les enjeux d’une IA plus éco responsable nous ont été présentés. En effet, cette technologie a un bilan carbone très élevé et les entraînements des modèles d’IA sont voraces en énergie. Il a été démontré par exemple qu’une requête ChatGPT équivaut à 60 recherches Google. De plus, nous remarquons fréquemment, sur les sites que nous exploitons, d&#039;importantes hausses de trafic sur certaines pages qui, après analyse, proviennent de bots qui collectent des données d&#039;entraînement pour des modèles d&#039;IA. Au-delà des questions légitimes de propriété intellectuelle, cela nous confronte souvent à deux problématiques : d&#039;abord, cela crée de la charge en plus sur les infrastructures Web et, ensuite, cela pose la question de la consommation de ressources serveur pour cet objectif discutable. On peut toutefois noter qu&#039;avec redirection.io, il est facilement possible de &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://redirection.io/recipes/block-ai-bots&quot;&gt;bloquer ces bots IA&lt;/a&gt;, si besoin.&lt;/p&gt;
&lt;p&gt;Nous avons également été sensibilisés sur le sujet de l’IA et du Handicap. Elles nous ont donné leur constat sur les avancées dans le domaine et nous ont partagé leur craintes par rapport aux mauvaises représentations induites par certaines IA qui vont préférer mettre de côté les &amp;quot;valeurs aberrantes&amp;quot; et créer une forme d’exclusion.&lt;/p&gt;
&lt;p&gt;Retrouvez les vidéos des conférences :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.youtube.com/live/k8LwIsgqbfg?feature=shared&amp;amp;t=7812&quot;&gt;L’IA et le handicap : progrès ou exclusion ? par Thanh Lan Doublier et Emmanuelle Aboaf&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.youtube.com/live/si6_--TAIVg?feature=shared&amp;amp;t=19480&quot;&gt;L&#039;IA Éco-Responsable : Utopie Marketing ou Réalité ?&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Découvrons ensemble la relève de l&#039;observabilité avec les logs et les traces : Quickwit par Idriss Neumann&lt;/h2&gt;
&lt;p&gt;Les logs, les métriques et les traces sont les 3 piliers du monitoring applicatif. L&#039;observabilité de sa plateforme est un point essentiel pour garantir la stabilité de son site dans le temps.
Bien que nous soyons très friands de Datadog, nous surveillons beaucoup ce qu&#039;il se passe autour d&#039;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://opentelemetry.io/&quot;&gt;OpenTelemetry&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Installer et maintenir Datadog représente un coût quasi nul, mais c’est un service qui peut vite coûter cher quand le nombre de serveurs ou le volume de données à traiter augmente.
On peut préférer une alternative auto hébergée, pour des raisons de sécurité, ou parce que c’est une association où le temps des bénévoles est en plus grande quantité que les euros (cf. notre retour sur la conférence des Restos du Coeur, où Quickwit est employé).&lt;/p&gt;
&lt;p&gt;Le choix le plus répandu dans ce cas, est la stack &lt;abbr title=&quot;Elasticsearch, Kibana, Beats et Logstash&quot;&gt;ELK&lt;/abbr&gt;, sur laquelle nous avons pas mal d&#039;expérience. Dans ce cas, le souci est la gestion du stockage longue durée des logs, qui peuvent vite représenter un volume conséquent.&lt;/p&gt;
&lt;p&gt;Quickwit se démarque en stockant ses données sur un object storage (type AWS S3 ou un cellar OVH / Clever Cloud). Ces stockages coûtent plusieurs centaines de fois moins cher que des disques traditionnels tout en assurant un stockage redondé et accessible (au dépend de la bande passante de sortie, souvent payante).
Lire de la données sur un S3 est cependant un peu plus lent qu’un disque réseau, Quickwit indique compenser cette latence par une implémentation d’Apache Lucene - le moteur d&#039;indexation/recherche utilisé par Elasticsearch - en Rust.&lt;/p&gt;
&lt;p&gt;Pour la partie UI, ils se reposent sur les traditionnels Grafana / Jaeger.&lt;/p&gt;
&lt;p&gt;Une autre alternative à étudier pour de l’auto hébergement semble être &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://signoz.io/&quot;&gt;Signoz.io&lt;/a&gt; qui lui s’appuie sur Clickhouse - que nous utilisons beaucoup pour &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://redirection.io/&quot;&gt;redirection.io&lt;/a&gt; - et une UI maison qui semble de haute qualité.&lt;/p&gt;
&lt;h2&gt;Un chatGPT sur vos propres données : comment ça marche ? par Hugo Vassard&lt;/h2&gt;
&lt;p&gt;Hugo Vassard nous a présenté avec brio comment concevoir un &lt;abbr title=&quot;Retrieval Augmented Generation&quot;&gt;RAG&lt;/abbr&gt; sur ses propres données. À l&#039;aide d&#039;un exemple concret, il nous explique ce qu&#039;est la vectorisation, les embeds, les chunks, ou encore les outils du type &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.langchain.com/&quot;&gt;LangChain&lt;/a&gt;, …&lt;/p&gt;
&lt;p&gt;Si c&#039;est quelque chose qui vous intéresse, nous vous conseillons de lire l&#039;article de Loïck sur &lt;a href=&quot;https://jolicode.com//blog/construire-un-chatbot-specialise-sur-vos-donnees-grace-a-lia-generative-et-php&quot;&gt;la construction d&#039;un chatbot spécialisé sur la documentation&lt;/a&gt; de notre task runner &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://castor.jolicode.com/&quot;&gt;Castor&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;La &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://youtu.be/k8LwIsgqbfg?t=15333&quot;&gt;vidéo de la conférence&lt;/a&gt; est également disponible.&lt;/p&gt;
&lt;h2&gt;HTMX, où le retour de l&#039;AJAX dans le développement Web par Stéphane Trebel&lt;/h2&gt;
&lt;p&gt;En Javascript, on passe beaucoup de temps à réinventer la roue. Tout ça pour créer des pages web de plus en plus grosses… Stéphane Trebel nous a présenté plusieurs cas d’utilisation de la librairie Htmx dans une conférence très dynamique.
De l’infinite scroll en quelques lignes à la captation et la gestion d’erreurs simplifiée les possibilités ont l’air nombreuses et le code très clair et moins éparpillé.
D’après lui, “Htmx c’est ce que le html aurait du devenir si on n&#039;était pas passés en tout JS”.&lt;/p&gt;
&lt;p&gt;Pour retrouver tous ses exemples, il suffit d’aller faire un tour sur son &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/StephaneTrebel/poc-htmx-rust&quot;&gt;dépôt Github&lt;/a&gt; et la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.youtube.com/live/k8LwIsgqbfg?feature=shared&amp;amp;t=1221&quot;&gt;vidéo de la conférence&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Les 20 minutes de Typescript les plus rentables de votre vie ! par Aurélien Loyer et Delphin Aubin&lt;/h2&gt;
&lt;p&gt;Cette conférence était un ensemble de très bons conseils sur l’écosystème Typescript, nous retenons :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Une &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/allegro/typescript-strict-plugin&quot;&gt;librairie&lt;/a&gt; pour passer en &lt;code&gt;strict:true&lt;/code&gt; par étape ;&lt;/li&gt;
&lt;li&gt;Les types guards (connaître le type actuel de notre valeur dans un contexte) ;&lt;/li&gt;
&lt;li&gt;L’utilisation du type never pour détecter les cas manquants ;&lt;/li&gt;
&lt;li&gt;La syntaxe en template string dans un type template littérals.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et si tous ces concepts vous interpellent voici &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/delphinaubin/ts-tips-conference&quot;&gt;les slides de la conférence&lt;/a&gt; et la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.youtube.com/live/uHx2lQ1Ku-s?feature=shared&amp;amp;t=18819&quot;&gt;vidéo&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Comment nous avons transformé les Restos du Cœur en Cloud Provider ? par Julien Briault&lt;/h2&gt;
&lt;p&gt;Amazon a créé AWS car la logistique est très consommateur en applications diverses et en ressources.&lt;/p&gt;
&lt;p&gt;Les &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.restosducoeur.org/&quot;&gt;Restos du Cœur&lt;/a&gt; sont dans la logistique également, et même dans la logistique compliquée : la nourriture (chaîne du froid, date de péremption, …).
De plus, c&#039;est une association qui tourne grâce à des bénévoles et où 1€ dépensé = 1 repas distribué en moins.&lt;/p&gt;
&lt;p&gt;Une équipe d&#039;admin sys et devops a réussi a rassembler de nombreux dons de matériels informatiques, beaucoup de connaissances et une tonne d&#039;heures de bénévolat pour mettre en place un « cloud provider » à destination de la centaine d&#039;antennes de l&#039;association.&lt;/p&gt;
&lt;p&gt;Tout un tas de noms connus et inconnus, avec pour objectifs : stabilité et gains financiers. On retrouve donc du &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://maas.io/&quot;&gt;Canonical Metal as a Service&lt;/a&gt;, Kubernetes, OpenStack. La plateforme permet même aux antennes de créer un cluster Kubernetes à la demande, donc il y a du Kubernetes dans OpenStack sur Kubernetes !&lt;/p&gt;
&lt;p&gt;Les bénévoles des Restos du Cœur recrutent et c&#039;est un très très bon moyen d&#039;apprendre auprès de devops super contents et motivés.&lt;/p&gt;
&lt;p&gt;Vous pouvez regarder la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.youtube.com/live/meNsahu8Up8?feature=shared&amp;amp;t=23398&quot;&gt;construction d&#039;un cloud provider&lt;/a&gt; sur Youtube.&lt;/p&gt;
&lt;h2&gt;Vivement 2025&lt;/h2&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/bdxio/202411-bdxio-2.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/bdxio/202411-bdxio-2.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(1100 / 619)&quot; src=&quot;https://jolicode.com//media/original/2024/bdxio/202411-bdxio-2.jpg&quot; alt=&quot;Keynote de clôture de BDX I/O en musique&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Nous avons beaucoup apprécié notre journée à BDX I/O. Avec 7 salles en parallèle, le choix peut être parfois difficile, mais toutes les conférences ont été de bonne qualité.  Heureusement, toutes les captations vidéo sont disponibles sur la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.youtube.com/@bdxio/streams&quot;&gt;chaine Youtube de BDX I/O&lt;/a&gt; pour voir les conférences que nous avons pu louper.&lt;/p&gt;
&lt;p&gt;Et, le plus important, le repas du midi était très bien : un buffet facile d&#039;accès et bien garni !&lt;/p&gt;
&lt;p&gt;BDX I/O, on se revoit l&#039;année prochaine !&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/a-la-decouverte-des-lazy-proxy-et-lazy-ghost-objets-de-php-8-4</id>
        <published>2024-10-28T10:42:00+01:00</published>
        <updated>2024-10-28T10:42:00+01:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/a-la-decouverte-des-lazy-proxy-et-lazy-ghost-objets-de-php-8-4"/>
        <title>A la découverte des lazy proxy et lazy ghost objets de PHP 8.4</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="php" />            <category term="php8" />            <category term="lazy" />        <summary><![CDATA[Parmi toutes les nouvelles fonctionnalités de PHP 8.4, nous souhaitions vous faire découvrir les différents usages de la RFC &amp;quot;Lazy Objects&amp;quot;. Cette RFC a été conduite par Arnaud Le Blanc, contributeur…]]></summary>
        <content type="html">
            &lt;p&gt;Parmi toutes les nouvelles fonctionnalités de PHP 8.4, nous souhaitions vous faire découvrir les différents usages de la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://wiki.php.net/rfc/lazy-objects&quot;&gt;RFC &amp;quot;Lazy Objects&amp;quot;&lt;/a&gt;. Cette RFC a été conduite par Arnaud Le Blanc, contributeur au cœur de PHP, et Nicolas Grekas, contributeur principal de Symfony.&lt;/p&gt;
&lt;h2&gt;Que signifie &lt;em&gt;Lazy Loading&lt;/em&gt; ?&lt;/h2&gt;
&lt;p&gt;En informatique - mais pas uniquement, 👋 la procrastination - nous aimons délayer au plus tard possible l&#039;utilisation de ressources. C&#039;est-à-dire que nous souhaitons économiser au maximum des cycles CPU, de la consommation de mémoire, des IO tant que nous n&#039;en avons pas besoin.&lt;/p&gt;
&lt;p&gt;Prenons un exemple simple : sur une page web très longue, il est inutile de charger toutes les images dès l&#039;ouverture. Il est probable que l&#039;utilisateur ne scrolle jamais jusqu&#039;en bas. En retardant le chargement des images, nous évitons plusieurs requêtes HTTP, ce qui allège le réseau.&lt;/p&gt;
&lt;p&gt;C&#039;est là qu&#039;intervient le terme &lt;em&gt;lazy&lt;/em&gt;. On reporte une tâche à plus tard. Par exemple, on ne charge les images que si l&#039;utilisateur commence à faire défiler la page.&lt;/p&gt;
&lt;p&gt;Dans cet article, nous allons voir ce que PHP 8.4 nous propose pour rendre notre code &lt;em&gt;lazy&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Un peu d&#039;histoire&lt;/h2&gt;
&lt;p&gt;Il n&#039;y avait pas besoin d&#039;attendre PHP 8.4 pour avoir des objets lazy. Nous pouvons déjà le faire dans notre propre code :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;CartRepository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    private&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Redis&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $redis;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $host,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; isCartExpired&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $cart)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; bool&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // de la logique qui n&#039;a pas besoin de $this-&gt;redis&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; save&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $cart)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;hMSet&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($cart&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;id, $cart&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; get&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $cartId)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Cart&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Cart&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;FromArray&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;hGetAll&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($cartId));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    private&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; redis&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; \&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Redis&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;redis)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            return&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;redis;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;redis &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; \&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;host);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;redis;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dans la classe &lt;code&gt;CartRepository&lt;/code&gt;, la connexion au serveur Redis est ouverte uniquement lorsque le code en a réellement besoin. Un appel à la méthode &lt;code&gt;isCartExpired()&lt;/code&gt; n&#039;ouvrira pas la connexion, contrairement aux méthodes &lt;code&gt;save()&lt;/code&gt; ou &lt;code&gt;get()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Bien que fonctionnel, ce genre de code est pénible à écrire, source de discussions, et n’apporte pas beaucoup de valeur ajoutée au produit.&lt;/p&gt;
&lt;p&gt;C’est pourquoi des bibliothèques ont vu le jour pour simplifier l’écriture de ce type de code. On peut citer :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/FriendsOfPHP/proxy-manager-lts&quot;&gt;FriendsOfPHP/proxy-manager-lts&lt;/a&gt;, un fork de &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/Ocramius/ProxyManager&quot;&gt;Ocramius/ProxyManager&lt;/a&gt; qui n&#039;est plus vraiment maintenue ;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/symfony/var-exporter&quot;&gt;symfony/var-exporter&lt;/a&gt;, une réécriture complète du premier.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Baptiste a d’ailleurs écrit un &lt;a href=&quot;https://jolicode.com//blog/php-object-lazy-loading-is-more-than-what-you-think&quot;&gt;article sur l’utilisation de &lt;em&gt;symfony/var-exporter&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Cependant, maintenir de telles bibliothèques en PHP est complexe. Certaines choses ne peuvent pas être faites en PHP pur, et il existe des cas où ces bibliothèques ne fonctionnent pas en raison de limitations techniques.&lt;/p&gt;
&lt;p&gt;C’est pourquoi Nicolas et Arnaud ont travaillé ensemble pour intégrer ces fonctionnalités directement dans le cœur de PHP. Merci à eux 💛&lt;/p&gt;
&lt;h2&gt;Qu&#039;est-ce qu&#039;un &lt;em&gt;lazy object&lt;/em&gt; ?&lt;/h2&gt;
&lt;p&gt;Lorsque vous souhaitez rendre un objet &lt;em&gt;lazy&lt;/em&gt;, disons une instance de &lt;code&gt;MyClass&lt;/code&gt;, PHP crée un proxy qui a la même signature que &lt;code&gt;MyClass&lt;/code&gt;. Vu de l&#039;extérieur, rien ne distingue ce proxy d&#039;un objet classique. C’est nécessaire pour respecter les types des arguments ou propriétés en PHP.&lt;/p&gt;
&lt;p&gt;Le &lt;em&gt;lazy object&lt;/em&gt; se comporte exactement comme l’objet réel. Toutefois, au premier accès à l&#039;une de ses propriétés non initialisées, le &lt;em&gt;lazy loading&lt;/em&gt; se déclenche :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;MyClass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Deps&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $deps;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; greet&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;Hello 👋&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;//  Nous verrons comment faire un lazy objet plus tard&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$myObject &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; create_lazy_object&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// return true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$myObject &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;instanceof&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; MyClass&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Le lazy loading n&#039;est pas déclenché car greet n&#039;a pas besoin de `$deps`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $myObject&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Le lazy loading est déclenché ici&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$myObject&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;deps;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Quand utiliser du &lt;em&gt;lazy loading&lt;/em&gt; ?&lt;/h2&gt;
&lt;p&gt;Une question fréquente lors de &lt;a href=&quot;https://jolicampus.com/formations/symfony-avancee&quot;&gt;nos formations&lt;/a&gt; est :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pourquoi ne pas activer le &lt;em&gt;lazy loading&lt;/em&gt; partout ?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Comme souvent, une fonctionnalité a un coût. Activer le &lt;em&gt;lazy loading&lt;/em&gt; ralentit l&#039;instanciation de la classe. Il faut donc trouver le juste milieu entre instancier toutes les dépendances ou rendre la classe &lt;em&gt;lazy&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Dans l&#039;exemple précédent, nous avons fait un petit benchmark entre une instanciation classique (&lt;code&gt;direct&lt;/code&gt;) et une instanciation d’un objet &lt;em&gt;lazy&lt;/em&gt; (&lt;code&gt;lazy&lt;/code&gt;). Dans chaque cas, nous récupérons la dépendance :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;direct   0.141μs (±2.09%)
lazy     0.434μs (±2.84%)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il n’y a pas de solution parfaite. Si une classe utilise toujours ses dépendances, il n’y a aucune raison d’activer le &lt;em&gt;lazy loading&lt;/em&gt;. Cependant, si une classe a une dépendance qui effectue une requête SQL, la question mérite d’être posée.&lt;/p&gt;
&lt;p&gt;C’est d’ailleurs l’une des utilisations classiques du &lt;em&gt;lazy loading&lt;/em&gt;. Doctrine ORM l&#039;utilise largement. Dès que notre code a besoin de la propriété d&#039;une relation, Doctrine exécute une requête SQL pour récupérer cette relation :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Category&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $name;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Page&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Category&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $category&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Une requête SQL pour récupérer la page avec l&#039;ID 12345&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$page &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $pageRepository&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;12345&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Une autre requête SQL pour récupérer la catégorie associée&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$page&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;name;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ici, &lt;code&gt;$page-&amp;gt;category&lt;/code&gt; est un &lt;em&gt;lazy object&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Un autre cas d’utilisation est celui des services &lt;em&gt;lazy&lt;/em&gt; dans Symfony :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        #[Autowire(lazy: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Bar&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $bar&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tant que la propriété &lt;code&gt;$this-&amp;gt;bar&lt;/code&gt; n’est pas utilisée, elle n’est pas instanciée.&lt;/p&gt;
&lt;p&gt;Il existe une méthode plus globale :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[Autoconfigure(lazy: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Bar&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ici, tous les services dépendant de &lt;code&gt;Bar&lt;/code&gt; recevront un proxy. La classe &lt;code&gt;Bar&lt;/code&gt; ne sera instanciée qu&#039;au moment d&#039;un appel sur ses propriétés ou méthodes.&lt;/p&gt;
&lt;p&gt;Les frameworks et bibliothèques adopteront sûrement rapidement cette nouveauté de PHP 8.4. Vous n’aurez probablement pas à l&#039;utiliser directement, mais il est toujours intéressant de comprendre comment cela fonctionne.&lt;/p&gt;
&lt;h2&gt;Comment faire un &lt;em&gt;lazy proxy&lt;/em&gt; avec PHP 8.4 ?&lt;/h2&gt;
&lt;p&gt;Étudions ensemble le code suivant :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $a,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $very,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $long,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $list,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $of,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $constructor,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $argument,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $userAgent,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; fetch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $id)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Ici, on imagine faire un appel HTTP vers une API distante&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Et nous aurons besoin de certains dépendance de la class client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;userAgent;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;id&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $id,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;name&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;John Doe&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; getName&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;The Super API Client&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;UserRepository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Client&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $client,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; find&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $id)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($id);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; isUserEnabled&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $user)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // de la logique qui n&#039;a pas besoin de $this-&gt;client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nous avons deux classes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Client&lt;/code&gt; effectue des requêtes HTTP vers une API ;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserRepository&lt;/code&gt; expose des méthodes pour récupérer des utilisateurs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L’instanciation de &lt;code&gt;Client&lt;/code&gt; est complexe car elle nécessite de nombreux paramètres coûteux à obtenir. Rendons cette classe &lt;em&gt;lazy&lt;/em&gt; grâce à la réflexion, qui est l’unique moyen de créer des objets &lt;em&gt;lazy&lt;/em&gt; :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$reflector &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ReflectionClass&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$client &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $reflector&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;newLazyProxy&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Client&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // Instanciation du vrai Client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // Ce code ne sera appelé uniquement au moment où une propriété du **Client** sera utilisé&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    return&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Client&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;a&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;very&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;long&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;list&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;of&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;constructor&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;argument&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;        &#039;Mozilla/5.0&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà 🎉, ce n&#039;était pas compliqué ?&lt;/p&gt;
&lt;p&gt;Nous pouvons maintenant utiliser ce lazy proxy dans notre repository :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$repository &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; UserRepository&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($client);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// A ce moment de l&#039;exécution du code, le vrai Client n&#039;a pas été instancié&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$repository&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;isUserEnabled&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($user);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// A ce moment de l&#039;exécution du code, le vrai Client n&#039;a toujours pas été instancié&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($repository&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Mais ce code va déclencher l&#039;instantiation du vrai Client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($repository&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Affichera :&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// ^ array:2 [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;//   &quot;id&quot; =&gt; 42&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;//   &quot;name&quot; =&gt; &quot;John Doe&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// ]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez voir ce &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://3v4l.org/S6B46/rfc#vgit.master&quot;&gt;code en action sur 3v4l.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Le titre de l&#039;article mentionne lazy ghost, mais nous n&#039;en avons toujours pas parlé ! Il est temps de remédier à ça.&lt;/p&gt;
&lt;p&gt;Les &lt;em&gt;Ghost Objects&lt;/em&gt; sont des &lt;em&gt;lazy objects&lt;/em&gt; d’un type un peu différent. Au lieu d&#039;instancier l&#039;objet, ils se concentrent sur l&#039;initialisation de ses propriétés.&lt;/p&gt;
&lt;p&gt;Cela peut être utile lorsque vous ne souhaitez pas lancer une logique complexe au début, mais juste initialiser une partie de votre objet.&lt;/p&gt;
&lt;h2&gt;Comment faire un &lt;em&gt;lazy ghost&lt;/em&gt; ?&lt;/h2&gt;
&lt;p&gt;Cette fois-ci, étudions un code un peu différent :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Post&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $id;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $name;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $email;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; greet&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;Hello 👋&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;PostRepository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; find&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $id)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Post&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Fait des appels à la base de données&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nous avons deux classes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Post&lt;/code&gt; est un DTO qui contient des données&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PostRepository&lt;/code&gt; est une classe qui expose des méthodes pour récupérer des posts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Contrairement au chapitre précédent, nous n&#039;avons pas un service que nous voulons rendre lazy. Ici, nous voulons rendre une instance de &lt;code&gt;Post&lt;/code&gt; lazy. C&#039;est-à-dire que nous voulons retourner une instance de &lt;code&gt;Post&lt;/code&gt; de la méthode &lt;code&gt;PostRepository::find()&lt;/code&gt;, mais que la requête SQL soit retardée jusqu&#039;à l&#039;utilisation d&#039;une propriété de l&#039;instance &lt;code&gt;Post&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Implémentons la méthode &lt;code&gt;find()&lt;/code&gt; ensemble, avec des commentaires dans le code :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;PostRepository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    private&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ReflectionClass&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $reflector;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Nous aurons besoin de la réflexion, alors stockons immédiatement une instance dans le repository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;reflector &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ReflectionClass&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; find&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $id)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Post&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Contrairement à newLazyProxy(), nous recevons une instance de Post, mais entièrement vide&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $entity &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;reflector&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;newLazyGhost&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $entity) &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($id)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;            // Nous récupérons les données stockées en base de données&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $data &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;loadFromDatabase&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($id);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;            // Nous assignons les données aux différentes propriétés&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;            $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;reflector&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;name&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($entity, $data[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;name&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;            $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;reflector&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;email&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($entity, $data[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;email&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Comme nous connaissons déjà l&#039;ID du post, nous pouvons le définir dans la classe directement.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Un appel à cette propriété ne déclenchera pas le chargement&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;reflector&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;id&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setRawValueWithoutLazyInitialization&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($entity, $id);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $entity;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    private&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; loadFromDatabase&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $id)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Simule une requête à la base de données&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;name&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;John Doe&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;email&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;john.doe@example.com&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voilà 🎉, ce n&#039;était pas compliqué ?&lt;/p&gt;
&lt;p&gt;Nous pouvons maintenant utiliser ce &lt;em&gt;ghost object&lt;/em&gt; dans notre code :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$em &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; PostRepository&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$post &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $em&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// À ce moment, la récupération en base de données n&#039;a pas été effectuée&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($post);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Affiche&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// ^ Post^ {#6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;//   +id: 42&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;//   +name: ? string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;//   +email: ? string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// À ce moment, la récupération en base de données n&#039;a toujours pas été effectuée&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($post&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Mais ce code va déclencher la récupération en base de données&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$post&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;email;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($post);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Affiche&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// ^ Post^ {#12&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;//   +id: 42&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;//   +name: &quot;John Doe&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;//   +email: &quot;john.doe@example.com&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vous pouvez voir ce &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://3v4l.org/XNRiS/rfc#vgit.master&quot;&gt;code en action sur 3v4l.org&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Un &lt;em&gt;use case&lt;/em&gt; plus réaliste ?&lt;/h2&gt;
&lt;p&gt;Les deux exemples précédents ne sont pas très réalistes. En général, vous n&#039;aurez pas à écrire ce code, car votre framework préféré, ainsi que votre ORM, le feront pour vous.&lt;/p&gt;
&lt;p&gt;Nous avons réfléchi à un cas d&#039;usage un peu plus réaliste. Laissez-nous poser le contexte. Sur un site à très fort trafic, il est peu recommandé de stocker les paniers dans une base de données, qui se remplirait trop vite, devenant trop gourmande en ressources et augmentant considérablement la facture. Une solution couramment utilisée est le stockage du panier dans Redis.&lt;/p&gt;
&lt;p&gt;Dans l&#039;exemple suivant, nous avons une classe &lt;code&gt;Cart&lt;/code&gt; avec un &lt;code&gt;id&lt;/code&gt; et une collection de &lt;code&gt;Product&lt;/code&gt;. Ces produits sont néanmoins stockés en base de données, avec Doctrine. Pour optimiser l&#039;application, nous ne voulons pas récupérer les produits tant qu&#039;ils ne sont pas affichés. Souvent, le total ainsi que le nombre d&#039;éléments sont suffisants.&lt;/p&gt;
&lt;p&gt;Observons nos objets métier :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Product&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $id,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $name,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $description,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $price &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 10&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Cart&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@param&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Product&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt; $products&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;     */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $id,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $products &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $total &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $quantity &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; addProduct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $product)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;products[] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $product;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;quantity&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;total &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $product&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;price;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;ProductRepository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; find&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $id)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Product&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Simule le chargement depuis la base de données&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Product&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($id, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;product-&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;description-&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; findByIds&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $ids)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Simule le chargement depuis la base de données&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $products &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($ids &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $id) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $products[] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($id);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $products;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rien de nouveau ici. Nous voulons utiliser ce type de code :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$productRepository &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ProductRepository&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$repository &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; CartRepository&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($productRepository);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$cart &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Cart&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;#cart-01&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$cart&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;addProduct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($productRepository&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;1&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$cart&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;addProduct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($productRepository&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;2&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$cart&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;addProduct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($productRepository&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;3&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// C&#039;est cette chaîne qui sera stockée dans Redis&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$serialized &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $repository&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;serialize&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($cart);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Une nouvelle requête HTTP arrive&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// On récupère la chaîne depuis Redis, et on hydrate le panier&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// À ce moment, aucune requête SQL n&#039;a été effectuée&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$newCart &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $repository&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;unserialize&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($serialized);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// À ce moment, toujours aucune requête SQL n&#039;a été effectuée&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($newCart&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;total);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Mais ce code va déclencher une requête SQL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$newCart&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;products;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant que nous avons vu les objets métier ainsi que leur utilisation, voyons comment coder le &lt;code&gt;CartRepository&lt;/code&gt; :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;CartRepository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    private&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ReflectionClass&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $reflector;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ProductRepository&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $productRepository,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;reflector &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ReflectionClass&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; serialize&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $cart)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; json_encode&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;id&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $cart&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;id,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;products&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; array_column&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($cart&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;products, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;id&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;total&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $cart&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;total,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;quantity&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $cart&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;quantity,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; unserialize&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $data)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Cart&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $previousCart &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; json_decode&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($data, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $newCart &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;reflector&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;newLazyGhost&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Cart&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $newCart) &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($previousCart)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $products &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;productRepository&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;findByIds&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($previousCart[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;products&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;            $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;reflector&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;products&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($newCart, $products);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;reflector&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;id&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setRawValueWithoutLazyInitialization&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($newCart, $previousCart[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;id&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;reflector&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;total&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setRawValueWithoutLazyInitialization&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($newCart, $previousCart[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;total&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;reflector&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;quantity&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setRawValueWithoutLazyInitialization&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($newCart, $previousCart[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;quantity&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $newCart;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nous avons une méthode &lt;code&gt;serialize&lt;/code&gt; qui nous permet d&#039;obtenir une représentation sous forme de chaîne de notre panier. C&#039;est elle qui sera stockée dans Redis. Elle ressemble à ceci :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot;{&amp;quot;id&amp;quot;:&amp;quot;#cart-01&amp;quot;,&amp;quot;products&amp;quot;:[&amp;quot;1&amp;quot;,&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;],&amp;quot;total&amp;quot;:30,&amp;quot;quantity&amp;quot;:3}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quand une nouvelle requête HTTP arrive, il faut désérialiser la représentation et reconstruire le panier. Comme dans le chapitre précédent, nous utilisons la réflexion.&lt;/p&gt;
&lt;p&gt;Comme d&#039;habitude, &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://3v4l.org/1uR3Y/rfc#vgit.master&quot;&gt;retrouvez le code en action ici&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Les &lt;em&gt;lazy ghosts&lt;/em&gt; et &lt;em&gt;lazy proxies&lt;/em&gt; sont vraiment très intéressants. Avec une API simple, ils permettent de retarder l&#039;instanciation de nos objets jusqu&#039;à ce que nous en ayons vraiment besoin. L&#039;API est assez intuitive et naturelle à prendre en main. Cependant, il est important de s&#039;exercer un peu pour en comprendre les subtilités. Cet article n&#039;a pas pu toutes les couvrir, ni montrer toutes les options disponibles. Nous vous invitons à lire la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://wiki.php.net/rfc/lazy-objects&quot;&gt;RFC&lt;/a&gt; pour en apprendre plus.&lt;/p&gt;
&lt;p&gt;Quelques points pourraient encore être améliorés :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;La possibilité de rendre une seule propriété lazy, et non toute la classe ;&lt;/li&gt;
&lt;li&gt;La possibilité de rendre un objet déjà existant lazy, de sorte que si une propriété non initialisée est appelée, l&#039;initialiseur soit déclenché.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PHP 8.4 n&#039;étant toujours pas officiellement sorti, cette fonctionnalité n&#039;est probablement pas encore utilisable. Mais nous sommes certains qu&#039;elle le sera très rapidement par &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.doctrine-project.org/2024/10/14/doctrine-core-team-meetup-2024.html&quot;&gt;Doctrine&lt;/a&gt; et Symfony (Nicolas l&#039;a conçue pour cela).&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/construire-un-chatbot-specialise-sur-vos-donnees-grace-a-lia-generative-et-php</id>
        <published>2024-10-24T11:42:00+02:00</published>
        <updated>2024-10-24T11:42:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/construire-un-chatbot-specialise-sur-vos-donnees-grace-a-lia-generative-et-php"/>
        <title>Construire un chatbot spécialisé sur vos données grâce à l&#039;IA générative et PHP</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="chatbot" />            <category term="ia" />            <category term="llm" />        <summary><![CDATA[Utilisée pour rédiger n&#039;importe quel type de contenus, pour faire des résumés ou encore intégrée à nos outils de développement, l&#039;IA, et en particulier l&#039;IA générative, a trouvé sa place dans bon nombre…]]></summary>
        <content type="html">
            &lt;p&gt;Utilisée pour rédiger n&#039;importe quel type de contenus, pour faire des résumés ou encore intégrée à nos outils de développement, l&#039;&lt;abbr title=&quot;Intelligence Artificielle&quot;&gt;IA&lt;/abbr&gt;, et en particulier l&#039;IA générative, a trouvé sa place dans bon nombre de secteurs. Basée sur des &lt;abbr title=&quot;Large Language Model&quot;&gt;LLM&lt;/abbr&gt; entraînés sur des quantités astronomiques de documents divers et variés, ces IA sont capables de comprendre et répondre à la plupart des questions qui leur sont posées. Ce qui ne les empêche cependant pas de donner des réponses erronées ou imprécises dans beaucoup de situations.&lt;/p&gt;
&lt;p&gt;Prenons l&#039;exemple de notre projet &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/jolicode/castor&quot;&gt;Castor&lt;/a&gt; et sa &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://castor.jolicode.com&quot;&gt;documentation en ligne&lt;/a&gt; et demandons à ChatGPT le rôle de la fonction &lt;code&gt;run()&lt;/code&gt; :&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/rag-ia/chatgpt-castor-exemple.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/rag-ia/chatgpt-castor-exemple.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;aspect-ratio: calc(816 / 757)&quot; src=&quot;https://jolicode.com//media/original/2024/rag-ia/chatgpt-castor-exemple.png&quot; alt=&quot;ChatGPT nous parle de Castor&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Même s&#039;il comprend à peu près de quoi nous parlons, l&#039;explication et l&#039;exemple fournis ne sont pas corrects : la fonction &lt;code&gt;line()&lt;/code&gt; n&#039;existe tout simplement pas. ChatGPT n&#039;a pas été entraîné spécifiquement sur la documentation de Castor mais a malgré tout &amp;quot;improvisé&amp;quot; une réponse.&lt;/p&gt;
&lt;p&gt;Heureusement, il existe un moyen de forcer ce genre d&#039;IA à être plus pertinente sur des domaines précis, sans avoir à dépenser du temps, ni d&#039;argent à entraîner nos propres modèles de LLM. C&#039;est notamment le principe de fonctionnement des &lt;em&gt;Retrieval-Augmented Generation&lt;/em&gt;, abrégés RAG.&lt;/p&gt;
&lt;h2&gt;Qu&#039;est-ce qu&#039;un RAG ?&lt;/h2&gt;
&lt;p&gt;Bien qu&#039;un modèle de langage (LLM) soit entraîné sur une vaste quantité de documents, il ne peut jamais être constamment à jour ni pertinent sur tous les sujets pour plusieurs raisons :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;son entraînement prend souvent du temps ;&lt;/li&gt;
&lt;li&gt;les documents en ligne (sur lesquels ils sont entraînés) changent régulièrement ;&lt;/li&gt;
&lt;li&gt;le modèle n&#039;a pas accès à toutes les informations du monde (et heureusement).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dans cet article, nous utiliserons la documentation de Castor comme exemple. Mais imaginez avoir une IA capable de répondre à vos questions sur n&#039;importe quel outil open source ou encore sur votre documentation interne du fonctionnement de votre entreprise.&lt;/p&gt;
&lt;p&gt;C&#039;est là qu&#039;intervient le concept de RAG (Retrieval-Augmented Generation). Il permet de découper le processus de génération de réponses en deux étapes distinctes :&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/rag-ia/2-etapes-rag.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/rag-ia/2-etapes-rag.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(1080 / 1029)&quot; src=&quot;https://jolicode.com//media/original/2024/rag-ia/2-etapes-rag.png&quot; alt=&quot;Les deux étapes d&#039;un RAG&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h3&gt;La récupération 🔎&lt;/h3&gt;
&lt;p&gt;Lorsque l&#039;utilisateur va saisir un message, nous allons nous occuper nous-mêmes de rechercher les documents pertinents pour lui répondre. Cette recherche pourrait se faire en faisant une simple recherche de mots-clefs dans notre ensemble de documents, par exemple avec Elasticsearch. Mais cela reste assez basique et les résultats peu satisfaisants. À la place, nous allons effectuer une recherche vectorielle dans notre corpus de documents. Cette idée peut faire peur au premier abord mais elle n&#039;est pas très compliquée à appréhender. Voyons maintenant comment cela fonctionne.&lt;/p&gt;
&lt;p&gt;Dans un premier temps, nous allons transformer chaque document en vecteurs. Ce vecteur est un objet mathématique qui permet de représenter le contexte et la sémantique du document en question. Ainsi, deux documents qui parlent d&#039;un sujet assez similaire auront des vecteurs assez proches dans l&#039;espace vectoriel rattaché.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/rag-ia/schema-vecteurs.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/rag-ia/schema-vecteurs.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;aspect-ratio: calc(578 / 476)&quot; src=&quot;https://jolicode.com//media/original/2024/rag-ia/schema-vecteurs.png&quot; alt=&quot;Exemple de représentation de documents dans un espace vectoriel à 3 dimensions&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Exemple de représentation de documents dans un espace vectoriel à trois dimensions.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Dans cet exemple simplifié, on voit que les deux chapitres de la documentation à propos des fonctions &lt;code&gt;run()&lt;/code&gt; et &lt;code&gt;capture()&lt;/code&gt; sont assez proches, et plutôt éloignés du chapitre concernant l&#039;utilisation et la souscription aux événements de Castor. Ici, nous avons trois dimensions, mais dans la réalité, nous utiliserons plutôt des vecteurs avec des milliers de dimensions, afin d&#039;être capable de capturer une sémantique la plus précise possible pour chaque document.&lt;/p&gt;
&lt;p&gt;Si vous n&#039;avez jamais fait de RAG, vous vous demandez sûrement comment obtenir un vecteur à partir d&#039;un simple texte ? La réponse est plutôt simple et presque décevante : on va demander à une IA ! Il existe des modèles spécialement entraînés pour capturer la sémantique d&#039;un texte et en extraire le vecteur correspondant. En anglais, cette représentation d&#039;une valeur (textuelle, image ou autre) dans un format adapté (ici un vecteur) à un traitement par un algorithme s&#039;appelle &amp;quot;embedding&amp;quot;.&lt;/p&gt;
&lt;p&gt;Une fois nos vecteurs calculés et stockés pour chaque document, nous pouvons nous occuper des entrées de l&#039;utilisateur. Chaque fois qu&#039;il interagit avec notre RAG, nous allons appliquer la même logique de vectorisation à son message. Avec ce nouveau vecteur, nous serons capables de faire une recherche vectorielle parmis tous nos documents afin de déterminer ceux les plus proches de la question de l&#039;utilisateur.&lt;/p&gt;
&lt;h3&gt;La génération ✍️&lt;/h3&gt;
&lt;p&gt;Une fois que nous avons identifié les documents les plus pertinents, nous demanderons à notre LLM préféré de formuler une réponse pour l&#039;utilisateur. Mais cette fois, nous allons lui fournir nous-mêmes les documents/connaissances sur lesquels il doit s&#039;appuyer.&lt;/p&gt;
&lt;p&gt;Cerise sur le gâteau, nous pouvons même lui demander :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;de s&#039;appuyer uniquement sur ces contenus fournis en ignorant son éventuelle connaissance interne ;&lt;/li&gt;
&lt;li&gt;d&#039;ajouter dans la réponse le lien correspondant au contenu, permettant à l&#039;utilisateur de vérifier les sources.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/rag-ia/demo.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/rag-ia/demo.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(1168 / 615)&quot; src=&quot;https://jolicode.com//media/original/2024/rag-ia/demo.png&quot; alt=&quot;La démo fonctionnelle à la fin de l&#039;article&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Mais assez de théorie pour l&#039;instant. Il est temps de passer à la pratique et de voir comment construire concrètement l&#039;exemple que nous venons d&#039;évoquer.&lt;/p&gt;
&lt;h2&gt;Mise en pratique&lt;/h2&gt;
&lt;p&gt;Nous allons chercher à créer un chatbot qui sera capable de répondre à n&#039;importe quelle question à propos d&#039;un site web qu&#039;il aura auparavant ingéré. Pour ce faire, nous allons créer une application Symfony dans un projet dockerisé. Pour gagner du temps, nous nous baserons sur un template &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/jolicode/docker-starter&quot;&gt;docker-starter&lt;/a&gt; pour nous consacrer sur l&#039;essentiel.&lt;/p&gt;
&lt;p&gt;Tout le code de cette démo se trouve dans un &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/pyrech/rag-ai-documentation&quot;&gt;repository open source&lt;/a&gt;, n&#039;hésitez pas à y jeter un œil pour approfondir cet article.&lt;/p&gt;
&lt;h3&gt;La base de données vectorielle 🐘&lt;/h3&gt;
&lt;p&gt;Pour stocker nos documents texte et leurs représentations vectorielles, tout en permettant une recherche vectorielle efficace, nous allons utiliser... PostgreSQL ! Plus précisément, PostgreSQL associé à l&#039;extension &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/pgvector/pgvector&quot;&gt;pgvector&lt;/a&gt;, qui ajoute les fonctionnalités nécessaires pour effectuer des recherches basées sur la similarité vectorielle. Dans notre docker-compose.yml, on peut utiliser directement l&#039;image &lt;code&gt;pgvector/pgvector&lt;/code&gt; qui ajoute uniquement l&#039;extension pgvector à l&#039;image officielle de PostgreSQL :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# …&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    # …&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    postgres&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        image&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;pgvector/pgvector:pg16&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        # …&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Découpage des pages en documents ✂️&lt;/h3&gt;
&lt;p&gt;Dans cette démo, j&#039;ai choisi de crawler le site de la documentation au lieu de récupérer directement les documents markdown car je souhaitais créer un chatbot capable d&#039;ingérer n&#039;importe quel site, et pas uniquement une documentation markdown en particulier.&lt;/p&gt;
&lt;p&gt;Plutôt que de créer un seul document pour chaque page, j&#039;ai décidé que chaque page serait séparée en plusieurs sections. L&#039;idée est d&#039;avoir des documents plus granulaires pour aiguiller au mieux notre IA plutôt qu&#039;un contenu conséquent qui risquerait de la perturber. Chaque fois qu&#039;un titre sera détecté dans la page, une nouvelle section sera stockée en base de données. Dans l&#039;idéal, chaque titre aura un &lt;em&gt;id&lt;/em&gt; servant d&#039;ancre afin que les URLs des sources soient plus précises pour l&#039;utilisateur (c&#039;est le souvent le cas avec les documentations générées depuis du markdown ou équivalent).&lt;/p&gt;
&lt;p&gt;Imaginons la structure d&#039;une page à l&#039;URL &lt;code&gt;/foobar&lt;/code&gt; :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; id&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;lorem&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;Lorem&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;Ipsum dolor sit amet&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; id&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;consectetur&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;Consectetur&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;          &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;Adipiscing elit&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;          &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;h3&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; id&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;sed&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;Sed&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;h3&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;Do eiusmod tempor&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;          &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;h3&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; id&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;incididunt&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;Incididunt&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;h3&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;Ut labore et&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; id&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;dolore&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;Dolore&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;h2&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;          &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;Magna aliqua&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cela nous donnera les cinq documents suivants :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;url: /foobar#lorem
title: Lorem
content: Ipsum dolor sit amet

—

url: /foobar#consectetur
title: Consectetur
content: Adipiscing elit

—

url: /foobar#sed
title: Sed
content: Do eiusmod tempor

—

url: /foobar#incididunt
title: Incididunt
content: Ut labore et

—

url: /foobar#dolore
title: Dolore
content: Magna aliqua
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voici un exemple simple de parsing du HTML et de découpage du contenu en fonction des titres à l&#039;aide du &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://symfony.com/doc/current/components/dom_crawler.html&quot;&gt;DomCrawler&lt;/a&gt; de Symfony :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\DomCrawler\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Crawler&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; DomCrawler;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;DocumentExtractor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    /** &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@return&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Document&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; extract&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $url, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $html)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $crawler &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; DomCrawler&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($html);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $crawler &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $crawler&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;h1, h2, h3, h4, h5, h6&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Iterate over the h1-h6 titles&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($crawler &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $node) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $documentUrl &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $url;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;            // Attach an anchor to the URL if title has an id&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($node &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;instanceof&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; \DOMElement&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $node&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;hasAttribute&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;id&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                $documentUrl &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;#&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $node&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;id&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $title &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;cleanContent&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($node&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;textContent);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $content &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $contentNode &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $node&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;nextSibling;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;            // Parse the content until the next title&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            while&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($contentNode &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; !&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;isTitle&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($contentNode)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                $content &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $contentNode&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;ownerDocument&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;saveHTML&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($contentNode);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                $contentNode &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $contentNode&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;nextSibling;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $documents[] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Document&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                $documentUrl,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                $title,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;                $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;cleanContent&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($content),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $documents;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // …&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Crawl du site 🕸️&lt;/h3&gt;
&lt;p&gt;Pour crawler le site, nous utiliserons le package &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/spatie/crawler&quot;&gt;&lt;code&gt;spatie/crawler&lt;/code&gt;&lt;/a&gt;, qui, grâce à son utilisation du &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://refactoring.guru/design-patterns/observer&quot;&gt;design pattern Observer&lt;/a&gt;, nous permet de nous brancher facilement dans le crawl et analyser chaque page, une par une :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Spatie\Crawler\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Crawler&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; SpatieCrawler;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Spatie\Crawler\CrawlProfiles\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;CrawlInternalUrls&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Crawler&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Observer&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $observer) {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    /** &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@return&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Document&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;  */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; crawl&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $url)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;observer&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;reset&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        SpatieCrawler&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setCrawlObserver&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;observer)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setCrawlProfile&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; CrawlInternalUrls&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($url))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;acceptNofollowLinks&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setMaximumDepth&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;startCrawling&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($url)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;observer&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getDocuments&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voici la définition de notre Observer, qui sera chargée d&#039;appeler le &lt;code&gt;DocumentExtractor&lt;/code&gt; définit plus tôt :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; GuzzleHttp\Exception\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;RequestException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Psr\Http\Message\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ResponseInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Psr\Http\Message\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;UriInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Psr\Log\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;LoggerInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Spatie\Crawler\CrawlObservers\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;CrawlObserver&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Contracts\Service\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ResetInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Observer&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;CrawlObserver&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;ResetInterface&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    /** &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@var&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Document&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $documents &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; DocumentExtractor&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $extractor,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; LoggerInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $logger,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; reset&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;documents &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; crawled&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        UriInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $url,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        ResponseInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $response,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        ?&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;UriInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $foundOnUrl &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        ?string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $linkText &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    )&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $html &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;) $response&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getBody&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$html) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $stringUrl &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;) $url;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;documents &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; array_merge&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;documents, &lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;extractor&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($stringUrl, $html));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; crawlFailed&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        UriInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $url,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        RequestException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $requestException,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        ?&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;UriInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $foundOnUrl &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        ?string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $linkText &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    )&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Could not crawl this url.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;url&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;) $url,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;exception&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $requestException&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; finishedCrawling&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintenant que l&#039;on a récupéré tous nos documents, nous pouvons passer à l&#039;étape d&#039;après : calculer leur représentation vectorielle.&lt;/p&gt;
&lt;h3&gt;Vectorisation des documents ➗&lt;/h3&gt;
&lt;p&gt;C&#039;est ici que nous commençons à mettre les mains dans le camboui. Comment faire pour obtenir les &amp;quot;embeddings&amp;quot;, c&#039;est-à -dire, les vecteurs correspondant à nos documents ? Il faut savoir que la plupart des services / modèles d&#039;IA générative proposent directement des outils pour calculer nos embeddings.&lt;/p&gt;
&lt;p&gt;OpenAI ne fait pas exception et propose une API dédiée. Rien de bien sorcier finalement, on se contentera d&#039;appeler ce endpoint en lui passant le texte à vectoriser :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;OpenAIClient&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        #[Autowire(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;%env(OPENAI_API_KEY)%&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $apiKey,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; HttpClientInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $client,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    /** &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@return&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; getEmbeddings&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $content)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $data &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/v1/embeddings&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;model&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;text-embedding-3-small&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// &quot;small&quot; model will produce vectors of 1536 dimensions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;input&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $content,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($data[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;data&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;embedding&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            throw&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; \RuntimeException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Could not get embeddings from OpenAI response.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $data[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;data&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;embedding&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    private&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; call&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $endpoint, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $data)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $response &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;POST&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;https://api.openai.com{&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$endpoint&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;}&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;headers&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                &#039;Authorization&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;Bearer {&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;apiKey&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;}&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;json&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $data,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $response&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pour chaque document, on appellera cet endpoint en passant le contenu du document à vectoriser. On récupère ainsi, pour chacun, un vecteur qui n&#039;est finalement qu&#039;un tableau de float. En utilisant le modèle &lt;code&gt;text-embedding-3-small&lt;/code&gt; d&#039;OpenAI, la taille du vecteur sera de 1536 dimensions. Il est tout à fait possible de &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://platform.openai.com/docs/guides/embeddings/embedding-models&quot;&gt;changer de modèle&lt;/a&gt;. Cependant, plus un modèle retourne de gros vecteurs, plus il sera précis, mais plus il coûtera cher côté OpenAI et plus il demandera de place à stocker dans notre base de données.&lt;/p&gt;
&lt;p&gt;Si vous &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/pyrech/rag-ai-documentation/blob/main/application/src/OpenAI/Client.php#L26-L40&quot;&gt;regardez le code de la démo&lt;/a&gt;, vous verrez que j&#039;ai ajouté une couche de cache sur tous les appels à OpenAI. Comme l&#039;API n&#039;est pas gratuite, cela permet d&#039;éviter de consommer des crédits quand les contenus à vectoriser ne changent pas entre les crawls.&lt;/p&gt;
&lt;h3&gt;Stockage des documents et leurs vecteurs 💾&lt;/h3&gt;
&lt;p&gt;J&#039;ai choisi d&#039;utiliser le package &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/partITech/doctrine-pgvector&quot;&gt;partITech/doctrine-pgvector&lt;/a&gt; qui intègre dans Doctrine tout ce dont on a besoin pour tirer parti de l&#039;extension pgvector.&lt;/p&gt;
&lt;p&gt;Pour stocker nos vecteurs, nous aurons besoin d&#039;utiliser le type &lt;code&gt;vector&lt;/code&gt;. Il faut donc l&#039;ajouter dans la configuration DBAL de Doctrine pour pouvoir l&#039;utiliser dans notre entité :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# config/packages/doctrine.yaml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;doctrine&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    dbal&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        # …&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        types&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            vector&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;Partitech\DoctrinePgVector\Type\VectorType&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nous pouvons maintenant définir notre document avec tous les champs nécessaires. Pour le champ &lt;code&gt;embeddings&lt;/code&gt;, il faudra spécifier la taille attendue de notre vecteur, soit dans notre cas &lt;code&gt;1536&lt;/code&gt; :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[ORM\Entity()]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Document&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; const&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; VECTOR_LENGTH&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 1536&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[ORM\Id]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[ORM\GeneratedValue(strategy: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;NONE&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[ORM\Column(type: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;guid&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, nullable: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $id;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    /** &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@var&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; float[]&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[ORM\Column(type: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;vector&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, length: &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;VECTOR_LENGTH&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $embeddings;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        #[ORM\Column(type: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;text&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $url,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        #[ORM\Column(type: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;text&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $title,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        #[ORM\Column(type: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;text&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $content,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;id &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; \&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;uuid_create&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà à quoi ressemble nos documents et leurs vecteurs dans notre base de données :&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/rag-ia/documents-et-vecteurs-bdd.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/rag-ia/documents-et-vecteurs-bdd.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(1140 / 546)&quot; src=&quot;https://jolicode.com//media/original/2024/rag-ia/documents-et-vecteurs-bdd.png&quot; alt=&quot;Nos documents et leurs vecteurs stockés en base&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Nos documents étant maintenant stockés en base, nous pouvons nous attaquer à la recherche de proximité sémantique de nos documents avec un message saisi par l&#039;utilisateur.&lt;/p&gt;
&lt;h3&gt;Recherche vectorielle 🔎&lt;/h3&gt;
&lt;p&gt;Vous vous souvenez du schéma en début d&#039;article illustrant trois vecteurs dans un espace à trois dimensions ? C&#039;est maintenant que nous allons mesurer à quel point ces vecteurs sont similaires entre eux. Heureusement, pas besoin de réinventer la roue pour y parvenir : les mathématiques nous fournissent déjà les outils nécessaires. Dans ce cas, nous utiliserons la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://fr.wikipedia.org/wiki/Similarit%C3%A9_cosinus&quot;&gt;similarité cosinus&lt;/a&gt;, qui calcule le cosinus de l&#039;angle entre deux vecteurs. Inutile de plonger trop profondément dans les détails mathématiques : l&#039;extension &lt;code&gt;pgvector&lt;/code&gt; nous offre directement une fonction pratique pour cela, &lt;code&gt;&#039;cosine_similarity&#039;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;L&#039;extension Doctrine pgvector supporte cette fonction, nous n&#039;aurons qu&#039;à l&#039;enregistrer dans la configuration de Doctrine :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# config/packages/doctrine.yaml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;doctrine&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    orm&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        # …&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        dql&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            string_functions&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                cosine_similarity&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;Partitech\DoctrinePgVector\Query\CosineSimilarity&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nous utiliserons la fonction &lt;code&gt;cosine_similarity()&lt;/code&gt; dans la section &lt;code&gt;ORDER BY&lt;/code&gt;, qui nous permettra de trier les documents par ordre de similarité par rapport à la représentation vectorielle du message de l&#039;utilisateur. Il suffira de passer le message vectorisé (le tableau de nombre flottants &lt;code&gt;$embeddings&lt;/code&gt;) de l&#039;utilisateur à la méthode &lt;code&gt;findNearest()&lt;/code&gt; du DocumentRepository que nous allons créer :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; App\Entity\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Doctrine\Bundle\DoctrineBundle\Repository\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ServiceEntityRepository&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Doctrine\Persistence\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ManagerRegistry&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;/** @extends ServiceEntityRepository&amp;#x3C;Document&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;DocumentRepository&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;ServiceEntityRepository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // …&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@param&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; float[]&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt; $embeddings&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;     *&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@return&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Document&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;[]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;     */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; findNearest&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $embeddings)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;createQueryBuilder&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;s&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;orderBy&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;cosine_similarity(s.embeddings, :embeddings)&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;DESC&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setParameter&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;embeddings&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $embeddings, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;vector&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setMaxResults&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getQuery&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getResult&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Une fois que l&#039;on a trouvé les cinq documents les plus similaires à ce qu&#039;a demandé notre utilisateur, il ne nous reste qu&#039;à demander à notre AI de générer une réponse en s&#039;appuyant sur ces documents en question.&lt;/p&gt;
&lt;h3&gt;Génération de la réponse à l&#039;utilisateur 💬&lt;/h3&gt;
&lt;p&gt;La plupart des modèles d&#039;IA générative fournissent une API permettant de configurer le prompt qui sera envoyé à l&#039;IA pour lui expliquer ce que l&#039;on attend d&#039;elle. Le format attendu de la réponse, le ton à employer mais surtout les informations sur lesquelles s&#039;appuyer pour répondre à l&#039;utilisateur.&lt;/p&gt;
&lt;p&gt;Plus tôt, nous avons utilisé OpenAI pour le calcul des &lt;code&gt;embeddings&lt;/code&gt;. Par simplicité, c&#039;est donc avec ce service que nous continuerons pour la partie génération de la réponse.
Ici nous utiliserons l&#039;endpoint &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://platform.openai.com/docs/api-reference/chat/create&quot;&gt;&lt;code&gt;/v1/chat/completions&lt;/code&gt;&lt;/a&gt;. On enverra à cet endpoint 2 choses :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le modèle à employer (on utilisera simplement &lt;code&gt;gpt-4o&lt;/code&gt; qui est disponible à l&#039;heure où j&#039;écris cet article) ;&lt;/li&gt;
&lt;li&gt;les &amp;quot;messages&amp;quot; qui permettront au modèle de comprendre la situation et d&#039;y répondre.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OpenAI supporte plusieurs type de &amp;quot;messages&amp;quot; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;les messages &lt;code&gt;system&lt;/code&gt;, qui permettent d&#039;envoyer des prompt et de donner tout type d&#039;indications notre modèle pour l&#039;aiguiller dans sa génération ;&lt;/li&gt;
&lt;li&gt;les messages &lt;code&gt;user&lt;/code&gt; qui contiendront, comme leur nom l&#039;indique, les messages saisi par l&#039;utilisateur ;&lt;/li&gt;
&lt;li&gt;les messages &lt;code&gt;assistant&lt;/code&gt; qui contiendront les réponses précédentes générées par notre assistant, ce qui donnera une espèce d&#039;historique / de mémoire à l&#039;assistant afin d&#039;offrir une conversation plus naturelle (pas besoin de tout re-préciser à chaque message, l&#039;assistant se souviendra du contexte) ;&lt;/li&gt;
&lt;li&gt;les messages &lt;code&gt;tool&lt;/code&gt; qui fourniront des fonctions / outils que l&#039;IA pourra appeler pour récupérer plus d&#039;informations si nécessaire, faire des requêtes à une API, etc. (nous n&#039;en aurons toutefois pas besoin dans le cadre de cet article).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Afin de conserver l&#039;historique et être capable de la redonner au LLM, nous stockerons tous les messages (utilisateurs et réponses de l&#039;IA) dans une entité &lt;code&gt;Message&lt;/code&gt;. Ainsi, chaque fois que nous demanderons à notre IA une réponse, en plus des documents pertinents, nous lui enverrons l&#039;historique de la conversation (&lt;code&gt;$historyMessages&lt;/code&gt; dans l&#039;exemple suivant).&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;OpenAIClient&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // …&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;     * &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;@param&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Document&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt; $documents&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;     */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; getAnswer&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $documents, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $historyMessages)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Send prompt to the IA in a `system` message&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $prompt &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;Notre prompt. Définit plus bas dans l&#039;article&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $messages &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                &#039;role&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;system&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                &#039;content&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $prompt,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Send most similar documents in a new `system` message&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $relevantInformation &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;Relevant information: \n&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($documents &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $document) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $relevantInformation &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.=&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; json_encode&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                    &#039;title&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $document&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;title,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                    &#039;content&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $document&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;content,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                    &#039;url&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $document&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;url,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                ]) &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $messages[] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;role&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;system&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;content&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $relevantInformation,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Send conversation history to give IA some context about previous messages&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($historyMessages &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $message) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $messages[] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                &#039;role&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $message&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;isMe &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;user&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; :&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;assistant&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                &#039;content&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $message&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;content,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $data &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/v1/chat/completions&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;model&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;gpt-4o&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;messages&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $messages,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($data[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;choices&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;message&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;content&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            throw&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; \RuntimeException&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Could not get completion from OpenAI response.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $data[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;choices&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;message&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;content&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Concentrons-nous sur notre prompt, car c&#039;est finalement lui qui va définir le cœur même de notre application. Pour tirer pleinement parti de notre RAG, nous allons pouvoir demander à l&#039;IA de s&#039;appuyer sur les informations/documents que nous aurons trouvés par nous-mêmes, ainsi que de répondre de manière concise, en utilisant les exemples présents dans notre documentation. Autre valeur ajoutée, nous allons pouvoir lui demander de sourcer ses réponses, afin que l&#039;utilisateur puisse vérifier les dires de notre assistant. Voici un exemple de prompt utilisé dans notre POC :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;You are a friendly chatbot.
You respond in a concise, technically credible tone (but do not hesitate to add examples if needed).
You only use information from the provided information.
Please add the link of the relevant documents to the end of your response (do not invent url, only use the one we provided).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OpenAI nous renverra ainsi la réponse à donner à l&#039;utilisateur. Le message généré par OpenAI utilise le format markdown, ce qui nous permettra par la suite de le mettre en forme dans notre application :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;exemples de code avec les balises &lt;code&gt;code&lt;/code&gt; et &lt;code&gt;pre&lt;/code&gt; et la coloration syntaxique qui va bien pour chaque langage ;&lt;/li&gt;
&lt;li&gt;liens ;&lt;/li&gt;
&lt;li&gt;sémantiques avec les balises &lt;code&gt;em&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;,  etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le prompt ci-dessus est très certainement perfectible. La création de prompts adaptés est d&#039;ailleurs une compétence à part entière, que l&#039;on retrouvera souvent sous le terme de &amp;quot;prompt engineering&amp;quot;. Mais nous n&#039;irons pas plus loin ici, car là n&#039;est pas le but de cet article. En revanche, il est désormais temps de brancher ensemble les différentes sections que nous avons vu afin de créer notre POC.&lt;/p&gt;
&lt;h3&gt;Le POC 🛠️&lt;/h3&gt;
&lt;p&gt;La partie crawl, découpages des pages en documents, vectorisation de chacun d&#039;entre eux et stockage en base de données se fera dans une commande Symfony classique :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; App\Crawl\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Crawler&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; App\Entity\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; App\OpenAI\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Doctrine\ORM\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;EntityManagerInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Attribute\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AsCommand&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Command\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Command&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Input\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;InputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Output\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;OutputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Style\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;SymfonyStyle&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\DependencyInjection\Attribute\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Autowire&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[AsCommand(name: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;app:crawl&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, description: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Crawl the website to extract content&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;CrawlCommand&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;Command&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Crawler&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $crawler,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Client&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $client,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; EntityManagerInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $entityManager,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        #[Autowire(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;%env(DOCUMENTATION_URL)%&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $documentationUrl,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;        parent&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    protected&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; execute&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;InputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $input, &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;OutputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $output)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $io &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; SymfonyStyle&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($input, $output);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $url &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;documentationUrl;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; ===&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; filter_var&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($url, &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;\FILTER_VALIDATE_URL&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $io&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Invalid url.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            return&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Command&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;FAILURE&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $io&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Crawling the website.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $documents &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;crawler&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;crawl&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($url);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $io&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Found %d documents.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, \&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($documents)));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $io&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Extracting embeddings.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($documents &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $document) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $embeddings &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getEmbeddings&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($document&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;content);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $document&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setEmbeddings&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($embeddings);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $io&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Persisting data.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($documents &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $document) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;            $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;entityManager&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($document);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;entityManager&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;flush&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $io&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Finished crawling this website.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Command&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SUCCESS&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il faudra lancer cette commande une première fois, avant toute interaction avec le front, pour avoir les documents disponibles en base de données.&lt;/p&gt;
&lt;p&gt;En parlant du front, c&#039;est justement le moment de s&#039;en occuper. Et on va se faire plaisir et utiliser les dernières fonctionnalités de Symfony :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AssetMapper et ImportMap pour construire la stack front sans besoin de configurer node ni webpack&lt;/li&gt;
&lt;li&gt;Symfony UX et un Twig component pour avoir un minimum d&#039;interactivité sans avoir besoin d&#039;écrire de JS.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On ajoutera également le package &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://commonmark.thephpleague.com/&quot;&gt;&lt;code&gt;league/commonmark&lt;/code&gt;&lt;/a&gt; pour la conversion markdown vers html des messages de l&#039;IA et nous en ferons une extension Twig pour l&#039;utiliser facilement dans notre component :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; League\CommonMark\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;MarkdownConverter&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Twig\Extension\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AbstractExtension&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Twig\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;TwigFilter&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;MarkdownExtension&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;AbstractExtension&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; MarkdownConverter&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $markdownConverter,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; getFilters&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; TwigFilter&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;markdown&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;markdownConverter&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;convert&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;), [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;is_safe&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;html&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On branche ensuite toute la partie formulaire, affichage de la conversation, recherche des documents et génération de la réponse utilisateur dans un twig component. Voici ce qu&#039;il se passe dans le traitement du formulaire du chat :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// On récupère le message de l&#039;utilisateur&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;submitForm&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$userPrompt &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getForm&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;userPrompt&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getData&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// On le stocke dans l&#039;historique de la conversation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$message &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Message&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($userPrompt, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;entityManager&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($message);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;entityManager&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;flush&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// On vectorise ce message&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$embeddings &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getEmbeddings&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($userPrompt);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Puis on cherche les documents les plus proches sémantiquement&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$documents &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;documentRepository&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;findNearest&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($embeddings);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// On demande à l&#039;IA de générer notre réponse en fonction de la conversation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$messages &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;messageRepository&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;findLatest&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$answer &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getAnswer&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($documents, $messages);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// On stocke la réponses dans la conversation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$message &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Message&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($answer, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;entityManager&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($message);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;entityManager&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;flush&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;resetForm&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// On peut maintenant afficher la conversation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// …&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Avec bootstrap et un peu de CSS custom, on arrive à un POC qui ressemble à ça ✨ (oui, vous l&#039;aurez deviné, je ne suis pas développeur frontend) :&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/rag-ia/poc.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/rag-ia/poc.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(1870 / 1016)&quot; src=&quot;https://jolicode.com//media/original/2024/rag-ia/poc.png&quot; alt=&quot;Le POC fonctionnel&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Et voilà qui conclut cet article et notre démo. Le POC fonctionne même s&#039;il pourrait être amélioré sur de très nombreux points : gestion d&#039;utilisateurs et conversation propre à chacun, micro interactions dans le navigateur, possibilité de laisser à l&#039;utilisateur d&#039;analyser le site de son choix, etc 🤓.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;À travers cet article, nous avons vu ce qu&#039;il se cachait derrière le concept de RAG et comment cela fonctionnait. Nous avons pu découvrir les différentes notions que ceux-ci impliquent, comme les espaces vectoriels, les embeddings ou encore la similarité cosinus.&lt;/p&gt;
&lt;p&gt;Mais surtout, nous avons pu voir comment nous pouvions créer un RAG nous-mêmes, sous la forme d&#039;un chatbot permettant de répondre à des questions sur un corpus documentaire bien à nous. Je rappelle que le code source du POC est &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/pyrech/rag-ai-documentation&quot;&gt;disponible publiquement&lt;/a&gt; si jamais vous vouliez le tester par vous-même ou explorer plus en profondeur son fonctionnement.&lt;/p&gt;
&lt;p&gt;Dans cette démonstration, nous n&#039;avons utilisé que les outils fournis par OpenAI. L&#039;objectif était surtout de simplifier l&#039;article en utilisant un service &amp;quot;clé en main&amp;quot; qui ne demande quasi rien d&#039;autre que d&#039;appeler une API pour obtenir les données dont nous avions besoin. Cependant, bien que cette société ait aidé à la démocratisation de l&#039;IA auprès du grand public, notamment avec son outil ChatGPT, elle est loin d&#039;être irréprochable. On peut d&#039;ailleurs s&#039;interroger sur les dangers de lui fournir tout le contenu de nos documents, potentiellement privés si l&#039;on travaille sur une documentation interne par exemple.&lt;/p&gt;
&lt;p&gt;Le domaine de l&#039;IA a connu un essor considérable ces dernières années, et il est désormais plus simple que jamais de faire tourner nos propres modèles en local pour éviter toute fuite de données, comme par exemple &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://ollama.com/&quot;&gt;Ollama&lt;/a&gt;. N&#039;hésitez pas à laisser un commentaire si vous seriez intéressé par un prochain article qui utiliserait un modèle local à la place de ceux d&#039;OpenAI.&lt;/p&gt;
&lt;h3&gt;Sources&lt;/h3&gt;
&lt;p&gt;Quelques sources supplémentaires utilisées pour mes recherches et la rédaction de cet article et son POC :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://medium.com/tessell-dbaas/postgresql-as-vector-database-create-llm-apps-with-pgvector-64677de48fc2&quot;&gt;https://medium.com/tessell-dbaas/postgresql-as-vector-database-create-llm-apps-with-pgvector-64677de48fc2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.timescale.com/blog/postgresql-as-a-vector-database-create-store-and-query-openai-embeddings-with-pgvector/&quot;&gt;https://www.timescale.com/blog/postgresql-as-a-vector-database-create-store-and-query-openai-embeddings-with-pgvector/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://help.openai.com/en/articles/8868588-retrieval-augmented-generation-rag-and-semantic-search-for-gpts&quot;&gt;https://help.openai.com/en/articles/8868588-retrieval-augmented-generation-rag-and-semantic-search-for-gpts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.oracle.com/fr/artificial-intelligence/generative-ai/retrieval-augmented-generation-rag/&quot;&gt;https://www.oracle.com/fr/artificial-intelligence/generative-ai/retrieval-augmented-generation-rag/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://datascientest.com/le-word-embedding&quot;&gt;https://datascientest.com/le-word-embedding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.nextstart.ai/2024/01/19/comment-fonctionne-le-rag-en-ia-generative/&quot;&gt;https://www.nextstart.ai/2024/01/19/comment-fonctionne-le-rag-en-ia-generative/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://ollama.com/blog/embedding-models&quot;&gt;https://ollama.com/blog/embedding-models&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/du-travail-solitaire-a-lengagement-collectif-la-dynamique-de-la-veille</id>
        <published>2024-10-22T15:15:00+02:00</published>
        <updated>2024-10-22T15:15:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/du-travail-solitaire-a-lengagement-collectif-la-dynamique-de-la-veille"/>
        <title>Du travail solitaire à l’engagement collectif : la dynamique de la veille</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="veille" />            <category term="opensource" />        <summary><![CDATA[Quand Cyril et Audrey de l’École O’Clock m’ont proposé d’animer un meetup autour de la veille technologique, je me suis souvenu d’une phrase du podcast IFTTD :

Le problème avec un ingénieur, c’est que…]]></summary>
        <content type="html">
            &lt;p&gt;Quand Cyril et Audrey de &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://oclock.io/&quot;&gt;l’École O’Clock&lt;/a&gt; m’ont proposé d’animer un meetup autour de la veille technologique, je me suis souvenu d’une phrase du &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.ifttd.io/&quot;&gt;podcast IFTTD&lt;/a&gt; :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Le problème avec un ingénieur, c’est que lorsqu’on lui montre un ciel bleu, ce qu’il voit, c’est la diffraction de la lumière.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Même sans être développeur moi-même, je connais cette obsession pour les détails techniques que peuvent avoir mes équipes. C’est précisément cette passion pour la technicité que la veille technologique permet de canaliser, tout en gardant une vision globale.&lt;/p&gt;
&lt;p&gt;Une autre chose que j’ai retenu récemment,  c’est &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.ifttd.io/episodes/le-bon-sens-du-dev&quot;&gt;l’interview de Maya Noel&lt;/a&gt;, dirigeante de &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://francedigitale.org/&quot;&gt;France Digitale&lt;/a&gt;, qui en parle si bien. Elle a mis l’accent sur un point essentiel : &lt;strong&gt;la maîtrise technique, c’est bien, mais ça ne suffit plus&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Il faut constamment évoluer. Les technologies bougent, et ce qui compte vraiment, c’est l’apprentissage continu et l’adaptation. Peu importe le niveau d’expérience, ce qui fait la différence, c’est la curiosité et la capacité à comprendre le contexte. Ça semble un peu bateau comme ça, non ? Mais avoir un avis sur les technologies qu’on manipule est essentiel, même si on a pas tous 10 ans d’expérience. Maya, qui vient du monde du recrutement, compare une équipe de développeurs à une équipe de football : il est impossible d’avoir uniquement des attaquants. Les meilleures entreprises recherchent des profils équilibrés, des développeurs capables d’apprendre et de s’adapter sur le long terme. Savoir coder, c’est la base, mais comprendre pourquoi vous codez, pour qui vous le faites, et comment cela s’intègre dans un projet global (et avoir une appétence business), c’est ce qui permet de se démarquer.&lt;/p&gt;
&lt;p&gt;Un autre exemple que j’ai partagé durant ce talk est une conférence que j’ai vue récemment au Forum PHP, présentée par Pascal Martin : &lt;a href=&quot;https://jolicode.com//blog/ce-que-nous-avons-retenu-du-forum-php-2024-jour-2#l-aventure-d-une-requete-http-ou-le-chemin-de-la-vie-des-devs&quot;&gt;L’aventure d’une requête HTTP&lt;/a&gt;. Il aime poser cette question en entretien : &amp;quot;Que se passe-t-il quand vous tapez une URL dans votre navigateur ?&amp;quot; Ce n’est pas un piège, mais une ouverture vers la complexité du web moderne. Derrière chaque requête HTTP, il existe des couches entières de technologie. C’est là que &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/T-shaped_skills&quot;&gt;l’approche T-shape&lt;/a&gt; des compétences prend tout son sens : une base solide en connaissances larges est nécessaire, mais c’est la profondeur dans certains domaines qui permet de se distinguer. Pour développer cette profondeur, il faut être en veille constante. Comme Pascal l’a si bien dit :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ce métier, c’est un assemblage de multiples compétences.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;La veille technologique : un impératif ? 🤷&lt;/h2&gt;
&lt;p&gt;La veille ne se résume pas à scroller ou écouter des podcasts, c’est un travail de fond qui nécessite également de s’intéresser au marché. Il y a pour cela par exemple le &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://barometre.afup.org/about&quot;&gt;baromètre annuel de l’AFUP&lt;/a&gt;, et bien sûr, &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://survey.stackoverflow.co/2024/&quot;&gt;l’enquête de StackOverflow&lt;/a&gt;. Cette enquête (Dimension international même si elle reflète plutôt un panel américain avec ±19% de répondant basés aux État-Unis), à laquelle plus de 65 000 développeurs participent chaque année, offre une vision globale sur les technologies et outils qu’ils utilisent, ceux qu’ils souhaitent apprendre, et même leur point de vue sur l’IA et l’expérience développeur.&lt;/p&gt;
&lt;p&gt;Il est également essentiel d’amener un peu d’humain dans sa veille. Depuis que je suis à JoliCode, &lt;a href=&quot;https://jolicode.com//nos-metiers/conferences&quot;&gt;on m’a toujours incité à participer à des conférences&lt;/a&gt; comme Paris Web, DotJS, ou le Forum PHP. Pour ceux qui préfèrent un cadre plus intimiste, il existe de nombreux meetups locaux à Paris ou ailleurs. Ne sous-estimez pas l’impact d’une simple participation, car il y a des événements (et des rencontres) qui peuvent être une véritable source de motivation et ouvrir de nouvelles perspectives.&lt;/p&gt;
&lt;h2&gt;Internet : une effervescence quotidienne 🌊&lt;/h2&gt;
&lt;p&gt;Pour illustrer à quel point la veille est indispensable, prenons l’exemple de GitHub, un acteur clé dans l’univers des développeurs. &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.blogdumoderateur.com/etude-github-langages-plus-utilises-tendances-open-source/&quot;&gt;Chaque année, 413 millions de contributions open source sont effectuées&lt;/a&gt;. Du côté de YouTube, c&#039;est une avalanche de contenu avec &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.wizishop.fr/blog/chiffres-youtube&quot;&gt;720 000 heures de vidéos publiées chaque jour&lt;/a&gt;. Sur X, les stats aussi explosent tout !&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/x-data.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/x-data.jpeg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(2048 / 1152)&quot; src=&quot;https://jolicode.com//media/original/2024/x-data.jpeg&quot; alt=&quot;Statistiques de X&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Ces chiffres illustrent en partie la rapidité avec laquelle le monde de la tech évolue. Avec un volume pareil, il est impossible de tout découvrir, tout lire, ou tout expérimenter. D’où l’importance de bien choisir ses sources et d’être stratégique dans sa veille.&lt;/p&gt;
&lt;p&gt;Personnellement, j’utilise &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://daily.dev/fr-fr&quot;&gt;Daily.dev&lt;/a&gt; pour me créer un feed sur mesure. C’est un réseau qui permet de collaborer et d’apprendre dans un environnement privé, loin du bruit d’autres plateformes. Les “squads” sont particulièrement intéressantes : elles permettent de suivre des groupes d’intérêt, souvent sur invitation, garantissant un haut niveau de contenu. Ça m’a permis aussi de découvrir les sources partagées, et in finé de supers nouveaux sites à suivre.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/article-veille/dailydev-article.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/article-veille/dailydev-article.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(1878 / 1092)&quot; src=&quot;https://jolicode.com//media/original/2024/article-veille/dailydev-article.png&quot; alt=&quot;DailyDev&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/trending/developers&quot;&gt;GitHub Trending&lt;/a&gt; est aussi une excellente ressource pour découvrir les projets les plus “hype” du moment. Cela permet de voir comment sont organisés, documentés et codés les projets qui attirent le plus d’attention.&lt;/p&gt;
&lt;h2&gt;Podcast, Substack et lectures 🧠&lt;/h2&gt;
&lt;p&gt;La veille ne se limite pas à des sites web. J’ai également parlé de podcasts qui m’ont réconcilié avec le format : &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://podcast.ausha.co/cto-z-1&quot;&gt;CTO’Z&lt;/a&gt;, &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.ifttd.io/&quot;&gt;IFTTD&lt;/a&gt;, &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://podcast.remotefr.com/&quot;&gt;Génération Remote&lt;/a&gt;, et &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://code-garage.fr/podcast?tab=classic&quot;&gt;Code Garage&lt;/a&gt;. Ils sont devenus des incontournables pour moi et on s’y attache rapidement. Côté lecture, il existe aussi des newsletters Substack de qualité, comme &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://happytodev.substack.com/&quot;&gt;HappyToDev,&lt;/a&gt; qui traite de PHP, Laravel et Vue.js, ou encore &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://hybridhacker.email/&quot;&gt;Hybrid Hacker&lt;/a&gt;, avec un focus plus humain sur le développement.&lt;/p&gt;
&lt;p&gt;En ce qui concerne les livres, j’ai mentionné &amp;quot;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://bastien.jaillot.fr/dette-technique-le-livre/&quot;&gt;La Dette Technique&lt;/a&gt;&amp;quot; écrit par &lt;a href=&quot;https://jolicode.com//qui-sommes-nous/equipe/bastien-jaillot&quot;&gt;Bastien Jaillot&lt;/a&gt;, qui met enfin des mots sur ce concept souvent flou et &amp;quot;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.engmanagement.dev/&quot;&gt;Engineering management for the rest of us&lt;/a&gt;” de &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://x.com/sarah_edo?lang=fr&quot;&gt;Sarah Drasner&lt;/a&gt; et qu’il faut que je rende à &lt;a href=&quot;https://jolicode.com//qui-sommes-nous/equipe/damien-alexandre&quot;&gt;Damien&lt;/a&gt; ! Je suis sûr que vous en avez mille fois plus que moi en tête !&lt;/p&gt;
&lt;h2&gt;Une initiative collective : &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://digest.club/&quot;&gt;digest.club&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;À cet égard, l’initiative digest.club, développée par &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.premieroctet.com/&quot;&gt;Premier Octet&lt;/a&gt;, méritait d’être soulignée. Ce projet open-source permet de partager notre veille, et vise aussi à lutter contre la diminution de la motivation, quand la veille devient un effort conscient. digest.club invite donc chacun à contribuer, à partager et à s’engager collectivement dans ce parcours d’apprentissage. Si l’outil vous intéresse, on parle de notre usage de cet outil &lt;a href=&quot;https://jolicode.com//blog/lorganisation-de-la-veille-technique-avec-digest-club&quot;&gt;sur notre blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/article-veille/digestclub-article.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/article-veille/digestclub-article.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(2270 / 1718)&quot; src=&quot;https://jolicode.com//media/original/2024/article-veille/digestclub-article.png&quot; alt=&quot;Digest&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;Contribuer pour mieux apprendre ✍️&lt;/h2&gt;
&lt;p&gt;Faire de la veille, c’est aussi contribuer. Avec &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://hacktoberfest.com/&quot;&gt;Hacktoberfest&lt;/a&gt;, c’est l’occasion idéale pour plonger dans l’open source. Grâce au label “&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://goodfirstissue.dev/&quot;&gt;Good First Issue&lt;/a&gt;”, il est aussi possible de trouver des tickets adaptés aux débutants. Si cette démarche n’a jamais été franchie, c’est le moment ou jamais !&lt;/p&gt;
&lt;p&gt;La clé pour réussir en tant que développeur en 2024 semble résider dans l’apprentissage continu, la curiosité et l’empathie. Que l’on soit novice ou expérimenté, se tenir informé, participer à des événements et contribuer à des projets open-source enrichit non seulement les compétences techniques, mais renforce aussi le lien humain au sein de notre communauté.&lt;/p&gt;
&lt;p&gt;Après tout, c’est cette connexion qui transforme le développement en une aventure collective, où chaque contribution compte et chaque voix a son importance.&lt;/p&gt;
&lt;p&gt;Bonne veille !&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/ce-que-nous-avons-retenu-du-forum-php-2024-jour-2</id>
        <published>2024-10-17T15:00:00+02:00</published>
        <updated>2024-10-17T15:00:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/ce-que-nous-avons-retenu-du-forum-php-2024-jour-2"/>
        <title>Ce que nous avons retenu du Forum PHP 2024, jour 2</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="php" />            <category term="conférence" />        <summary><![CDATA[Comme chaque année l’équipe de JoliCode était présente au Forum PHP de l’AFUP, le plus gros événement PHP de l’Hexagone, pour deux jours de conférence sur l’éco-système !
Cet article est la suite du premier…]]></summary>
        <content type="html">
            &lt;p&gt;Comme chaque année l’équipe de JoliCode était présente au Forum PHP de l’AFUP, le plus gros événement PHP de l’Hexagone, pour deux jours de conférence sur l’éco-système !&lt;/p&gt;
&lt;p&gt;Cet article est la &lt;a href=&quot;https://jolicode.com//blog/ce-que-nous-avons-retenu-du-forum-php-2024-jour-1&quot;&gt;suite du premier jour&lt;/a&gt;, nous allons vous partager nos take-aways, ce que nous y avons découvert, et ce que nous en retenons.&lt;/p&gt;
&lt;p&gt;Il y a encore ici beaucoup de belles choses à raconter, à vos lunettes !&lt;/p&gt;
&lt;h2&gt;L’aventure d’une requête HTTP — ou le chemin de la vie des devs&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://blog.pascal-martin.fr/&quot;&gt;Pascal Martin&lt;/a&gt; est principal engineer chez Bedrock et conférencier depuis… 2004 ! Sa conférence, intitulée &amp;quot;L’aventure d’une requête HTTP&amp;quot;, a exploré un sujet que tout le monde utilise quotidiennement, mais qui reste souvent mystérieux : que se passe-t-il lorsqu’on saisit une URL dans la barre d’adresse de son navigateur ?&lt;/p&gt;
&lt;p&gt;Cette question, que Pascal aime poser en entretien (nous aussi 🤗), n’est pas un piège, mais un tremplin pour aborder la complexité d’une requête HTTP et les nombreuses technologies qu’elle met en jeu.&lt;/p&gt;
&lt;p&gt;Durant la conférence, il a mentionné des outils comme Wireshark ou Sysdig, essentiels pour observer et comprendre le chemin d’une requête. Un des points forts de son intervention était de souligner l’importance de l’observabilité pour les développeurs. Comprendre ce qui se passe en coulisse est crucial pour optimiser des projets et améliorer ses compétences.&lt;/p&gt;
&lt;p&gt;Comme il l’a si bien rappelé : &amp;quot;notre métier est un assemblage de multiples compétences&amp;quot;. Les conférences comme celles-ci permettent de choisir les technologies à expérimenter et ouvrent des perspectives de carrière insoupçonnées !&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/forum-php-2024/http.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/forum-php-2024/http.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(2048 / 2048)&quot; src=&quot;https://jolicode.com//media/original/2024/forum-php-2024/http.jpg&quot; alt=&quot;Vie d&#039;HTTP&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Sketch de la conférence &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://x.com/mdesnouveaux/status/1844674713630441530&quot;&gt;par Mathieu Desnouveaux&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Créer des interfaces d’administration rapidement avec Symfony UX et Sylius&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://x.com/loic_425&quot;&gt;Loïc Frémont&lt;/a&gt; fait partie de la core team de Sylius et nous a parlé d&#039;un sujet qui résonne fort avec notre article récent : &lt;a href=&quot;https://jolicode.com//blog/administrer-une-entite-custom-dans-un-back-office-sylius&quot;&gt;Administrer une entité custom dans un back-office Sylius&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Il a débuté sa présentation par la Grid Standalone de Sylius, un composant maintenant totalement découplé du système de ressources. Il permet de construire des interfaces d’administration performantes sans avoir à gérer toute la logique liée aux ressources, ce qui offre plus de flexibilité pour intégrer cette solution dans &lt;strong&gt;des projets non-e-commerce&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;En revenant sur l&#039;évolution historique, il a rappelé que l&#039;intégration de Semantic UI en 2016, suivie de AdminLTE et de Bootstrap, était devenue vieillissante. Ces solutions n&#039;étaient plus adaptées aux besoins actuels, notamment en termes d&#039;extensibilité et d&#039;expérience développeur (DX). C’est pourquoi en 2019, Sylius a introduit un mécanisme de templates d’événements, conçu pour remplacer les traditionnels blocs Sonata et apporter plus de souplesse. Aujourd&#039;hui, ces &amp;quot;event templates&amp;quot; ont été remplacés par des hooks Twig, facilitant encore davantage la personnalisation des interfaces administratives.&lt;/p&gt;
&lt;p&gt;Il a aussi parlé de &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/Monofony/Monofony&quot;&gt;Monofony&lt;/a&gt;, un projet basé sur Symfony et utilisant les composants de Sylius. Ce projet permet d&#039;utiliser les fonctionnalités clés de Sylius, comme la Grid et le système de ressources, sans la partie e-commerce. Il est cependant difficile de l’installer sur un projet existant car il dépend de certains packages du monorepos de Sylius.&lt;/p&gt;
&lt;p&gt;Il nous a ensuite montré les différentes étapes pour construire rapidement une admin fonctionnelle en prenant l’exemple d’une gestion de conférenciers :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Créer l’entité &lt;code&gt;Speaker&lt;/code&gt; avec la commande &lt;code&gt;make:entity&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;Créer le formType avec la commande &lt;code&gt;make:form&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;Créer la grid toujours avec le MakerBundle &lt;code&gt;make:grid&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;Déclarer l’entité en tant que Resource Sylius grace à l’attribute &lt;code&gt;#[AsResource]&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;Modifier la grid afin de personnaliser les filtres, les champs qu’on affiche ainsi que les actions possibles (notamment une action qui permet aussi de filtrer la liste affichée).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Le rôle de Symfony UX a très peu été abordé par Loïc, il y a cependant le composant &lt;code&gt;ux_autocomplete&lt;/code&gt;. Ce dernier permet d’ajouter des champs d’autocomplétion pour les filtres, rendant l’interface d’administration plus fluide et agréable à utiliser pour les utilisateurs finaux.&lt;/p&gt;
&lt;p&gt;Pour conclure, il a indiqué que la documentation n’était pas encore écrite mais que le code était déjà prêt à être utilisé.&lt;/p&gt;
&lt;h2&gt;Utiliser une faille glibc pour attaquer le moteur PHP&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://x.com/cfreal_&quot;&gt;Charles FOL&lt;/a&gt; est chercheur en sécurité, et il nous a raconté comment il y a quelques mois, il a découvert un bug vieux de 24 ans dans glibc, la librairie de base des programmes linux.&lt;/p&gt;
&lt;p&gt;Le bug est très compliqué à atteindre, sauf depuis PHP, qui permet son exploitation depuis le web.&lt;/p&gt;
&lt;p&gt;Via ce bug, et beaucoup de ténacité, Charles peut convertir n&#039;importe quelle lecture de fichier en RCE : Remote Code Execution (donc prendre le contrôle de votre serveur).&lt;/p&gt;
&lt;p&gt;Exemple de code qui pourrait être exploité pour prendre le contrôle :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; file_get_contents&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($_GET[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;file&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oui ce code n&#039;est pas très malin, mais la faille est exploitable de bien d&#039;autres manières car elle exploite en réalité &lt;strong&gt;iconv&lt;/strong&gt;. Tout ce qui touche à iconv est donc impacté. Saviez-vous que &lt;code&gt;symfony/polyfill-mbstring&lt;/code&gt; est basé sur iconv ? 😬&lt;/p&gt;
&lt;p&gt;Essayons de résumer le cheminement :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PHP a un système de filtre à la lecture de fichier : &lt;code&gt;php://filter/&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;Il y a un filtre de conversion, utilisant &lt;strong&gt;iconv&lt;/strong&gt; : &lt;code&gt;php://filter/convert.iconv.UTF-8.UTF-16/resource=/etc/passwd&lt;/code&gt; ; ici on traduit le fichier &lt;code&gt;/etc/passwd&lt;/code&gt; de UTF-8 vers UTF-16 ;&lt;/li&gt;
&lt;li&gt;À la base le chercheur était en train de jouer avec ces filtres pour modifier le contenu du fichier, en effet, à force d&#039;enchaîner des centaines de conversions, le contenu textuel du fichier peut être forcé ! Par exemple, rendre un fichier &lt;code&gt;/etc/passwd&lt;/code&gt; valide en JSON pour le voler 🙈&lt;/li&gt;
&lt;li&gt;Une librairie a été créée pour &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/ambionics/wrapwrap&quot;&gt;générer ces filtres&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ça aurait pu s&#039;arrêter là, c&#039;est déjà chouette et très impressionnant ! 😋&lt;/p&gt;
&lt;p&gt;Mais à force de jouer avec les conversions de charset, l&#039;auteur est tombé sur un cas exceptionnel qui fait crasher le système. Quand on convertit en &lt;code&gt;ISO-2022-CN-EXT&lt;/code&gt;, iconv a de fortes chances de rater son allocation mémoire et de faire un Out-of-bound write (écrire dans un espace qui n&#039;est pas le sien).&lt;/p&gt;
&lt;p&gt;Vous pouvez voir &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/ambionics/cnext-exploits/blob/main/pocs/poc.php&quot;&gt;un POC PHP complet ici&lt;/a&gt;, segfault garantie.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$input &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&quot;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;AAA劄劄&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;劄劄&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;劄劄&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;劄&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;劄&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;劄&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;劄&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$output &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; iconv&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;ISO-2022-CN-EXT&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, $input);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il est donc possible de manipuler la mémoire et donc on obtient le graal : une RCE, via le remplacement d&#039;un pointeur de fonction !&lt;/p&gt;
&lt;p&gt;Cette partie de l&#039;exploit n&#039;a pas été détaillée dans le talk, mais &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/ambionics/cnext-exploits&quot;&gt;un script existe pour l&#039;exploiter&lt;/a&gt;. Ce qu&#039;il faut retenir, c&#039;est qu&#039;aujourd&#039;hui, une faille de lecture de fichier se transforme très rapidement en RCE et c&#039;est le carnage !&lt;/p&gt;
&lt;p&gt;Si vous voulez voir comment la HEAP PHP est manipulée via ce bug, c&#039;est sur &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.ambionics.io/blog/iconv-cve-2024-2961-p3&quot;&gt;le blog de Ambionics&lt;/a&gt;, qui détaille en trois parties toute l&#039;histoire de la CVE-2024-2961.&lt;/p&gt;
&lt;h2&gt;Les objets paresseux en PHP&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://x.com/nicolasgrekas&quot;&gt;Nicolas Grekas&lt;/a&gt; aime beaucoup les objets paresseux 😄 et c&#039;est en effet très pratique, que ça soit pour nos services &amp;quot;lazy&amp;quot; ou pour nos entités Doctrine non hydratées tant qu&#039;on y accède pas. On gagne du temps, de la mémoire et donc de la performance.&lt;/p&gt;
&lt;p&gt;Nous avons déjà parlé &lt;a href=&quot;https://jolicode.com//blog/php-object-lazy-loading-is-more-than-what-you-think&quot;&gt;des différents types d&#039;objets paresseux en PHP dans l&#039;article de Baptiste, avec un cas d&#039;usage sur les appels HTTP&lt;/a&gt;. Nous vous en recommandons la lecture.&lt;/p&gt;
&lt;p&gt;La grosse nouveauté ici c&#039;est le &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://wiki.php.net/rfc/lazy-objects&quot;&gt;support natif dans PHP&lt;/a&gt; de cette fonctionnalité, prévue pour PHP 8.4 !&lt;/p&gt;
&lt;p&gt;Ça va se passer dans &lt;code&gt;ReflectionClass&lt;/code&gt; et &lt;code&gt;ReflectionProperty&lt;/code&gt;, nous allons pouvoir déclarer des classes lazy et contrôler comment l&#039;initialisation se fait ou ne se fait pas :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;MyClass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $foo)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // Heavy initialization logic here.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$initializer &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; static&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;MyClass&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $ghost)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $ghost&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$reflector &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ReflectionClass&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;MyClass&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$object &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $reflector&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;newLazyGhost&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($initializer);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// At this point, $object is a lazy ghost object.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Avec &lt;code&gt;newLazyGhost&lt;/code&gt;, nous avons dit à PHP que &lt;code&gt;MyClass&lt;/code&gt; ne doit plus être bootée comme d&#039;habitude, son constructeur ne doit pas être exécuté à chaque création d&#039;objet.&lt;/p&gt;
&lt;p&gt;Nous pouvons aussi manipuler les données et déclarer certaines propriétés comme connues / accessible sans déclencher l&#039;initialisation :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// ID is already known and can be accessed without triggering initialization&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;reflector&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;id&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setRawValueWithoutLazyInitialization&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($entity, $id);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Avec cet appel, nous avons mis une valeur dans &amp;quot;id&amp;quot;, et précisé que lire cette propriété ne doit pas déclencher le constructeur ; comme avec Doctrine et une entité proxy par exemple.&lt;/p&gt;
&lt;p&gt;L&#039;arrivée de cette feature dans PHP va permettre de débloquer quelques cas d&#039;usages, rendre lazy des services avec le mot clé &amp;quot;final&amp;quot; par exemple. Vivement PHP 8.4 !&lt;/p&gt;
&lt;h2&gt;Optimiser les performances de votre API avec AutoMapper&lt;/h2&gt;
&lt;p&gt;Salle comble pour la seconde intervention de &lt;a href=&quot;https://jolicode.com//qui-sommes-nous/equipe/joel-wurtz&quot;&gt;Joël&lt;/a&gt;, qui nous a présenté l’&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/jolicode/automapper&quot;&gt;AutoMapper&lt;/a&gt;, une bibliothèque open source made by JoliCode 💛&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/forum-php-2024/automapper-joel.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/forum-php-2024/automapper-joel.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(4032 / 2268)&quot; src=&quot;https://jolicode.com//media/original/2024/forum-php-2024/automapper-joel.jpg&quot; alt=&quot;Joël&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Si vous n’êtes pas encore familier avec cette bibliothèque, sachez qu’elle vise à éliminer le code boilerplate lié au mapping d’un schéma (&lt;em&gt;object&lt;/em&gt; ou &lt;em&gt;array&lt;/em&gt;) à un autre. Son utilisation est comparable à celle du Serializer de Symfony, mais elle génère du code au lieu de s’appuyer sur des métadonnées, &lt;strong&gt;améliorant sensiblement les performances&lt;/strong&gt;. Elle est particulièrement adaptée dans un projet Symfony, conjointement à API Platform et nécessite un typage fort.&lt;/p&gt;
&lt;p&gt;Joël met l’accent sur une migration endpoint par endpoint, il n’est pas question de tout migrer d’un coup. Voyons les étapes à suivre pour utiliser l’AutoMapper, de sa configuration à son utilisation avec les attributs et les transformers.&lt;/p&gt;
&lt;h3&gt;Configuration&lt;/h3&gt;
&lt;p&gt;Après avoir installé et enregistré le bundle dans le fichier &lt;code&gt;config/bundles.php&lt;/code&gt;, il suffit d’ajouter quelques lignes de configuration et l’attribut &lt;code&gt;#[Mapper(...)]&lt;/code&gt; aux entités voulues.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;automapper&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    # ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    mapping&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        paths&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;          - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;%kernel.project_dir%/src/Entity&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[AutoMapper\Attribute\Mapper(target: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;array&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    //...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Utilisation&lt;/h3&gt;
&lt;p&gt;Par défaut, vous n’avez rien à faire. L’AutoMapper va essayer de mapper vos propriétés &lt;em&gt;automagiquement&lt;/em&gt; 🧙. Cependant si vous avez des normalizers custom, il est nécessaire de les migrer vers des transformers puis de les configurer avec l’attribut adéquat.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[MapTo(name: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;requestsEvolution&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, transformer: &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;RequestEvolutionTransformer&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    //...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;À noter, l’argument &lt;code&gt;transformer&lt;/code&gt; supporte l’&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://symfony.com/doc/current/components/expression_language.html&quot;&gt;Expression Language&lt;/a&gt; de Symfony.&lt;/p&gt;
&lt;p&gt;Il est également possible d’utiliser l’attribut inverse pour définir un mapping depuis un DTO.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;ProjectDto&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[MapFrom(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        source: &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        transformer: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;service(&quot;permissions_builder&quot;).buildProjectPermissions(source)&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    )]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $permissions;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    // ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;D’ailleurs, Joël conseille d’user (et d’abuser) des DTO, &lt;strong&gt;1 endpoint = 1 DTO&lt;/strong&gt;, et pour deux grandes raisons :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Typage fort et propriétés auto documentées ;&lt;/li&gt;
&lt;li&gt;Code plus lisible : plus besoin de groupes de sérialisation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;c-card c-card--primary c-card__inner c-card__inner--lg&quot;&gt;L’AutoMapper a maintenant sa section dans le Profiler de Symfony, bien pratique pour débugger un souci de mapping !&lt;/p&gt;
&lt;p&gt;Pour aller plus loin, vous pouvez consulter &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://jolicode.github.io/les-confs-de-joel/optimiser-les-performances-de-votre-api-avec-automapper/dist/index.html&quot;&gt;les slides de la conférence&lt;/a&gt;, &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://automapper.jolicode.com/latest/&quot;&gt;la documentation&lt;/a&gt; ou &lt;a href=&quot;https://jolicode.com//blog/automapper-9-is-out&quot;&gt;le dernier article de notre blog à ce sujet&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;L&#039;Orienté Objet au Cœur du Templating Symfony : Découvrez TwigComponent et LiveComponent&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.linkedin.com/in/felix-eymonot/&quot;&gt;Felix Eymonot&lt;/a&gt; est venu nous présenter la dernière évolution en matière de templating sur Symfony, les Twig et Live Components.&lt;/p&gt;
&lt;p&gt;L’idée est simple, découper les templates en composants qui chacun est lié à une classe PHP qui s’occupe de gérer leurs données.&lt;/p&gt;
&lt;p&gt;Ces composants s’intègrent en respectant 4 principes clés :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Réutilisabilité ;&lt;/li&gt;
&lt;li&gt;Encapsulation ;&lt;/li&gt;
&lt;li&gt;Modularité ;&lt;/li&gt;
&lt;li&gt;Interaction.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L’objectif final de Symfony UX avec ces composants est donc des template réutilisables et orientés objet, auxquels les développeurs backs n’auraient pas peur de toucher.&lt;/p&gt;
&lt;p&gt;Pour créer des Components, on commence par définir un dossier dans lequel ils iront. On peut ensuite le remplir à la main, où à l’aide de la commande fournie par le &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://symfony.com/bundles/SymfonyMakerBundle/current/index.html&quot;&gt;MakerBundle&lt;/a&gt; qui génère la classe PHP + le template Twig.
Une classe de Component peut avoir des propriétés privées, auxquelles elle seule peut accéder, et des propriétés publiques, qui seront accessibles directement depuis le template.&lt;/p&gt;
&lt;p&gt;Il reste néanmoins possible de passer des données dans les templates sans faire appel à sa classe PHP grâce à la variable “attribute” qui apparaît dans chaque template de component. Elle peut être remplie en définissant les données avec une nomenclature qui vous rappellera peut-être certains frameworks JS :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;Twig:Component&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; :&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;prop&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il est évidemment possible de fournir des variables par défauts aux composants, et étant donné qu’ils sont déclarés dans votre namespace &lt;code&gt;App&lt;/code&gt;, ils sont disponibles en AutoWiring !
On peut même faire des tests dessus grâce au trait &lt;code&gt;InteractWithTwigComponents&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Enfin, Felix nous a parlé des &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://symfony.com/bundles/ux-live-component/current/index.html&quot;&gt;LiveComponents&lt;/a&gt;, qui sont finalement une surcouche des TwigComponent, inspirée de &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://laravel-livewire.com/&quot;&gt;Livewire&lt;/a&gt;, et qui permet de modifier la donnée des components en direct sur la page.&lt;/p&gt;
&lt;h2&gt;Écrire du PHP avec seulement 6 caractères&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://x.com/b_viguier&quot;&gt;Benoit Viguier&lt;/a&gt; est revenu sur scène pour une présentation humoristique, presque digne d’un &lt;em&gt;one-man show&lt;/em&gt;. Vous connaissez peut-être les défis consistant à écrire un jeu avec le moins de code possible (JS1K). Il existe un challenge similaire où l&#039;objectif est d&#039;écrire du code en utilisant un nombre minimal de caractères.&lt;/p&gt;
&lt;p&gt;Sous contrainte, on devient plus créatif ! C’est le mantra de Benoît, que nous partageons également. Il s&#039;est donc lancé dans ce défi, avec pour objectif de résoudre le challenge en PHP. Non seulement il y est parvenu, mais il a réussi à le faire en seulement 5 caractères ! 👏&lt;/p&gt;
&lt;p&gt;Grâce à une accumulation de hacks, tous plus ingénieux les uns que les autres, il est effectivement possible d&#039;écrire du PHP en utilisant seulement 5 caractères. Nous aurions aimé vous montrer un simple &lt;code&gt;echo &#039;bonjour&#039;;&lt;/code&gt;, mais le code généré pèse 240 Ko 😅. Pour satisfaire votre curiosité, voici un aperçu du début du fichier :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;((((9999999999).(9))^(((((9).(((9^9).(9))^(((9)^(99)).(9))^((9).(9))))^((9).(9^9))^((9^9).(9^9)))^(9^9)).((((99^((9^((9^9)^((9).((9^9))))).(9^9))).(9))^...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si cela vous intrigue, vous pouvez consulter &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://b-viguier.github.io/downloads/talks/ForumPhp2024_6Characters.pdf&quot;&gt;les slides&lt;/a&gt; pour découvrir comment c&#039;est possible !&lt;/p&gt;
&lt;h2&gt;Webhooks at scale&lt;/h2&gt;
&lt;p&gt;Lors de son talk, &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://x.com/FabienPaitry&quot;&gt;Fabien Paitry&lt;/a&gt; a présenté les défis rencontrés par &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://yousign.com/fr-fr&quot;&gt;Yousign&lt;/a&gt; pour gérer les webhooks face à une forte augmentation de trafic sur leur API.&lt;/p&gt;
&lt;p&gt;Chaque jour, l’API Yousign envoie environ 120 000 webhooks avec des temps de réponse variables selon les clients, allant de quelques millisecondes à plusieurs secondes. Le délai maximal de réponse est fixé à 10 secondes pour s&#039;assurer de la compatibilité avec tous les clients. Les webhooks sont gérés de façon asynchrone via RabbitMQ et traités par des workers Symfony. Toutefois, certains clients envoient des demandes de signature par batch (par exemple 1 000), et si un endpoint ne répond pas, avec un &lt;em&gt;retry&lt;/em&gt; de 10 secondes, le temps de traitement cumulé peut atteindre les 10 000 secondes, de quoi saturer la queue et ne plus pouvoir dépiler les messages dans un temps raisonnable.&lt;/p&gt;
&lt;p&gt;La solution quick and dirty ? Augmenter le nombre de workers. Mais Fabien rappelle que ce n’est pas scalable à l’infini et décrit plutôt les actions mises en place avec son équipe.&lt;/p&gt;
&lt;h3&gt;Fail fast policy&lt;/h3&gt;
&lt;p&gt;Première action, réduire le timeout à &lt;strong&gt;1 seconde&lt;/strong&gt; (temps de réponse moyen de 97% de leurs endpoints) lors du premier appel au webhook. Easy win ! Cela permet d’avoir un plus gros débit sur la queue principale et une meilleure granularité avec une queue dédiée au retry.&lt;/p&gt;
&lt;h3&gt;Circuit breaker&lt;/h3&gt;
&lt;p&gt;Deuxième solution adoptée, implémenter le pattern &lt;strong&gt;circuit breaker&lt;/strong&gt;. L’idée est qu’il est inutile de retenter d’appeler directement un serveur distant qui ne répond pas ; il est préférable de réessayer plus tard. Le pattern a 3 états :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ouvert : le serveur distant est indisponible, aucun appel n&#039;est effectué pendant une période définie ;&lt;/li&gt;
&lt;li&gt;Semi ouvert : une seule requête HTTP est envoyée pour vérifier la disponibilité du serveur ;&lt;/li&gt;
&lt;li&gt;Fermé : si la requête réussit, le circuit se referme et l’ensemble du trafic est à nouveau autorisé.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et à l’inverse, si le circuit est fermé mais que le nombre d&#039;appels échoués atteint un seuil critique, il passe à l&#039;état ouvert, stoppant ainsi temporairement les appels sur ce nom de domaine.&lt;/p&gt;
&lt;p&gt;En PHP, la bibliothèque &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/ackintosh/ganesha&quot;&gt;Ganesha&lt;/a&gt; implémente ce pattern et dispose d’une intégration avec le HttpClient de Symfony.&lt;/p&gt;
&lt;h2&gt;Covariance, Contravariance et Diamant&lt;/h2&gt;
&lt;p&gt;À travers de nombreux exemples de code usant (entre autres) d’abstraction et d’héritages, Alexandre Daubois nous montre comment élargir ou réduire les types utilisés en arguments, en valeur de retour, etc.&lt;/p&gt;
&lt;p&gt;Ces exemples reposent notamment sur deux des 5 principes SOLID : le principe d’inversion de dépendance et le principe de substitution de Liskov.&lt;/p&gt;
&lt;p&gt;Ce sujet assez technique peut difficilement être résumé ici mais nous vous invitons à vous pencher sur &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://speakerdeck.com/alexdaubois/covariance-contravariance-and-diamond&quot;&gt;les slides&lt;/a&gt; qui sont une mine de bons exemples.&lt;/p&gt;
&lt;p&gt;Alexandre en a profité pour aborder un autre sujet : l&#039;héritage multiple, et le problème du diamant (aussi appelé problème du diamant de la mort, parce que les informaticien.ne.s ne sont pas dramatiques du tout).&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://jolicode.com/media/cache/content/media/original/2024/forum-php-2024/covariance.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://jolicode.com/media/cache/content_standard/media/original/2024/forum-php-2024/covariance.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;c-dialog__target c-dialog__image js-dialog-target&quot; style=&quot;aspect-ratio: calc(1252 / 693)&quot; src=&quot;https://jolicode.com//media/original/2024/forum-php-2024/covariance.png&quot; alt=&quot;&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Encore une fois, en utilisant intelligemment les traits et l’héritage, Alexandre nous montre comment résoudre ce genre de problème - nous vous invitons à voir ses slides !&lt;/p&gt;
&lt;h2&gt;Découverte de Castor : Le Task Runner PHP qui Simplifie votre Workflow&lt;/h2&gt;
&lt;p&gt;Grégoire Pineau, core contributeur à Symfony, est venu nous présenter &lt;strong&gt;Castor&lt;/strong&gt; 🦫. Si vous suivez régulièrement notre blog, vous connaissez déjà cet outil open source de task running. Castor est conçu pour être simple et facile à prendre en main, avec une expérience développeur optimisée. Il est fourni avec une collection d&#039;outils et de nombreux points d&#039;extension, ce qui permet de l&#039;adapter parfaitement à vos besoins.&lt;/p&gt;
&lt;p&gt;Pour en savoir plus, &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://speakerdeck.com/lyrixx/castor-le-task-runner-php-qui-simplifie-votre-workflow&quot;&gt;vous pouvez consulter les slides&lt;/a&gt; ou lire &lt;a href=&quot;https://jolicode.com//blog/castor-a-journey-across-the-sea-of-task-runners&quot;&gt;notre article&lt;/a&gt; dédié à ce sujet.&lt;/p&gt;
&lt;h2&gt;Dis Siri, mets des éléPHPants dans ma domotique&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://x.com/lanfisis&quot;&gt;David Buros&lt;/a&gt;, développeur chez nos copains de MonsieurBiz, nous a parlé d&#039;un sujet qui n&#039;a pas souvent été abordé aux conférences PHP, mais dont certains d&#039;entre nous sont particulièrement friands à JoliCode : &lt;strong&gt;la domotique&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Il a ainsi présenté le fonctionnement de sa domotique qui se base sur &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.home-assistant.io/&quot;&gt;Home-Assistant&lt;/a&gt;. Ce logiciel, open source et gratuit, a l&#039;avantage de proposer des intégrations avec la plupart des services existants (assistants Google/Siri/etc, produits connectés grand public comme Philips Hue, Ikea, et bien d&#039;autres encore). David nous rappelle les deux fonctions principales de Home-Assistant : afficher les entités dans des dashboard (aka toutes les valeurs / états de tous les appareils reliés à notre domotique) et nous permettre de créer des automatisations (pour réagir aux changements d&#039;états des entités, mais pas que).&lt;/p&gt;
&lt;p&gt;David nous a parlé de quelques exemples qu&#039;il a mis en place chez lui, avec par exemple la remontée de sa consommation électrique en temps réel, l&#039;utilisation de micro-module pour contrôler des prises ou des interrupteurs, etc.&lt;/p&gt;
&lt;p&gt;L&#039;occasion de parler des protocoles Zigbee et MQTT qui sont souvent employés en domotique. Ainsi, David a pu faire son propre module PHP qui interroge une API tierce, puis envoie les données en MQTT à sa domotique. Une bonne idée pour bidouiller sa domotique sans avoir à faire de python (le langage dans lequel est écrit Home-Assistant).&lt;/p&gt;
&lt;h2&gt;Le Zéro Downtime Deployment en pratique&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://x.com/SmaineDev&quot;&gt;Smaïne Milianni&lt;/a&gt;, engineering manager chez Yousign, nous a montré les techniques qu&#039;ils utilisent pour s&#039;assurer d&#039;avoir des déploiements sans downtime.&lt;/p&gt;
&lt;p&gt;Ils mettent bien sûr en place la méthodologie du &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Blue%E2%80%93green_deployment&quot;&gt;Blue-Green Deployment&lt;/a&gt; mais dans ce talk il nous donne leurs pro-tips pour le faire bien 💪&lt;/p&gt;
&lt;p&gt;L&#039;idée du Zéro Downtime Deployment (ou ZDD), c&#039;est de pouvoir faire des releases transparentes pour les utilisateurs (sans impact, et pas de page &amp;quot;maintenance en cours&amp;quot;), n&#039;importe quand (même un vendredi), mais aussi d&#039;avoir des rollback faciles et rapides quand quelque chose se passe mal. À l&#039;extrême, Smaïne nous montre l&#039;exemple d&#039;une société qui a un rollback automatique qui se fait en 4 minutes si la nouvelle version génère des anomalies ou des erreurs sur Sentry.&lt;/p&gt;
&lt;p&gt;Mais pour parvenir à faire du ZDD, le blue-green deployment ne suffit pas. Nous avons également besoin de respecter plusieurs règles. Par exemple, tous les changements doivent être backward compatibles, c&#039;est-à-dire compatible avec la version N actuellement en prod. Dans le cas où l&#039;on veut ajouter une colonne &amp;quot;not nullable&amp;quot; à une table existante, on va devoir procéder en 2 temps :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On ajoute la colonne en nullable dans une release 1, et le code commence à remplir ce nouveau champ&lt;/li&gt;
&lt;li&gt;Puis dans une release 2, on update les lignes existantes dont la valeur est encore &lt;code&gt;null&lt;/code&gt; et on enlève la contrainte nullable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La même logique est extrapolable pour le delete, l&#039;update ou le rename d&#039;une colonne. Smaïne a même développé un bundle &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/ismail1432/safe-migrations-bundle&quot;&gt;eniams/safe-migrations&lt;/a&gt; qui permet de vérifier que nos migrations sont bien &amp;quot;ZDD-compliant&amp;quot;.&lt;/p&gt;
&lt;p&gt;Lorsque l&#039;on utilise rabbitMQ, il faut également penser à couper le worker pendant le déploiement. Ainsi les messages vont s&#039;accumuler en version N dans les queues. Donc si nous voulons ajouter une nouvelle propriété à nos messages, il faut le faire avec une valeur par défaut. Cela permettra aux messages en version N d&#039;être désérialisés sans soucis par Messenger avec le code de la version N+1.&lt;/p&gt;
&lt;p&gt;Les mêmes problématiques existent lorsque l&#039;on a une application frontend dissociée du code du backend. Si on souhaite ajouter une nouvelle propriété dans une ressource de l&#039;API, il faudra le faire en deux temps, comme pour une modification de la BDD.&lt;/p&gt;
&lt;p&gt;Pour finir, Smaïne nous rappelle que le ZDD n&#039;est pertinent que si l&#039;on déploie fréquemment (et ça devrait probablement être le cas 😇) et qu&#039;il nécessite de la réflection et une bonne discipline pour l&#039;équipe de développeurs. Et le ZDD n&#039;évitera pas non plus les down-times lors de changements critiques de l&#039;application. Il ne faut donc pas hésiter à bien éduquer l&#039;équipe, faire de la documentation sur les différents processus ainsi que faire des post-mortems pour apprendre de nos erreurs après des soucis. Retrouvez ses slides &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://ismail1432.github.io/conferences/2024/forum_php/index.html&quot;&gt;sur son site&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;WordPress : une cause perdue ?&lt;/h2&gt;
&lt;p&gt;Cyrille Coquard commence sa conférence avec une question choc : est-ce que WordPress est &amp;quot;de la merde&amp;quot; ? 💩&lt;/p&gt;
&lt;p&gt;En effet le code et certains fonctionnements du CMS peuvent être critiqués, mais WordPress a le mérite de rendre la création de site accessible à tous, tout en gérant une dette technique de 2003.&lt;/p&gt;
&lt;p&gt;Pour l&#039;utilisateur, c&#039;est parfait, il y a des thèmes, des extensions, Gutenberg (l&#039;éditeur de bloc) et de supports et de tutoriels.&lt;/p&gt;
&lt;p&gt;Et puis ensuite il y a la frontière du développement pour les besoins spécifiques. Créer ses propres plugins, gérer les dépendances, les mises à jour... Sans s&#039;outiller correctement c&#039;est vite un enfer.&lt;/p&gt;
&lt;p&gt;Il nous a montré qu&#039;on pouvait :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tester nos plugins avec phpunit 🎉 ;&lt;/li&gt;
&lt;li&gt;installer nos dépendances avec composer et même gérer les conflits de dépendances avec les autres plugins (voir les librairies mozart et strauss) ;&lt;/li&gt;
&lt;li&gt;avoir un event dispatcher orienté objet pour manipuler les hooks de WordPress.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Malheureusement le code des slides n&#039;était pas lisible par l&#039;audience ; ce que nous retenons est qu&#039;il est possible de livrer du code moderne dans WordPress, d&#039;avoir une architecture respectant les bonnes pratiques actuelles : il faut simplement aller voir ailleurs que dans la documentation officielle ! Cyrille a édité son propre socle technique, le projet open-source &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/wp-launchpad/launchpad&quot;&gt;Launchpad&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Nous regrettons que le drama WP Engine / Automattic n&#039;ait pas été abordé non plus ! 🍿 Il met en péril l&#039;écosystème si riche de WordPress.&lt;/p&gt;
&lt;h2&gt;Utiliser un framework pour écrire de la documentation ? 📖&lt;/h2&gt;
&lt;p&gt;Dans le contexte d&#039;une boîte qui grandit, de plus en plus de développeuses et développeurs rejoignent l’équipe technique. Très vite, la nécessité d’un vocabulaire commun, et donc d’une solide documentation se fait sentir. &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://x.com/martinsupiot&quot;&gt;Martin Supiot&lt;/a&gt; a tenu à compléter cette documentation qui était très faible et nous donne son retour d’expérience sur le sujet.&lt;/p&gt;
&lt;p&gt;Il a pris quelques développeurs et ils ont analysé la documentation : un retour surprenant, la documentation n’était pas seulement difficile à utiliser, elle était également… pas simple à écrire ! 😨&lt;/p&gt;
&lt;p&gt;Alors c&#039;est quoi leur processus d&#039;écriture ? Ici on ne va parler que documentation technique, ainsi que de documentation informationnelle, donc qui explique ce qui existe actuellement.&lt;/p&gt;
&lt;p&gt;Ils utilisent le framework Dàtaxis, un framework pensé pour les lecteurs. Les documents créés s’articulent autour de quatre axes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Les explications&lt;/strong&gt; : elles clarifient un sujet, facilitent la compréhension, expliquent un contexte ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les tutoriels&lt;/strong&gt; : ils permettent l’apprentissage, apportent de la confiance ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le how-to guide&lt;/strong&gt; : il est axé sur un objectif précis, souvent une action répétitive ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les références&lt;/strong&gt; : c’est comme le swagger d&#039;une Api. Elles informent sur tous les paramètres qui existent, les options possibles etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Comment nommer ces documents ? Le titre doit être simple, clair, efficace. Quand on lit le nom du document, on doit tout de suite comprendre ce qu&#039;il y a dedans. Pour faire simple, les titres peuvent commencer par :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;Pourquoi …&amp;quot; s’il s’agit d’une explication ;&lt;/li&gt;
&lt;li&gt;&amp;quot;Comment …&amp;quot; pour les tutoriels et les how-to ;&lt;/li&gt;
&lt;li&gt;&amp;quot;Les différents …&amp;quot; lorsqu’on fait des références.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ce framework offre également la possibilité d&#039;avoir des templates de documentation. Martin nous donne pas mal de conseils pour un documentation de qualité et bien maintenue :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ajouter des guidelines afin d’aider ses équipes à documenter ;&lt;/li&gt;
&lt;li&gt;Savoir à quelle audience on s’adresse : développeur.se.s front ? Back ? Graphistes ? Product Owner ?&lt;/li&gt;
&lt;li&gt;Définir des outils (comme &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://emersonbottero.github.io/mermaid-docs/syntax/classDiagram.html&quot;&gt;Mermaid&lt;/a&gt;, ou &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.drawio.com/&quot;&gt;draw.io&lt;/a&gt;) ;&lt;/li&gt;
&lt;li&gt;Faire attention au formatage, à la ponctuation ;&lt;/li&gt;
&lt;li&gt;Revenir régulièrement sur la documentation pour la garder à jour, surtout les how-to ;&lt;/li&gt;
&lt;li&gt;Trouver un relecteur, et définir des mainteneurs pour chaque fichier, qui seront en charge d’effectuer le point précédent par exemple ;&lt;/li&gt;
&lt;li&gt;Supprimer les documents qui ne sont plus à jour.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pro Tip © : dans Notion on peut donner une date de validité à un document !&lt;/p&gt;
&lt;p&gt;Et bien sûr, il ne faut pas oublier de documenter la documentation ! Cela pousse les autres à écrire de la documentation technique. Martin en a fait l’expérience et les résultats ont été très satisfaisants : toutes les équipes se sont mises à consulter et rédiger beaucoup plus de documentation depuis qu’il l’a lui-même documentée !&lt;/p&gt;
&lt;h2&gt;Merci l&#039;AFUP&lt;/h2&gt;
&lt;p&gt;Pour conclure, nous voulons remercier très très fort l&#039;ensemble des bénévoles de l&#039;AFUP, les speakers, Amélie et tous les sponsors qui ont rendu cet événement possible. C&#039;est une organisation de dingue pour offrir au public ces deux journées de PHP, et nous avons adoré y assister.&lt;/p&gt;
&lt;p&gt;À bientôt et bravo aux lecteurs qui sont arrivés jusque là ! Nous espérons que ces récapitulatifs vous sont utiles et que vous avez pu y apprendre autant que nous ! 💕&lt;/p&gt;

        </content>
    </entry></feed>
