Aller au contenu
AFUP AFUP Day 2025 Baromètre Planète PHP PUFA
 

FrankenPHP, dans les entrailles de l'interpréteur PHP, de machines virtuelles et des threads

Description

Lors de l'AFUP Day, je vous ai présenté FrankenPHP, un SAPI experimental permettant d'embarquer notre langage préféré dans les serveurs web écrits en Go tels que Caddy ou le serveur web local de Symfony CLI. Ce talk revient sur le progrès effectués depuis l'AFUP Day, et sur les nouveaux problèmes rencontrés suite aux retours de la communauté.

FrankenPHP utilise cgo pour permettre au code Go d'appeller le code de l'interpreteur PHP écrit en C, et inversement. Cela revient à faire exécuter la machine virtuelle de PHP elle même au sein d'un process géré par le runtime Go. Cette technique offre des opportunités immenses :

  • simplifier vos déploiements en embarquant directement un serveur web, l'interpréteur PHP et votre code source dans un seul binaire ;
  • conserver les services des applications Symfony ou Laravel chargés dans la mémoire du serveur web et s'en servir pour traiter plusieurs requêtes HTTP ;
  • écrire des fonctions en Go (ou en C, voir même en assembleur) et les appeler directement depuis le code PHP ou encore

Cependant, faire cohabiter le runtime de deux langages dans un seul process n'est pas chose aisée. Dans ce talk orienté programmation système, nous étudierons tous les pièges que nous avons du contourner pour réaliser ce synchrétisme particulier. Ca sera l'occasion de découvrir comment fonctionne l'interpréteur PHP, son mode ZTS, les threads systèmes et les green threads de Go, les signaux Unix ou encore les problèmes que peuvent poser les astuces utilisées par l'interpréteur PHP pour implémenter des fonctionnalités telles que le max_execution_time.

Note : il n'est pas nécessaire d'avoir vu le premier talk pour assister à celui ci.

Conférence donnée lors du Forum PHP 2022, ayant eu lieu les 13 et 14 octobre 2022.

Informations complémentaires

Vidéo

Le speaker

Kévin DUNGLAS

Kévin est le créateur du framework API Platform ainsi que des projets Mercure et Vulcain. Il est également membre de la Core Team Symfony et a cofondé la société autogérée Les-Tilleuls.coop.

Verbatim

_ Merci, Allan*. Vous ne le savez peut-être pas, mais toute l'identité visuelle d'API Platform et celle des Les Tilleuls.com., elle est faite par une illustratrice. Voilà le petit copain qui s'appelle FrankenPHP.

Allan m'a déjà bien présenté. Je fais du PHP depuis plusieurs années. Et de code aussi Go. Je vais vous présenter pour marier ces deux passions.

Je vais vous montrer à quoi FrankenPHP sert. On va voir les fonctionnalités qui sont dedans et qui sont relativement innovantes pour l'écosystème PHP grâce au mariage entre Go et PHP. Contrairement à PHP et FPM. La dernière partie sera moins technique que le reste du forum. Mais plus technique. Nous allons voir comment on peut interpréter l'interpréteur PHP dans un programme. Et le mot de fin habituel.

FrankenPHP, qu'est-ce que c'est ? Un serveur d'applications. Ça vous rappelle Com4. C'est pour vos applications PHP. Première particularité, il est fait pour le cloud native. Les environnements comme Kubernetes. Ce n'est pas évident de déployer des fonctions PHP dans Kubernetes. Deuxièmement, un mode worker qui permet d'améliorer les performances de l'application si elle a été conçue pour en tirer parti. Troisième grosse nouveauté, c'était mon fil rouge, je vous ai présenté plein de choses en lien avec HTTP, la nouvelle version, comment on peut améliorer la performance de nos applications web. 103 Early Hints. Ça permet d'accélérer le temps de chargement des pages Web. C'est difficile à utiliser avec PHP. Sauf avec FrankenPHP. Nous verrons que FrankenPHP peut nécessiter des adaptations pour marcher avec vos applications. Même avec WordPress.

PHP, et les conteneurs. Ce n'est pas évident. J'ai eu l'occasion de travailler pour Symfony sur la création d'un environnement docker pour lancer vos applications Symfony en local dans une chaîne de production continue et dans Kubernetes. Il y a une complexité assez importante. N'hésitez pas à regarder les squelettes qu'on propose avec Symfony et avec API Platform. À force de travailler là-dessus et avec mon expérience en développement Go, on copie-colle dans une image et on n'a pas besoin de distributions Linux. Ça marche. Je me suis dit : Est-ce qu'on ne peut pas pour tester essayer de simplifier tout cela ? C'est de là que part ce projet. FrankenPHP.

N'hésitez pas à aller voir les slides. Ce que j'ai présenté dans la SymfonyCON.

Pour conteneuriser avec Docker PHP, nous avons plusieurs moyens. Il faut choisir quelle SAPI on va utiliser. On ne va pas entrer dans trop de détails. Pascal a fait le tour hier. Nous avons plusieurs grandes variantes, de grandes manières d'embarquer PHP. Nous avons le module Apache qui va nous permettre de marquer l'interpréteur PHP dans le serveur Apache, dans les mêmes binaires et les mêmes processus. Les structures de données internes qui représentent les requêtes http. C'est reconverti dans le format PHP. Nous avons le FPM. Un processus séparé. Ensuite, Fast CDI. La requête arrive, le serveur Web la transforme. Ce sont des variables d'environnement. Il utilise le protocole Fast CGI pour envoyer dans un autre serveur distant. Vous avez accès à une version plus ou moins transformée. Via les supports globaux de PHP. Vous écrivez sur la fonction standard avec la fonction Header. Vous avez CGI tout court. C'est pareil, mais en plus long.

Il y a des trucs plus expérimentaux. Qui parmi vous a déjà utilisé nginx Unit ? Ça n'a rien à voir avec le serveur Web nginx. C'est la même boîte qui le fait. Ils ont voulu répondre aux besoins de... serveur PHP. Open Source, ça marche bien. Ça a des limitations. C'est un autre SAPI.

Pour conteneuriser nos applications, ce qu'on utilise habituellement avec Docker, c'est Apache. C'est si simple. Ça permet de faire un seul conteneur, un seul service, une seule image. Elle contient à la fois le serveur Web et l'environnement édition PHP. Si vous regardez les images officielles de WordPress pour les gros logiciels tout faits. Les progiciels en PHP. C'est systématiquement du Apache. Le problème, c'est que ça utilise Apache. Le mode de module d'Apache, il n'est pas performant. Il ne supporte pas toutes les mêmes fonctionnalités de la plate-forme Web. On ne peut pas faire du HTTP trois avec Apache. C'est moins performant que php-fpm.

Pascal a donné tous les détails hier. Celui que vous utilisez habituellement. Puisque vous utilisez FastCGI, ça marche avec à peu près tous les serveurs Web que vous avez l'habitude d'utiliser. Comme Apache, nginx, Caddy. Le problème, PHP est un service qui est externe. Ça va utiliser une connexion réseau ou un socket Unix pour transférer le réseau de Word jusqu'à... c'est difficile. Une image égale un service. On se retrouve avec ce genre d'image.

Nous avons un conteneur qui doit parler avec un autre conteneur php-fpm. Il faut partager un volume entre les deux pour qu'il y ait un socket. Il faut configurer tout cela pour que les affinités se fassent correctement. Kubernetes n'est déjà pas évident à la base. C'est chiant. J'ai passé la moitié de la nuit à réparer un cluster... si on peut éviter d'en ajouter une couche, c'est sympathique. Dans d'autres langages communication, on peut mettre un reverse proxy, etc. C'est terminé. En PHP, ce n'est pas simple.

Est-ce qu'on peut améliorer les choses ? J'ai envie de dire oui. À la fin, la réponse est plutôt peut-être.

Il y a un truc qui est sorti il n'y a pas longtemps. Un nouveau serveur Web qui s'appelle Caddy. Vous pouvez l'utiliser. C'est celui qu'on distribue avec Symfony et son intégration avec Docker et API Platform. C'est l'outsider qui commence à grappiller pas mal de parts de marché à Apache et nginx. Il est plus simple à configurer. Il a été conçu dès le départ pour être conteneurisé. Il supporte activement tous les protocoles modernes de la plate-forme. Il va exposer un serveur https, il supporte HTTP 2, HTTP 3. Il supporte les early. Il supporte ce qui est écrit en Go. C'est déjà utilisé par pas mal de gros projets avec de forts trafics en production. Ce n'est pas écrit en C. C'est plus facile quand on ne connaît pas C. Ça enlève toute une classe de problèmes de sécurité qu'on peut avoir.

FrankenPHP, qu'est-ce que c'est ? C'est un serveur d'applications. Il est basé sur Caddy. C'est fait pour les conteneurs. L'idée de base était de me simplifier la vie avec Kubernetes. De pouvoir avoir un seul conteneur de cœur qui contient un serveur Web qui expose l'application que j'ai développée. En gros, la même chose qu'Apache, mais avec quelque chose de plus moderne, et plus de fonctionnalités modernes qu'apache. Un week-end, j'ai décidé de refaire le module Apache pour Caddy.

FrankenPHP, cette version de Caddy avec PHP intégré, il va ajouter pas mal d'autres fonctionnalités qu'il est possible d'ajouter uniquement parce que notre serveur d'applications est écrit en Go et qu'on a tout un système sympathique. Tout ce que je vais vous montrer là, vous pouvez l'étendre en Go, en C, en PHP si vous êtes aventuriers. Le but à terme, c'est ce que ça puisse fonctionner en production et dans les chaînes d'application continue et de développement. Je vous montrerai des possibilités.

Pour faire cela, au cœur de ce serveur d'applications, il y a un nouveau SAPI pour Go. Pour étendre Caddy, il faut faire une bibliothèque en Go qui va implémenter l'interface, c'est HTTP de Go. Dans sa bibliothèque standard, il y a un serveur Web très optimisé qui est fait pour la production. On peut le lancer assez facilement. On peut l'étendre. Caddy, c'est tout un serveur pour la prod qui est construit autour de ce microserveur web qui a déjà dans le langage. Je me suis dit : Autant faire un truc qui marche pour n'importe quel programme écrit en Go. Ça peut être Symfony. Symfony and U, Symfony Serve, volontiers* le serveur intégré qui est fourni dans la bibliothèque standard dans la bibliothèque Go. Si vous utilisez Kubernetes, c'est aussi fait là-dessus. Si vous faites vous-même des petits serveurs Web avec Go, vous allez pouvoir utiliser FrankenPHP. L'idée, c'est de récupérer une requête HTTP pour le format interne de Go, récupérer les superglobals, récupérer le script, récupérer ce que vous allez envoyer sur la partie standard, avec Header, écho, le transformer en HTTP qui est fourni par le langage de programmation. C'est vraiment une bibliothèque autonome que vous pouvez utiliser pour faire le serveur d'applications FrankenPHP. Vous pouvez l'utiliser hyper facilement pour vos propres programmes en Go. On peut l'intégrer dans des choses avec le trafic. Il y a le petit module Caddy qui enveloppe la bibliothèque. Ce qui va aller lire le fichier.

Qu'est-ce qu'on a dedans ? Premier but, c'est que ça marche avec n'importe quelle application PHP. Vous avez un dépot git, il faut que ça puisse fonctionner. Il faut que ce soit facile à dockeriser. Un service d'envoi. Il faut que ça marche avec Docker. Docker et Kubernetes, c'est comme écraser une mouche avec un marteau-piqueur. Il faut rester le plus proche possible des logiciels qu'on utilise. Caddy. Et PHP. Ce n'est pas une réécriture de PHP. C'est le standard que vous connaissez qui est compilé et embarqué dans un programme Go, et toutes les extensions, elles vont fonctionner avec ça. Bien entendu, c'est du logiciel.

Je viens de me rendre compte que j'ai oublié quelque chose. J'ai oublié de donner accès au dépôt git. Contrairement à mon toc lors du Forum PHP, cette fois-ci, c'est prêt et ça marche. Vous allez pouvoir le tester pendant que je parle. Si je suis chiant. Comment on fait ? Je ne sais plus. Il y a un truc. C'est tout en bas. Je vais le mettre visible. Public. Est-ce que le nom est pas trop pourri ? Ça va. C'est parti.

Si vous allez...

(applaudissements)

Si vous voulez le spoiler, vous pouvez aller sur FrankenPHP.dev. Vous pourrez voir le site qu'a fait Laurie. Ça vous redirige vers le dépôt public. Vous allez pouvoir voir en détail ce que je vais vous présenter.

En plus de ce code, il y a une image, elle est dégueulasse, elle est moche, mais elle fonctionne. Si vous voulez tester avec un projet que vous avez sous la main, vous tapez cette commande. Je crée un volume, je monte dans le dossier app de mon conteneur le répertoire courant. Je mappe les ports 443. Chez moi, normalement, ça marche. Si vous lancez l'application Symfony, vous pouvez voir dans la debug bar, il y a un nouveau truc qui apparaît. SAPI. Faites chauffer les serveurs de Docker.

Ça doit marcher aussi avec Kubernetes. Nous avons ici le premier truc nécessaire.

En développant ce truc, je vais vous montrer comment j'ai procédé, je me suis dit qu'il y avait un projet sympa qui est celui de sauver la planète. Nous faisons des logiciels qui sont toujours plus gros. Ce n'est pas ce qui consomme le plus. Il faudrait peut-être mieux arrêter de faire des téléphones qui ne servent à rien... Des jets privés. Mais si on peut réduire un petit peu la consommation de nos logiciels, c'est pas mal. Est-ce qu'on peut essayer en même temps d'améliorer un petit peu la consommation énergétique et la performance ? C'est souvent lié. De nos applications Web ? Je ne sais pas si vous avez vu passer road runner. Qui a utilisé cela ? Peu de gens. C'est un truc écrit en Go. C'est un peu plus compliqué que ce que je vais vous montrer. Il y a un trafic et un mode worker. Vous savez que PHP, c'est du fire and forget. À chaque requête, il y a un nouveau chapitre qui est lancé. Tout le contexte d'exécution est recréé. Votre application Symfony, on crée les objets, on traite la requête, et on détruit tout. Ça a plein davantage en termes de simplicité de développement. Mais pas pour la consommation de ressources. Road runner et FrankenPHP changent un peu la donne. Bootez votre carnel symfony, gardez cela en mémoire, réutilisez le contexte d'exécution pour traiter chacune des requêtes comme nous faisons dans la plupart des autres langages de communication. C'est un truc spécifique PHP. Le supprimer. Le mode worker ne va pas utiliser le moteur d'opération de PHP pour le faire. Il permet de le faire très bien. Nous allons déléguer pas mal de trucs à Go et à son système qui permet de gérer l'exécution récurrente pour l'exécution de codes en parallèle. La fonctionnalité iconique comme disent les jeunes du langage. Les Goroutine. Avec Symfony runtime. Ça le serait prochainement avec Laravel.

En gros, vous créez un script comme d'habitude. Pour recréer votre autoloader comme d'habitude. Mais cette fois-ci, ça va attendre et tout va rester en mémoire. Les HTTP vont être traités avec ce qui est déjà là. Vous allez gagner du temps et des ressources.

FrankenPHP expose une nouvelle fonction qui s'appelle FrankenPHP handle request à laquelle vous allez passer une closure. La closure va être rappelée. Ce sera remis à zéro pour correspondre à la requête qui vient d'arriver. Vous gardez les objets définis ici. Mais tout ce qui concerne les entrées et les sorties, ça revient à zéro. Vous pouvez utiliser ce que vous avez déjà et traiter les nouvelles requêtes.

Après, la requête consiste à envoyer au client, on peut faire des nettoyages avant d'arrêter le serveur. Facile ? Ça va ? Ce n'est pas trop petit ?

Pour activer ce mode worker, il faut adapter l'application. Une variable d'environnement à ajouter. Un petit peu de configurer sur Caddy. Ça fonctionne. Rien de plus. J'ai mis une petite application de démo sur github.

J'ai aussi fait les petits changements nécessaires. Les petites extensions nécessaires pour Symfony pour que vous puissiez faire tourner dès maintenant les applications Symfony que vous avez avec le mode worker de FrankenPHP. Il faut récupérer un petit paquet pour le runtime de Symfony. Pour l'instant, c'est seulement une fonction de développement. Ensuite, plutôt que de faire tourner l'application avec PHP classique, on le fait tourner avec le mode worker de PHP. On peut utiliser road runner à la place.

Le petit benchmarker qui vaut ce qu'il vaut, j'ai utilisé K6 pour faire cela. J'envoie 3000 requêtes, je mets 100 connexions parallèles pendant 30 secondes. Avec FPM, je suis à 9,45 secondes en moyenne. J'ai 12,72 secondes. Nous sommes moins bons qu'avec FPM. Mais en mode worker, je suis à 2,53 ms de moyenne.

C'est très expérimental. Ne le mettez pas en production. À un moment ou l'autre, ça ne fonctionnera pas.

Une autre fonctionnalité qu'on peut ajouter grâce à Go. Le support des 103 early hints. C'est une fonctionnalité qui peut remplacer la fonctionnalité server qui est poussée par les gros acteurs de l'attaque pour des questions de performance Web. En particulier par Google. Chrome vient d'activer par défaut dans tous les navigateurs le support de ce nouveau code de réponse HTTP. L'idée de base, c'est celui-ci.

Quand nous sommes sur une page Web traditionnelle, faite en PHP par des cochons avec six lignes comptables avec un index, du vrai code, ça marche comme ça. Nous demandons un fichier. Le serveur mouline. Il génère du HTML. Quand on le récupère, le navigateur se rend compte qu'il a besoin de ressources supplémentaires. Des CSS. À ce moment, le navigateur demande le fichier CSS. Le serveur Web le renvoie. Les codes 1XX permettent de faire des réponses informatives. On peut envoyer plusieurs réponses à une même requête. Ici, dès que le serveur reçoit la requête, il sait que cette page aura besoin de ressources externes. Il envoie une ressource informative qui : commence à développer ce fichier CSS. Tu en auras besoin. Le navigateur va télécharger le fichier. Quand j'ai fini les requêtes SQL, le navigateur peut faire le rendu tout de suite. D'après les mesures qui ont été faites par Google, ça permet de gagner jusqu'à 30 % de temps de chargement pour que la page soit utilisable par l'utilisateur.

Nikita a essayé d'utiliser cela dans PHP. Mais à cause du fonctionnement de FPM, on ne peut pas faire cela. Parce que FPM, c'est forcément une seule réponse pour une requête. Pas une, pas deux, pas trois. Avec php-fpm,dans l'état actuel des choses, il n'y a pas de possibilité pour faire cela. Il faudrait patcher le CGI qui n'est plus maintenu.

Je contribue au langage. J'ai eu l'occasion de bosser là-dessus. Depuis la dernière version Go, le serveur supporte les réponses informatives qu'on peut envoyer.

Puisque FrankenPHP est en Go et qu'on utilise indirectement le moteur d'exécution, le serveur Web intégré à la bibliothèque de Go, c'est si facile d'exposer une nouvelle fonction PHP qui utilise la capacité de Go d'apporter des réponses informatiques. J'ai implémenté l'API qui a été instruit sur le digitub de PHP. Ça devrait être possible de l'ajouter pour mode Apache. Vous envoyez une en-tête. La réponse informative. Tout de suite, le navigateur reçoit la réponse et peut récupérer le fichier CSS. Vous faites les requêtes HTML, mais quand le navigateur reçoit la réponse, il est prêt.

Il reste 10 minutes. Ça devrait être moins hardcore que les trucs comme Rust. Ça marche pareil. Tout ce que vous avez appris dans les talks, ça va vous servir beaucoup. Quelques rappels, PHP, c'est un langage script interprété. L'interpréteur PHP est écrit en C. L'officiel. Il y a eu des tentatives. Facebook en a fait un en C++. Mais le standard qu'on utilise avec le mode page, il est en C. Une partie de son code qu'on appelle le Zengine va permettre d'améliorer les performances du langage comme Java. Plutôt que de faire du parcing à chaque requête, il va compiler le code source dans l'opcache, il va le réutiliser chaque fois. Il y a un compilateur dans cet outil. Il y a un exécutant qui est une machine virtuelle qui permet d'exécuter.

C'est la première grosse partie de PHP. La deuxième, ce sont les SAPI. Apache, etc. Ils vont se brancher sur un serveur Web. Ils vont implémenter un protocole réseau comme CGI ou FAST CGI. Ils vont prendre la structure de données internes du serveur qui présente une requête et la transformer dans la structure de donnée internes qui est attendue dans PHP. Ils vont prendre la sortie de l'exécuteur PHP. Il va transformer cela dans la structure du serveur Web qui va permettre d'écrire sur le stream la réponse et l'envoyer au navigateur.

Il y a un peu plus de SAPI que ça dans PHP. L'idée, c'est de se dire : Je pourrais complémenter des programmes généralement écrits en C. Et ça marche aussi avec Rust. En me permettant de les traiter à l'aide de PHP. Je pourrais utiliser PHP comme une bibliothèque C. C'est une bibliothèque C, et je pourrais l'embarquer dans une autre programme en C. Je peux booter l'interpréteur PHP. On désinitialise après.

On peut utiliser PHP comme une bibliothèque en C. Et c'est comme cela qu'il est fait le module Apache. Cette bibliothèque peut être dynamique ou statique. On peut faire un gros binaire qui contient l'interpréteur PHP. Il n'a pas besoin d'aller le chercher quelque part sur le système. PHP supporte la compilation static. C'est intéressant pour des langages comme Go et Rust. On peut copier-coller dans un conteneur Docker.

Go, c'est un langage compilé. Ce n'est pas interprété. Le code source est transformé en un seul champ compréhensible par la machine et exécuté. Ça a été créé en partie par les créateurs de C. et ils ont mis un point d'honneur au départ pour que le langage soit compatible avec les bibliothèques et le langage écrit en C. En C, on peut appeler du Go et vice versa.

On a un petit paquet qui est dans la bibliothèque standard de Go et qui permet de faire le statique entre le code C et Go. Ça devrait être possible d'embarquer PHP dans un programme écrit en Go comme Caddy. On devrait pouvoir avec FrankenPHP...

si on veut essayer de faire cela, on peut transformer un petit peu notre boucle de code. Plutôt que de faire une fonction Mail qu'on exécute, on va faire un fichier C et on crée une fonction. Ensuite, en Go, il faut les Flags pour passer au compilateur C. C'est ce qu'on va retrouver dans les faire*. Ensuite, nous avons la ligne magique qui dit : Je veux importer Go. Je veux importer le code C. La chaîne caractère n'est pas la même en C et en Go. Tu dois faire attention à la gestion de la mémoire. En Go, il faut gérer la mémoire. Après, j'appelle ma fonction C en passant le stream. Et ça marche. Je n'y croyais pas trop.

Sauf que nous avons seulement donné la possibilité de voir le script en Go. Mais on veut l'intégrer dans le serveur Web de Go. La bibliothèque standard, je l'ai expliqué, elle s'appelle net.http. C'est ce qui implémente HTTP 2. Pour lancer un serveur Web en Go, c'est facile avec la bibliothèque standard. On a un response writer. Une ligne, je lance le serveur Web. Je suis sur le port 80. Je peux faire aussi du https. C'est un serveur Web écrit en Go. En Caddy, c'est plus compliqué.

Comment on fait cela avec notre bibliothèque en C, il faut écrire... on va faire pour que le code P puisse s'écrire dans le stream. Dans notre contrôleur Symfony, il faut que j'appelle la bibliothèque C de tout à l'heure. Ça devrait fonctionner.

Pour implémenter un SAPI, il y a une structure C. Il y a des fonctions, il y en a une pour écrire dans le corps de la requête une fonction pour lire dans le corps de la réponse. Lire le corps de la requête, lire les cookies, enregistrer les dollars. Il suffit de l'implémenter. C'est fastidieux, mais pas compliqué.

Depuis le Code C, on peut appeler le code Go. J'ai implémenté les fonctions attendues par la structure en déléguant. Quand on le lance, ça reste du C. On se retrouve avec ce genre de choses. Une erreur de segmentation. Tu n'as pas géré ta mémoire comme il faut. Tu as voulu faire le malin. En plus, tu as de la programmation parallèle.

Go, Goroutine est au cœur. Ça permet de faire plusieurs choses parallèlement. Sur PHC, ce n'est pas fait comme cela. Php-fpm, c'est mono. Go utilise des threads. Ça amène plusieurs Goroutine dans un seul thread system. C'est un gros moteur d'exécution asynchrone fait pour la haute performance. Le serveur Web HTTP de Go vient implémenter avec cela.

Vous avez vu tout cela hier. De base, PHP, c'est isolé par process. Nous n'avons pas de problème. On peut avoir de la mémoire globale qui est partagée par tout le thread. Mais quand on lance avec Go, ça ne marche pas.

On peut activer le mode ZTS, Pascal m'a gâché mon talk, il a tout dit. On va pouvoir avoir un mode thread safe, et exécuter. Ça pourrait fonctionner. Mais dans un seul thread system, nous avons plusieurs Goroutine. Ce n'est pas suffisant pour faire fonctionner PHP avec Go. J'ai plein de problèmes avec les signaux qui entrent en conflit. C'est compliqué.

Même ZTS, ça ne suffit pas. Mais c'est un bon début pour faire fonctionner Go avec les Goroutine.

Il a fallu bosser. Regardez dans PHP comment ça marche et comment régler cela. La technique, c'est d'isoler PHP avec ZTS dans ses threads. Go a ses threads à lui. Il communique. Ils ne se marchent pas trop dessus. Une fois qu'on a le code, ça fonctionne presque. On n'a pas le cache. En termes de performance, le script est recompilé à chaque requête. Ça prend un temps fou. On va voir dans les codes sources pourquoi on n'a pas le cache. C'est codé en code source. Pour avoir un opcache, on ajoute le nom du SAPI, et ça marche. Le pool request n'est pas encore ouvert, mais ça va arriver bientôt. Qu'est-ce qui nous reste à faire ? Le code, nous l'avons vu. N'hésitez pas à y aller. N'hésitez pas à donner des étoiles. C'est très expérimental. Il y a eu beaucoup de choses à corriger. Si vous les trouvez, corrigez-les. Encore mieux. On va voir avec Laravel Octane. Avec Windows, je ne sais pas si ça fonctionne. Je n'en ai pas.

Voilà. Merci à tous. Bonne fin de forum.

(applaudissements)