Migration

Ce guide fait passer une application PHP existante sur OxPHP. Commencez par la section qui correspond à ce que vous exécutez aujourd'hui, nginx + PHP-FPM ou bien FrankenPHP et RoadRunner, et lisez Ce qui ne se transpose pas avant de vous engager dans la migration.

Depuis nginx + PHP-FPM

La plupart des applications migrent sans modification de code. Le mode par défaut exécute chaque requête à partir d'un état PHP vierge, le même cycle de vie que celui de PHP-FPM, si bien que le travail consiste à traduire la configuration. Les exemples de déploiement sont des recettes complètes pour Laravel, Symfony, WordPress, Drupal, Magento et d'autres.

1. Choisissez le mode de routage qui correspond à votre try_files

nginx OxPHP
try_files $uri /index.php?$query_string (Laravel, Symfony, Yii) ENTRY_FILE=index.php (Framework)
try_files $uri $uri/ /index.php avec plusieurs points d'entrée PHP (WordPress, OpenCart) ENTRY_FILE non défini (Traditional)
try_files $uri /index.html pour une application monopage ENTRY_FILE=index.html (SPA)

2. Construisez l'image

Partez du Dockerfile de deux lignes du Démarrage rapide. Les extensions doivent être compilées pour PHP thread-safe, dans une étape php:*-zts-alpine avec la même version mineure de PHP que l'image OxPHP ; voir Installer des extensions PHP en production.

Warning

Vérifiez que chaque extension PECL que vous utilisez prend en charge ZTS. OxPHP n'exécute que du PHP thread-safe, si bien qu'une extension sans prise en charge de ZTS ne peut pas être chargée.

3. Traduisez la configuration

L'image ne charge aucun php.ini. Conservez vos réglages PHP (memory_limit, upload_max_filesize, OPcache, …) dans un fichier à vous, placé n'importe où dans le projet, par exemple docker/php.ini, et copiez-le dans le répertoire de configuration PHP de l'image. Ce répertoire est un chemin à l'intérieur du conteneur, pas dans votre projet :

Dockerfile
FROM ghcr.io/oxphp/oxphp:0.13.0 COPY docker/php.ini /usr/local/etc/php/conf.d/zz-app.ini COPY --chown=www-data:www-data . /var/www/html/public

PHP lit chaque fichier .ini de ce répertoire dans l'ordre alphabétique et la dernière valeur l'emporte : le préfixe zz- fait donc s'appliquer le vôtre après les fichiers propres à l'image. Monter le fichier en tant que volume fonctionne aussi ; voir Configuration PHP.

Le reste se traduit en variables d'environnement :

nginx / PHP-FPM OxPHP
listen 443 ssl, ssl_certificate TLS_CERT, TLS_KEY, TLS_MIN_VERSION ; voir TLS
pm = static / pm = dynamic, pm.max_children PHP_WORKERS=N / PHP_WORKERS=MIN:MAX
php_admin_value[...], php.ini votre propre fichier .ini copié dans /usr/local/etc/php/conf.d/ dans l'image (voir plus haut)
gzip on, brotli on compression activée par défaut, configurée avec COMPRESSION_ENCODINGS ; voir Compression
expires 30d pour les fichiers statiques STATIC_MAX_AGE (par défaut 30d)
limit_req RATE_LIMIT, RATE_WINDOW_SECONDS (fenêtre fixe par IP) ; voir Limitation de débit
set_real_ip_from, real_ip_header TRUSTED_PROXIES ; voir Proxys de confiance
error_page 404 /404.html ERROR_PAGES_DIR avec {status}.html ; voir Pages d'erreur
access_log ACCESS_LOG=all ou error (lignes JSON) ; voir Journalisation des accès
location ~ ^/uploads/.*\.php$ { deny all; } PHP_DENY_PATHS=/uploads/** ; voir Liste de refus d'exécution PHP
pm.status_path, ping.path INTERNAL_ADDR avec /health et /metrics ; voir Contrôles de santé
fastcgi_finish_request() oxphp_finish_request() ; voir Réponse anticipée

4. Vérifiez les différences de comportement

  • max_execution_time compte le temps réel écoulé. Sous PHP-FPM, le temps passé à attendre une base de données ou un appel HTTP n'est pas décompté ; sous PHP thread-safe, il l'est. Un endpoint lent qui n'atteignait jamais la limite auparavant peut se mettre à renvoyer 504. Voir Délais d'expiration.
  • php_sapi_name() renvoie cli-server. Le code qui bifurque selon fpm-fcgi a besoin d'une autre vérification : function_exists('oxphp_request_id').
  • Quelques clés de $_SERVER diffèrent. SERVER_ADDR, PATH_TRANSLATED et REDIRECT_STATUS ne sont pas définies, et PHP_AUTH_USER / PHP_AUTH_PW ne sont pas extraites ; lisez plutôt HTTP_AUTHORIZATION. Voir Différences avec PHP-FPM.
  • Le framing est restreint par défaut. Les réponses portent X-Frame-Options: SAMEORIGIN et une politique frame-ancestors assortie. Définissez FRAME_OPTIONS=off si vous gérez le framing ailleurs ou si vous intégrez l'application dans une autre origine ; voir Protection contre le clickjacking.

5. Déployez à côté de l'ancienne pile

Faites tourner OxPHP à côté de nginx + PHP-FPM, dirigez une part du trafic vers lui via votre load balancer existant, utilisez /health/readiness pour les sondes, et comparez les taux d'erreur et la latence sur /metrics avant de basculer le reste.

6. Activez le mode worker en dernier

Migrez d'abord dans le mode par défaut. Définissez ensuite WORKER_MODE_ENABLED=true et faites pointer ENTRY_FILE vers un script de worker. L'état qui vit dans des propriétés statiques et les connexions de longue durée survivent désormais d'une requête à l'autre : lisez donc Ce qui est réinitialisé et ce qui persiste avant de l'activer.

Depuis FrankenPHP ou RoadRunner

Les scripts de worker gardent leur forme : un amorçage unique, puis le traitement des requêtes. Remplacez la boucle frankenphp_handle_request() par un unique appel à oxphp_worker($handler). OxPHP exécute la boucle et appelle le gestionnaire pour chaque requête ; à l'intérieur, lisez la requête depuis les superglobales ou oxphp_http_request(). La boucle PSR-7 waitRequest() / respond() de RoadRunner n'a pas d'équivalent : déplacez le gestionnaire de l'application dans ce même callback. Voir oxphp_worker() et Mode worker.

Supprimez le compteur MAX_REQUESTS

Il n'existe pas d'équivalent, et vous n'en avez pas besoin : un worker n'est pas censé être redémarré toutes les N requêtes, et le faire ne sert qu'à masquer une fuite. Si vous en soupçonnez une, suivez-la plutôt :

  • Tracez oxphp_worker_memory_bytes par worker et surveillez une hausse régulière.
  • Définissez WORKER_MAX_MEMORY_MIB comme filet de sécurité. Un worker qui dépasse la limite est remplacé avant que le conteneur ne manque de mémoire, et chacun de ces recyclages est compté dans oxphp_worker_recycles_by_reason_total{reason="max_memory"}.
  • Cette limite ne mesure que le tas propre de PHP. Si votre pile logicielle s'appuie largement sur des extensions, comparez Worker::current()->rss() à votre propre seuil et appelez Worker::scheduleExit().

Ce qui ne se transpose pas

  • OxPHP ne prend en charge ni reverse proxy ni upstream : il n'y a pas de proxy_pass ni de serveur WebSocket. Si nginx sert aussi de frontal à d'autres services, gardez un proxy devant OxPHP pour ces routes.
  • Une instance a un seul port et un seul protocole. Elle sert du HTTP en clair ou du HTTPS, pas les deux, si bien que la redirection du port 80 vers HTTPS nécessite un proxy ou une seconde instance. Le certificat est lu au démarrage, donc un renouvellement nécessite un redémarrage ; voir TLS.
  • HTTP/3 et 103 Early Hints ne sont pas encore disponibles ; voir la feuille de route.
  • OxPHP ne fonctionne que sous Linux et est distribué sous forme d'image Docker. Il n'existe pas de paquets apt ni brew. PHP 8.4 et 8.5 sont pris en charge.

Prompts pour votre agent

Ces prompts supposent un agent connecté au serveur MCP de la documentation OxPHP — il lira lui-même les pages dont il a besoin.

prompt
Migre cette application de nginx et PHP-FPM vers OxPHP. Lis la page `getting-started/migration` via le serveur MCP de la documentation OxPHP. Traduis les blocs server de nginx, les réglages de pool de PHP-FPM et les surcharges de php.ini de ce dépôt en un Dockerfile OxPHP et en variables d'environnement, choisis le mode de routage d'après les règles `try_files`, et liste chaque différence de comportement et chaque fonctionnalité non prise en charge qui s'applique à ce code, avec le fichier concerné.
prompt
Porte vers OxPHP le worker FrankenPHP ou RoadRunner de ce dépôt. Lis la page `getting-started/migration` via le serveur MCP de la documentation OxPHP. Réécris le script de worker autour d'un unique appel à `oxphp_worker()`, supprime tout compteur qui redémarre le worker toutes les N requêtes, et dis-moi quelle métrique et quelle limite mémoire mettre en place à sa place.

Et ensuite

  • Routage — les modes de routage Traditional, Framework, SPA et Worker en détail
  • Guide Docker — Dockerfiles de développement et de production, configuration de Compose, montages d'ini PHP
  • Configuration — référence complète des variables d'environnement
  • Mode worker — workers PHP persistants qui s'initialisent une fois et traitent plusieurs requêtes
  • Exemples de déploiement — recettes complètes pour Laravel, Symfony, WordPress, Drupal, Magento et d'autres
Une erreur ? Signalez-la →