14 points par GN⁺ 2025-09-02 | 5 commentaires | Partager sur WhatsApp
  • Récemment, une approche hybride consistant à intégrer Go et Rust comme langages d’extension au sein d’une architecture monolithique PHP suscite un intérêt croissant
  • Auparavant, la combinaison de microservices Go et d’un monolithe PHP 8.3 permettait d’atteindre un bon équilibre entre productivité et hautes performances
  • Selon la loi de Pareto (80 % du trafic concentré sur 20 % des API), l’optimisation des endpoints critiques était indispensable ; autrefois, on y répondait par le caching et la séparation en services Go, mais la complexité augmentait
  • Avec les progrès récents de l’écosystème PHP, des techniques comme FFI, les extensions Rust et les extensions Go (FrankenPHP) permettent désormais d’améliorer fortement les performances à l’intérieur même du monolithe
  • Les extensions Rust offrent à la fois sécurité mémoire et rapidité, et FrankenPHP, avec son worker mode et ses extensions basées sur Go, a montré des performances plus de 4 fois supérieures
  • Sans subir le coût ni le risque d’une réécriture complète en Go/Rust, l’approche PHP hybride permet de conserver à la fois productivité et vitesse

Contexte et architecture existante

  • À l’origine, on s’appuyait sur une application monolithique DDD (mother) autour de laquelle étaient développés séparément des microservices basés sur Go (children) afin d’optimiser certaines fonctions
  • Les microservices Go prenaient en charge le traitement de trafic à haute performance, tandis que le monolithe PHP 8.3 apportait, dans le cadre d’une petite équipe backend, une mise en production fiable et un développement rapide de fonctionnalités
  • Cette structure offrait un point d’équilibre permettant de garantir vitesse, stabilité et productivité

Goulots d’étranglement de performance et réponses classiques

  • On observe souvent le principe de Pareto : 80 % du trafic se concentre sur 20 % des endpoints API
  • Pour ces 20 % les plus sensibles aux performances, différentes approches étaient utilisées : code optimisé, ajout d’une couche de cache, externalisation en microservices Go, etc.
  • Mais ces solutions atteignent leurs limites en matière de complexité et de charge opérationnelle

Les options hybrides du PHP moderne

  • Aujourd’hui, les technologies permettant d’améliorer directement les performances au sein d’un monolithe PHP se multiplient
  • 1. FFI (Foreign Function Interface)

    • La fonctionnalité FFI de PHP permet d’appeler directement du code C depuis PHP
    • Il devient ainsi possible d’implémenter, dans un projet PHP, de la logique de niveau système ou critique en matière de performances
    • En revanche, son usage est recommandé uniquement dans les cas adaptés, en tenant compte du coût des changements de contexte
  • 2. Extensions basées sur Rust

    • Il est possible de développer des extensions PHP en Rust (ou en Zig)
    • En déportant les zones à forte charge vers des extensions Rust offrant sécurité mémoire et performances compilées, on peut obtenir à la fois fiabilité et haute vitesse
  • 3. Extensions basées sur Go : FrankenPHP

    • Après un passage récent à FrankenPHP, l’exécution en worker mode a montré des performances plus de 4 fois supérieures à l’existant
    • Une version récente permet aussi d’écrire des extensions PHP en Go
    • Il devient ainsi possible d’exploiter directement, dans le monolithe PHP, les performances API de Go, et de combiner productivité et vitesse sans scinder les langages

Pourquoi ne pas tout migrer vers Go ou Rust ?

  • Le coût d’une réécriture complète et les risques associés sont élevés
    • Remplacer entièrement une application déjà vaste et stable par du Go ou du Rust implique un niveau important de risque et de consommation de ressources
  • PHP conserve toujours de vrais atouts
    • Pour la plupart des besoins métier, la rapidité de développement, l’écosystème accessible et les performances largement suffisantes de PHP restent très compétitifs
    • En adoptant une architecture hybride avec Go ou Rust uniquement sur les zones qui atteignent de véritables limites de performance, on peut éviter la nécessité d’une migration complète

Conclusion : la valeur du PHP hybride

  • L’écosystème PHP moderne offre à la fois une forte productivité de développement et des options d’intégration avec des extensions hautes performances (C, Rust, Go)
  • Cette structure hybride permet de garantir à la fois vitesse et productivité
  • Elle propose un nouveau paradigme d’architecture où l’on conserve un développement centré sur PHP, tout en permettant des extensions sélectives par langage selon les besoins

5 commentaires

 
naearu 2025-09-02

On dirait que cela évolue un peu comme JavaScript est en train de changer.

 
skageektp 2025-09-02

À la rigueur, avec Rust pour Node.js, d’accord ;;; mais avec PHP, comme on utilise sans arrêt des $, ça me semble peu pratique pour taper du code. Ceux qui l’utilisent bien trouvent-ils en général que ce n’est pas si gênant ?

 
proinworks 2025-09-03

Même si l’on ressent de l’inconfort, on s’y habitue vite à force de l’utiliser, non ?
L’être humain est un animal d’adaptation.

 
nemorize 2025-09-02

Si j’ai déjà ressenti une gêne vis-à-vis du concept même des variables/fonctions en PHP, je n’ai en revanche jamais été gêné par la notation en $.

Dire qu’on ne peut pas l’utiliser à cause du dollar, ou ~~que ceux qui utilisent le signe dollar gagnent beaucoup d’argent, ou qu’ils n’en gagnent pas tant que ça parce que ce n’est pas le dollar américain mais le dollar zimbabwéen~~, ce n’était pas juste des plaisanteries, non...

 
GN⁺ 2025-09-02
Avis Hacker News
  • Je développe une aversion croissante pour les frameworks généralistes (Spring, Laravel, Phoenix, etc.) : au début ils sont vraiment productifs, mais sur les projets legacy on retombe toujours sur les mêmes problèmes. Chaque projet a son propre environnement d’infrastructure et ses propres contraintes métier ; on ne peut donc pas se contenter de la voie recommandée par le framework, et on accumule partout des patchs additionnels et des contournements via des dépendances. Quand on essaie de migrer vers un nouveau langage ou une nouvelle version, toutes ces parties custom cassent, donc au final personne ne met rien à jour, jusqu’au moment où l’infra ne peut plus faire tourner le projet et où l’on finit par faire une migration massive dans la douleur. Assembler plusieurs bibliothèques et construire soi-même ses abstractions peut prendre plus de temps, mais cela permet des mises à niveau progressives avec plus de souplesse et des changements de direction rapides. L’écosystème Go me paraît idéal sur ce point : au début ça m’a semblé étrange, mais aujourd’hui j’aime même davantage cette approche.

    • Pour les frameworks web, j’ai vraiment cette impression de « c’est génial au début, puis à un moment ça devient pénible ». Pour faire une appli simple, le côté « créer un blog en 15 minutes » à la Rails donne presque l’impression de magie, mais dès que ça se complexifie, le framework devient au contraire un obstacle. Personnellement, je suis plus à l’aise avec des configurations HTTP de « niveau intermédiaire » comme Express + Node.js ou Vert.x + Java.

    • En Python, on peut distinguer les microframeworks (type Flask) et les macroframeworks (Django). Moi, je choisis toujours Django. Flask ne recommande presque rien, donc chaque projet devient à chaque fois un flocon de neige à réinventer. Il y a une fatigue décisionnelle à devoir choisir parmi N options pour l’authentification, les templates, les cookies, les e-mails, etc. En plus, beaucoup de ces bibliothèques sont maintenues par une seule personne, donc la qualité en maintenance et en sécurité est très variable. À l’inverse, avec Django, la plupart des projets se ressemblent et presque toutes les fonctionnalités de base sont disponibles immédiatement. Je n’ai de raison d’utiliser des bibliothèques d’extension que lorsque j’ai des besoins particuliers ; comme une grande partie est maintenue et validée directement, j’ai davantage confiance dans le code et dans sa sécurité.

    • S’il n’y a pas d’énorme framework en Go, c’est parce que le système de types du langage reste très inachevé. Créer des bibliothèques complexes qui s’imbriquent bien entre elles est difficile. J’ai attendu 9 ans avant de pouvoir utiliser les génériques pour créer enfin mon premier toolkit de base de données pour Go. Ça a été un succès, mais j’en suis presque à me dire que c’était mieux quand je faisais ce genre de chose en Java. Si on pouvait faire map/filter/reduce d’un type résultat vers un autre type générique, ce serait un tout autre monde. Rien qu’avec les types union, on n’aurait plus besoin du type any. Et si le langage prenait en charge la surcharge, le code serait bien plus propre ; le système de types de Go a encore besoin d’évoluer.

    • Dans mon domaine, seuls Go et Rust sont vraiment utiles. Je ne suis pas à l’aise avec la culture des frameworks très prescriptifs. Je pense que Rails, Laravel et Django sont excellents quand on est dans un cas clair de CRUD sur base de données relationnelle.

    • Ces 5 dernières années, je n’ai utilisé que des composants compatibles PSR (Php Standards Recommendation), sans framework du tout. La raison, c’est que les gros frameworks finissent toujours par ne plus convenir sur la durée. Ils imposent beaucoup de contraintes, et leur maintenance comme leurs mises à jour deviennent trop difficiles. Que ce soit pour de gros projets en entreprise ou des services personnels, j’ai le sentiment qu’une architecture centrée sur des composants PSR est meilleure.

  • Je comprends qu’une approche hybride ait du sens (C, Rust, Go, etc. en parallèle de PHP) quand on a une grosse base de code impossible à réécrire entièrement. Mais si ce n’est pas nécessaire, mon expérience est que les API en C# permettent à la fois une bonne vitesse de développement et de très bonnes performances à l’exécution. J’ai rarement eu besoin d’aller jusqu’au C++ ou Rust. PHP est bien aussi, mais il ne gère toujours pas des choses comme les tableaux typés. Par exemple, c’est un langage qui ne rejettera pas une chaîne dans un tableau de dates.

    • J’ai longtemps utilisé C# et j’ai de l’expérience avec plusieurs frameworks web/API. Quand on creuse un peu PHP, c’est vraiment agréable de voir à quel point il fournit beaucoup de fonctions de base intégrées pour le développement web. Il a aussi des défauts, mais quand il faut construire quelque chose rapidement, j’ai l’impression que PHP l’emporte.

    • C’est vrai que PHP accepte des types erronés dans les tableaux (par exemple une chaîne dans un tableau de dates). Il arrive que des comportements bizarres ou des bugs surgissent. Récemment, j’ai eu un bug où, après un json_decode(), les clés numériques devenaient des int et les autres des string, ce qui mélangeait les types de clés. Ces petits recoins peuvent être étranges, mais malgré tout PHP reste un langage Frankenstein vraiment séduisant.

    • En pratique, si on utilise un analyseur statique, ce genre d’erreur de type est généralement évité. Et il y a de fortes chances que PHP obtienne bientôt le support des génériques. On peut suivre le sujet sur le blog de thephp.foundation.

    • C’est moi l’auteur de l’article, merci de l’avoir lu. En pratique, il n’est pas nécessaire de tout réécrire : si on fait tourner PHP sur un runtime à base de workers comme swoole ou frankenphp, on peut atteindre des performances du niveau de Node. Les questions de tableaux typés ou de génériques sont prises en charge par des analyseurs statiques comme phpstan, et en utilisant des annotations de type on peut aussi nettement renforcer la sûreté de typage.

    • Depuis la fin du support de VB6, j’ai décidé de ne plus utiliser aucun langage Microsoft. Seuls les langages open source sont bons pour la santé mentale.

  • Quand je suis entré chez {{company}}, tout tournait en PHP 5.4 à l’échelle de l’entreprise, et à l’époque il y avait énormément de rejet de PHP. Mais après avoir expérimenté le PHP moderne, j’ai presque l’impression qu’au moment précis où nous sommes en train de quitter PHP, cette migration ressemble plutôt à une régression. Beaucoup de gens jugent encore PHP sur ce qu’il était à l’époque de la version 5.x, alors qu’aujourd’hui c’est complètement différent.

    • Je vois les choses un peu différemment : sur le marché de l’emploi, il subsiste encore une perception de PHP — bonne ou mauvaise — qui limite le recrutement d’excellents développeurs. Même si techniquement PHP n’est plus aussi mauvais qu’avant, sortir de PHP garde du sens en termes d’image employeur.

    • Je ne suis pas d’accord avec l’idée que « PHP est désormais awesome ». Il s’est clairement amélioré, mais le mot awesome me paraît exagéré.

    • PHP ne cesse de s’améliorer ; je me dis qu’il va falloir que j’y regarde de plus près bientôt.

    • Nous avons eu exactement la même réflexion il y a 4 ans, et nous avons finalement choisi de rester dessus en migrant vers PHP 8. Avec le recul, ce choix a bien fonctionné pour notre équipe ces dernières années.

  • Pasir ressemble à frankenphp, mais en version Rust ; c’est très prometteur, mais encore au tout début de son développement.

  • Pasir github Pasir utilise une conversion en Rust des bindings de l’API Zend.

  • Bindings Rust de l’API Zend Il existe aussi des expériences intéressantes comme ngx-php, où PHP est embarqué dans le binaire nginx via l’API Zend.

  • ngx-php github workerman implémente un runtime très rapide avec un backend hybride basé sur asio.

  • workerman github

    • Je me demande si Pasir, frankenphp et les autres prennent aussi en charge les modules PHP existants.

    • Merci pour la recommandation, ça a vraiment l’air excellent. Mais comme vous le dites, je suis d’accord pour dire que c’est encore loin d’un niveau production. Nous avons finalement choisi frankenphp, soutenu par la php foundation.

  • La complexité en débogage et en maintenance est souvent sous-estimée ; si on a le choix, je pense qu’il vaut mieux éviter ce genre de combinaison.

    • Merci pour votre avis. Nous avons choisi frankenphp ; pourriez-vous préciser concrètement pourquoi le débogage risquerait d’être plus difficile ?
  • Moi aussi, j’ai réécrit mon application, de PHP vers Go, et pour l’entreprise ça a été un investissement gagnant. Nous sommes passés de 20 000 lignes de PHP à 4 000 lignes de Go, tout en augmentant fortement l’efficacité. Si vous êtes dans une entreprise centrée sur PHP, je recommande vivement de planifier une vraie réécriture à grande échelle, accompagnée de l’ajout de tests — ce qui est plus facile en Go. À mon avis, c’est mieux que de souffrir en maintenance avec un mélange Rust/PHP ou Go/PHP.

    • Je me demande comment le passage de PHP à Go a pu réduire autant le nombre de lignes. Go me semble pourtant être un langage très verbeux ; dans mon expérience, PHP est d’un niveau intermédiaire, Haskell est le plus compact, et Java/Go ont plutôt tendance à être plus longs à cause de la gestion des erreurs, etc.

    • J’ai du mal à croire à une division par 5 du nombre de lignes en migrant de PHP vers Go. J’ai toujours trouvé que PHP avait beaucoup de syntaxes raccourcies, alors que Go en offre très peu.

    • Quand une réécriture améliore les performances et l’efficacité, la vraie question est toujours de savoir si cela vient du langage lui-même ou des améliorations d’architecture introduites au passage.

    • Quand je réécris en Go, j’ai plutôt l’impression que les motifs if err != nil font gonfler le code jusqu’à le multiplier par 10. J’ai connu une réécriture vers Python, et Python aussi devient très verbeux, avec des patterns comme l’injection de dépendances qui rendent les tests pénibles.

    • Je ne recommande pas automatiquement les réécritures (même si j’en ai mené deux à bien, je ne pense pas que ce soit ce que je choisirais moi-même). Les runtimes PHP modernes sont vraiment devenus très rapides, donc cela vaut aussi la peine d’être essayé. Surtout si on exploite à fond les caches de travail avec quelque chose comme swoole, on peut parfois aller aussi vite que Go (ça vaut le coup de consulter des benchmarks).

  • J’ai parfois l’impression qu’on devrait vraiment revenir aux fondamentaux : les pixels, les données, la latence et la bande passante. Le web aussi est au fond un problème d’optimisation consistant à afficher les bons pixels, assez vite pour que l’œil humain le perçoive comme rapide, dans les limites des ressources réseau. Je trouve qu’il faut raisonner ainsi : « quels sont les pixels que l’utilisateur va voir immédiatement ? », « quelles données sont nécessaires pour les afficher ? », « quelles données utiles pour la suite faut-il précharger ? ».

    • Je construis alumina-ui avec egui pour WASM, et sans toute la complexité de HTML, JavaScript, CSS et des autres connaissances web, il suffit de fournir un canvas à la bonne taille dans le navigateur, puis de tout rendre directement en WebGL. C’est très pratique, parce que ça me permet de produire rapidement des graphismes accélérés par le GPU avec un langage que j’aime. J’aime beaucoup WASM/WebGL grâce à ce niveau d’abstraction.

    • Se concentrer uniquement sur les pixels visibles par l’utilisateur, c’est trop réducteur. Dans un projet logiciel, il faut optimiser non seulement l’UX immédiate, mais aussi le temps de développement. Le délai avant l’affichage du premier écran et le temps réel nécessaire au développement ne sont absolument pas proportionnels.

  • FrankenPHP a l’air très intéressant, mais dans la pratique c’est un peu atypique. Personne n’utilise PHP sans modules PHP ; la liste des modules pris en charge par FrankenPHP n’est pas très claire, et il n’est pas évident non plus de savoir si l’on peut compiler et ajouter les modules tiers dont on a besoin. C’est très étroitement lié à Caddy, et comme je ne connais pas bien ce serveur web, je préfère nginx. Faute de guide, je ne sais même pas s’il peut servir de remplaçant à php-fpm derrière nginx. En plus, les images Docker de Caddy ou FrankenPHP semblent ne penser qu’aux certificats Let’s Encrypt ; si on veut configurer soi-même le SSL ou n’opérer qu’en HTTP, ce n’est vraiment pas intuitif.

    • Pour la question des modules PHP, en général on les compile directement dans le Dockerfile. Par exemple, pour ajouter le module pgsql, on installe les dépendances via apt, on installe le module avec docker-php-ext-install, puis on supprime les dépendances et on nettoie. Pour le HTTP aussi, il suffit d’ouvrir directement le port 80 dans le Caddyfile.

    • Le build statique inclut par défaut plusieurs dizaines de modules PHP majeurs. La liste détaillée des modules et le script de build sont visibles dans frankenphp build-static.sh.

  • Je me demande pour quel type de charge de travail il devient vraiment nécessaire d’ajouter des extensions dans des langages comme C/Rust/Go. Je comprends que ça existe, mais j’aimerais mieux comprendre pourquoi ajouter cette complexité à la stack, et dans quels cas on ne peut pas résoudre le problème autrement.

  • Ce que je déteste le plus dans PHP, c’est que l’application entière est bootstrapée à chaque requête HTTP, avec autoloading et réévaluation de la configuration à chaque fois. Bien sûr il existe du cache et d’autres mécanismes, mais par rapport à une approche où le moteur reste toujours en mémoire comme en Go, ça me paraît encore difficile à justifier.

    • En réalité, il existe plusieurs bibliothèques assez bien documentées qui permettent de faire tourner un serveur autonome uniquement en PHP, et elles sont utilisables en production. Les performances du JIT de PHP sont aussi assez impressionnantes.
  • reactphp.org

  • php.net ev module

  • pecl-event

  • workerman.net

    • Il n’est pas du tout obligatoire de fonctionner ainsi. Certains runtimes PHP permettent à une seule exécution de script de traiter plusieurs requêtes HTTP.
  • documentation worker de frankenphp

    • Moi, je considère au contraire que c’est le meilleur atout de PHP. Le scale-out en devient extrêmement simple.

    • Personnellement, j’aime plutôt cette architecture. Elle minimise intrinsèquement l’état partagé (jusqu’à un certain point, en tout cas).

    • Tu as raison, cette approche est vraiment mauvaise. C’est particulièrement problématique quand PHP sert lui-même de langage de template. Les moteurs de templates custom ou les runtimes persistants qui essaient de corriger ça reviennent juste à « maquiller un cochon ». On aurait mieux fait, dès le départ, de choisir un langage qui ne s’appelait pas Personal HomePage.