FrankenPHP Symfony : Guide pour tripler vos performances

FrankenPHP Symfony : Guide pour tripler vos performances

Sam Rollin
Sam Rollin
January 24, 2026
Dernière mise à jour : February 15, 2026
January 24, 2026

PHP-FPM nous a bien servis pendant des années, mais il comporte une limitation fondamentale : chaque requête déclenche un démarrage complet du framework. FrankenPHP adopte une approche différente en gardant votre application Symfony en mémoire, éliminant ce coût de démarrage répété. Les résultats? Des benchmarks publiés montrent une amélioration du débit de 3× et une latence qui passe de 45ms à 8ms.

Ce guide couvre ce qu'est le mode worker de FrankenPHP, comment le configurer avec Symfony, ce que les benchmarks montrent réellement (et ce qu'ils ne montrent pas), ainsi que les réalités opérationnelles auxquelles vous devez vous préparer. Ce tour d'horizon complet de l'intégration FrankenPHP Symfony vous aidera à comprendre les compromis entre PHP-FPM et FrankenPHP.

Prérequis

Avant de commencer, assurez-vous d'avoir :

  • PHP 8.2 ou supérieur
  • Symfony 7.2 (Symfony 7.4 recommandé pour le support natif)
  • Docker (recommandé) ou un environnement d'hébergement compatible
  • Une familiarité de base avec le composant runtime de Symfony

Qu'est-ce que FrankenPHP et pourquoi est-ce important?

FrankenPHP intègre PHP directement dans un serveur basé sur Go construit sur Caddy. Créé par Kévin Dunglas (la personne derrière API Platform), il remplace la configuration traditionnelle Nginx PHP-FPM par un seul serveur d'application. Cette approche de serveur PHP Caddy simplifie considérablement le déploiement.

En date de mai 2025, FrankenPHP est officiellement soutenu par la PHP Foundation, avec le code source hébergé sous l'organisation PHP. Il compte plus de 8 000 étoiles GitHub et plus de 100 contributeurs.

La fonctionnalité clé sur laquelle nous nous concentrons est le mode worker. Voici la différence :

PHP-FPM traditionnel :

  • Crée de nouveaux processus PHP par requête
  • Le framework démarre à chaque requête
  • Les connexions à la base de données et au cache se rétablissent à chaque fois

Mode Worker FrankenPHP :

  • Maintient des workers persistants qui gèrent plusieurs requêtes
  • Démarre Symfony une seule fois et garde le kernel chaud
  • Les connexions à la base de données et Redis restent ouvertes entre les requêtes

Ce changement architectural produit quatre gains spécifiques :

  • Aucun coût de démarrage par requête
  • Connexions persistantes à la base de données et au cache (connexions PHP persistantes)
  • Le bytecode OPcache reste chaud en mémoire
  • Moins d'opérations sur le système de fichiers

Comprendre les benchmarks

Avant d'implémenter quoi que ce soit, examinons ce que les benchmarks FrankenPHP montrent réellement.

Données de benchmark primaires (AWS t3.medium, janvier 2026)

Conditions de test : endpoint API JSON Symfony 7.4, tests de charge k6

Nginx PHP-FPM : ~1 240 RPS de débit, ~45ms de latence p95

Mode Worker FrankenPHP : ~3 850 RPS de débit, ~8ms de latence p95

Différence : débit 3,1× plus élevé, latence 5,6× plus rapide

Ces chiffres proviennent d'un benchmark publié par un seul auteur. Ils sont plausibles étant donné l'élimination du coût de démarrage, mais vos résultats varieront selon votre charge de travail réelle.

Pourquoi les affirmations de performance varient autant

Vous verrez des chiffres très différents selon les sources :

  • Benchmarks API publiés : 3× de débit, amélioration de latence de 5-6×
  • Tests communautaires sur Raspberry Pi 5 : amélioration de 12-19×
  • Présentations en conférence : « 15× plus rapide »

Ces chiffres ne sont pas nécessairement contradictoires. L'amélioration dépend fortement de la proportion du temps de requête consacrée au démarrage du framework par rapport au travail réel comme les requêtes de base de données ou les appels API externes. Les endpoints CPU-bound avec un overhead de framework minimal verront des gains plus modestes que les endpoints lourds en démarrage.

Notre expérience montre que les applications avec des problèmes de performance de démarrage Doctrine importants ou des conteneurs de services complexes voient les améliorations les plus spectaculaires. Les endpoints simples déjà bien mis en cache montrent des gains plus modestes.

Étapes d'implémentation

Cette section couvre le processus de configuration FrankenPHP Symfony 7.4 étape par étape.

Étape 1 : Choisissez votre stratégie de version Symfony

Symfony 7.4 : Le support natif de FrankenPHP est intégré. Aucun paquet supplémentaire requis.

Symfony 7.2-7.3 : Vous aurez besoin du paquet runtime :

composer require runtime/frankenphp-symfony

Le composant runtime de Symfony gère l'intégration automatiquement.

Étape 2 : Configurez votre environnement

Ajoutez ceci à vos variables d'environnement :

APP_RUNTIME=Runtime\ FrankenPhpSymfony\Runtime

Pour Symfony 7.4 , cette étape peut être inutile selon votre configuration, car le framework gère la détection automatiquement.

Étape 3 : Configurez le mode worker FrankenPHP

Votre Caddyfile (ou configuration équivalente) doit pointer vers le contrôleur frontal de Symfony :

{    frankenphp } localhost {    root * /app/public    php_server {        worker /app/public/index.php    } }

Étape 4 : Lancez FrankenPHP

Pour le développement local :

frankenphp php-server --worker public/index.php

Avec FrankenPHP Docker :

docker run -v $PWD:/app -p 80:80 dunglas/frankenphp

Étape 5 : Configurez les limites de redémarrage des workers

Le runtime expose un paramètre frankenphp_loop_max qui contrôle combien de requêtes chaque worker gère avant de redémarrer :

FRANKENPHP_LOOP_MAX=500

La valeur par défaut est 500 requêtes. Définissez à -1 pour ne jamais redémarrer les workers. Ce paramètre existe pour aider à gérer les fuites de mémoire dans les processus de longue durée.

Erreurs courantes à éviter

Comprendre les problèmes de fuite de mémoire en mode worker PHP est essentiel pour un déploiement réussi.

1. Oublier que les workers sont de longue durée

Le plus grand changement de mentalité : vous n'êtes plus dans un modèle sans partage par requête. Les variables statiques et l'état des services persistent entre les requêtes.

Code problématique :

class RequestCounter {    private static int $count = 0;        public function increment(): int    {        return   self::$count;    } }

Ce compteur s'accumulera entre les requêtes, ce qui n'est probablement pas ce que vous voulez.

2. Ignorer les fuites de mémoire

Ce qui est un désagrément mineur en PHP-FPM devient critique en mode worker. Un service qui fuit 1 Mo par requête finira par faire planter votre worker.

Nous avons constaté que les sources de fuites les plus courantes sont :

  • Les écouteurs d'événements qui accumulent des données
  • Les services qui mettent en cache des données spécifiques à la requête
  • Les entités Doctrine conservées en mémoire

Utilisez le tag kernel.reset de Symfony pour les services qui nécessitent un nettoyage entre les requêtes :

services:    App\Service\RequestScopedService:        tags:            - { name: 'kernel.reset', method: 'reset' }

Le mécanisme kernel.reset de Symfony est crucial pour une optimisation correcte des performances Symfony en mode worker.

3. S'attendre au rechargement à chaud en développement

Puisque l'application reste en mémoire, les changements de code ne prendront pas effet immédiatement. Vous aurez besoin d'un observateur de fichiers pour redémarrer les workers pendant le développement :

frankenphp php-server --worker public/index.php --watch

Consultez la documentation FrankenPHP pour la configuration watch exacte qui correspond à votre installation.

4. Ne pas tester les fuites d'état

Avant de déployer le mode worker en production, exécutez des requêtes séquentielles sur vos endpoints et vérifiez que la requête A n'affecte pas les résultats de la requête B.

Tests et vérification

Vérifiez que le mode worker est actif

Vérifiez vos en-têtes de réponse ou vos logs. FrankenPHP ajoute des en-têtes spécifiques indiquant que le mode worker est actif.

Faites des tests de charge avant et après

Exécutez des tests de charge comparatifs avec un outil comme k6 ou wrk :

# Baseline avec PHP-FPM k6 run --vus 50 --duration 30s load-test.js # Après le passage à FrankenPHP k6 run --vus 50 --duration 30s load-test.js

Comparez le débit et la latence p95. Vous devriez voir des améliorations, bien que l'ampleur dépende de vos endpoints spécifiques.

Surveillez l'utilisation de la mémoire

Observez la consommation mémoire de vos workers au fil du temps :

docker stats

Si la mémoire augmente constamment, vous avez une fuite à traquer ou vous devez réduire votre valeur frankenphp_loop_max.

Vérifiez les fuites d'état

Créez un test qui envoie plusieurs requêtes séquentielles et vérifie l'isolation :

public function testRequestIsolation(): void {    // La première requête définit une valeur    $this->client->request('POST', '/session/set', ['value' => 'first']);        // Nouvelle session client    $newClient = static::createClient();        // Ne devrait pas voir la valeur précédente    $newClient->request('GET', '/session/get');    $this->assertNotEquals('first', $newClient->getResponse()->getContent()); }

Quand FrankenPHP est pertinent (et quand il ne l'est pas)

Bons candidats :

  • Endpoints API avec un overhead de démarrage de framework significatif
  • Applications où les coûts d'infrastructure sont une préoccupation
  • Projets déjà à l'aise avec les déploiements conteneurisés

Moins idéal :

  • Applications déjà fortement optimisées avec le préchargement OPcache (les gains seront moindres)
  • Code legacy avec des suppositions d'état statique qui nécessiteraient un refactoring important
  • Équipes sans expérience dans la gestion de processus de longue durée

Conclusion

Le mode worker FrankenPHP offre de réels gains de performance pour les applications Symfony en éliminant le démarrage du framework à chaque requête. Les benchmarks publiés montrent des améliorations de débit de 3× et des réductions de latence de 5-6×, bien que vos résultats spécifiques dépendent des caractéristiques de votre charge de travail.

Le compromis est la complexité opérationnelle. Vous passez du modèle traditionnel sans partage de PHP à des processus persistants, ce qui signifie accorder plus d'attention à la gestion de la mémoire et à l'isolation de l'état.

Notre approche consiste à commencer avec des endpoints non critiques en staging, mesurer les gains réels pour votre cas d'utilisation spécifique, et étendre graduellement une fois que vous avez confirmé que vos services gèrent correctement le cycle de vie des workers.

L'implémentation de FrankenPHP nécessite une attention particulière à la configuration des services, à la gestion de la mémoire et à l'infrastructure de déploiement. Si vous évaluez si le mode worker convient à votre application Symfony, ou si vous avez rencontré des problèmes de fuites de mémoire et de gestion d'état pendant la migration, nous pouvons vous aider à évaluer votre base de code et planifier une approche d'implémentation qui tient compte de votre architecture spécifique.

Share this article