Vue d'ensemble de l'architecture
OxPHP est un serveur HTTP mono-binaire qui remplace la traditionnelle pile nginx + PHP-FPM. Il prend en charge l'analyse HTTP, la terminaison TLS, le routage, l'exécution PHP, la compression et l'observabilité dans un seul processus, sans aucune dépendance externe à l'exécution.
Fonctionnement d'OxPHP
OxPHP combine deux couches d'exécution dans un même processus :
- Couche HTTP asynchrone. Une couche réseau événementielle accepte les connexions TCP, effectue les handshakes TLS, analyse les requêtes HTTP et envoie les réponses. Elle gère des milliers de connexions concurrentes grâce à des E/S non bloquantes, si bien qu'un client lent n'en bloque jamais un autre.
- Pool de workers PHP. Un pool de workers PHP dédiés exécute vos scripts PHP. En mode standard, chaque worker traite une requête à la fois. En mode worker avec le multiplexage par Fibers activé, un seul worker peut servir plusieurs requêtes concurrentes : lorsqu'un script appelle
oxphp_sleep()ouoxphp_async_await(), la Fiber cède le thread et le worker passe à la requête suivante. - Pool asynchrone (optionnel). Des threads OS distincts pour les tâches soumises via
oxphp_async(). Activé en définissantASYNC_WORKERS > 0. Isolé du pool de workers pour que les tâches d'arrière-plan ne bloquent pas le traitement des requêtes HTTP.
Les deux couches communiquent au travers d'une file bornée. Lorsqu'une requête HTTP nécessitant une exécution PHP arrive, la couche asynchrone la place dans la file. Un worker PHP disponible la récupère, exécute le script et renvoie la réponse à la couche asynchrone pour livraison au client.
Grâce à cette séparation, les E/S réseau (acceptation des connexions, lecture des en-têtes, compression des réponses, service des fichiers statiques) n'entrent jamais en concurrence avec l'exécution PHP pour les ressources. Chaque couche monte en charge indépendamment.
Pool de workers
Le pool de workers PHP détermine combien de scripts PHP peuvent s'exécuter simultanément. OxPHP prend en charge deux modes de pool.
Pool statique
Un nombre fixe de workers démarrent au boot et restent actifs pendant toute la durée de vie du serveur. C'est le mode par défaut.
PHP_WORKERS=8 # exactly 8 workers
PHP_WORKERS=0 # auto-detect (default): half of available CPU cores, minimum 1Pool dynamique
Les workers montent et descendent en charge selon la demande. Indiquez un nombre minimal et maximal séparés par deux-points :
PHP_WORKERS=2:16 # start with 2, scale up to 16 under loadLorsque tous les workers courants sont occupés, OxPHP démarre de nouveaux workers jusqu'au maximum. Lorsqu'un worker est resté inactif plus longtemps que PHP_WORKERS_IDLE_SECONDS (par défaut : 30 secondes), il est retiré pour revenir vers le minimum.
File et contre-pression
Entre la couche HTTP asynchrone et le pool de workers se trouve une file bornée. Sa capacité vaut par défaut le nombre initial de workers multiplié par 128 et peut être remplacée par QUEUE_CAPACITY. Pour un pool statique, le nombre initial est le nombre de workers configuré. Pour un pool dynamique (MIN:MAX), le nombre initial est le minimum.
Lorsque la file est pleine (tous les workers sont occupés et la file a atteint sa capacité), OxPHP renvoie immédiatement au client une réponse 529 Site is Overloaded accompagnée d'un en-tête Retry-After. Le code de statut 529 (non standard, utilisé par Cloudflare et d'autres) distingue clairement la surcharge des erreurs applicatives (500) et de la maintenance (503), ce qui facilite la configuration des alertes et des répartiteurs de charge.
Flux des requêtes
Chaque requête traverse le même pipeline, qu'elle serve un fichier statique ou exécute du PHP :
graph TD
Client(["Client"]) --> TLS["TLS termination<br/>(if configured)"]
TLS --> Parse["HTTP parsing + Request ID"]
Parse --> Proxy["Trusted proxy resolution<br/>(if TRUSTED_PROXIES set)"]
Proxy --> Rate["Rate limiting check"]
Rate --> Route{"Route resolution"}
Route -->|Static file| Cache["File cache / disk read"]
Cache --> Compress["Compression + Response headers"]
Route -->|PHP request| Queue["Bounded queue<br/>(529 if full)"]
Queue --> Worker["PHP worker executes script"]
Worker --> Normal["Normal response"]
Worker --> SSE["SSE streaming (chunked)"]
Worker --> Early["Early response (finish_request)<br/>+ background work"]
Compress --> Deliver(["Response to client"])
Normal --> Deliver
SSE --> Deliver
Early --> Deliver
- Terminaison TLS. Si
TLS_CERTetTLS_KEYsont configurés, OxPHP gère TLS directement. Aucun reverse proxy distinct n'est nécessaire. - Analyse HTTP et ID de requête. La requête est analysée et un ID de requête unique est généré (ou un en-tête
X-Request-IDentrant est préservé). - Résolution des proxys de confiance. Si
TRUSTED_PROXIESest défini et que l'IP de connexion est de confiance, OxPHP extrait l'IP réelle du client, le protocole et l'hôte à partir des en-têtesForwarded(RFC 7239) ouX-Forwarded-*. L'IP résolue est utilisée pour toutes les étapes suivantes, y compris la limitation de débit et la journalisation des accès. Voir Proxys de confiance. - Limitation de débit. Si
RATE_LIMITest défini, l'IP du client est vérifiée par rapport au compteur de requêtes par IP. Les requêtes qui dépassent la limite reçoivent immédiatement une réponse429 Too Many Requests. - Résolution de route. L'URL est mise en correspondance avec le mode de routage configuré (traditionnel, framework ou SPA). Le résultat est soit un fichier statique, soit un script PHP, soit une 404. Le mode worker, s'il est activé, change la façon dont PHP exécute le script résolu, mais ne modifie pas la résolution de route elle-même.
- Fichiers statiques. Servis directement depuis un cache en mémoire (pour les fichiers fréquemment consultés) ou diffusés depuis le disque. OxPHP ajoute automatiquement les en-têtes
ETag,Last-ModifiedetCache-Control. - Exécution PHP. La requête est placée dans la file bornée et récupérée par un worker disponible. Si la file est pleine, le client reçoit immédiatement une 529.
- Compression. Les réponses textuelles sont compressées avec Brotli avant d'être envoyées lorsque le client envoie
Accept-Encoding: br(configurable viaCOMPRESSION_LEVEL). - Streaming SSE. Si le script définit
Content-Type: text/event-streamou appelleoxphp_stream_flush(), OxPHP bascule en mode streaming : chaque appel àflush()envoie immédiatement un fragment au client sans mettre en tampon l'intégralité de la réponse. En mode worker, le SSE fonctionne de façon coopérative avec le multiplexage par Fibers. - Réponse anticipée. L'appel à
oxphp_finish_request()envoie immédiatement la réponse HTTP au client. Le script continue de s'exécuter en arrière-plan (écriture de journaux, mise à jour de caches, envoi de notifications) sans maintenir la connexion ouverte. - Livraison de la réponse. La réponse achevée est renvoyée sur la connexion et, si la journalisation des accès est activée, une entrée de journal est écrite.
Mode worker vs mode standard
OxPHP prend en charge deux modèles d'exécution PHP :
Crée un environnement PHP neuf pour chaque requête. Les autoloaders, la configuration et les connexions à la base de données sont initialisés à chaque requête puis démantelés ensuite. Ce modèle est compatible d'emblée avec toutes les applications PHP.
Maintient les processus PHP en vie d'une requête à l'autre. Votre application effectue son bootstrap une seule fois (chargement de l'autoloader, de la configuration et établissement des connexions à la base de données), puis entre dans une boucle de requêtes. Entre les requêtes, OxPHP réinitialise automatiquement les superglobales, les tampons de sortie et les en-têtes de réponse tout en préservant l'état amorcé.
Le mode worker élimine la surcharge de démarrage propre à chaque requête, ce qui peut réduire sensiblement les temps de réponse pour les applications basées sur un framework (Laravel, Symfony, etc.) où le bootstrap est coûteux.
Pour activer le mode worker, définissez WORKER_MODE_ENABLED=true et faites pointer ENTRY_FILE vers un script PHP qui appelle oxphp_worker() :
<?php
require __DIR__ . '/../vendor/autoload.php';
$app = new MyApp\Application();
oxphp_worker(function () use ($app) {
$app->handle();
});Pour un guide détaillé, voir Mode worker.
Serveur interne
Si la variable INTERNAL_ADDR est définie, OxPHP démarre un serveur HTTP distinct sur le port spécifié. Il sert trois endpoints :
| Endpoint | Description |
|---|---|
GET /health |
État de santé en JSON (uptime, compteurs de requêtes, connexions, état des workers). Renvoie 200 en fonctionnement normal, 503 en cas de dégradation. |
GET /metrics |
Métriques au format Prometheus — compteurs de requêtes, temps de réponse, temps d'attente en file, statistiques des workers, gains de compression. |
GET /config |
Instantané de la configuration active en JSON. Les chemins des fichiers TLS sont masqués. |
Le serveur interne ne passe pas par le pool de workers PHP ni par la file bornée. Il répond directement depuis la couche HTTP asynchrone, ce qui le maintient accessible même lorsque le pool PHP est totalement chargé. Cela rend /health adapté aux sondes de liveness/readiness de Kubernetes.
Pour plus de détails, voir Serveur interne.
Sûreté
OxPHP fournit plusieurs garanties pour maintenir votre application en fonctionnement fiable en production :
- Isolation des requêtes. Si un script PHP plante ou déclenche une erreur fatale, seule cette requête est affectée. Le serveur continue de traiter normalement toutes les autres requêtes. Le worker qui a planté est automatiquement remplacé par un worker neuf.
- Redémarrage automatique des workers. OxPHP surveille la santé de tous les workers PHP. Si un worker meurt de façon inattendue, un nouveau worker est démarré à sa place sans intervention manuelle.
- Protection par contre-pression. La file de requêtes bornée prévient la surcharge. Lorsque le serveur est à pleine capacité, les nouvelles requêtes reçoivent une réponse 529 assortie d'un en-tête
Retry-Afterplutôt que d'être mises en file indéfiniment et de provoquer des délais d'expiration en cascade. - Protection contre la traversée de répertoires. Tous les chemins d'URL sont assainis avant tout accès au système de fichiers. Les tentatives de traversée encodées en pourcentage, les segments
..et les chemins qui s'échappent de la racine du document sont bloqués. - Arrêt gracieux. À la réception de SIGTERM ou SIGINT (Ctrl+C), OxPHP cesse d'accepter de nouvelles connexions et attend l'achèvement des requêtes en cours (jusqu'à un délai de drainage configurable) avant de se terminer.
Voir aussi
- Mode worker — processus PHP persistants et l'API
oxphp_worker() - Multiplexage par Fibers — des centaines de requêtes concurrentes sur un seul worker
- Streaming SSE — diffusion d'événements depuis PHP
- Réponse anticipée —
oxphp_finish_request()et traitement en arrière-plan - Promesses asynchrones —
oxphp_async()/oxphp_async_await() - Serveur interne — santé, métriques, configuration
- Routage — les trois modes de routage et la résolution des URL
- Référence de configuration — liste complète des variables d'environnement
- Métriques — observer le pool de workers et le pipeline de requêtes
- Démarrage rapide — mettre OxPHP en route en moins de 5 minutes