Миграция

Это руководство описывает перенос существующего PHP-приложения на OxPHP. Начните с раздела о том, на чём вы работаете сейчас, — nginx + PHP-FPM или FrankenPHP и RoadRunner, — и прочитайте раздел Что не переносится, прежде чем окончательно решиться на переезд.

С nginx + PHP-FPM

Большинство приложений переезжает без изменений в коде. Режим по умолчанию выполняет каждый запрос с чистого состояния PHP — это тот же жизненный цикл, что даёт PHP-FPM, — поэтому вся работа сводится к переводу конфигурации. Примеры развёртываний — это готовые рецепты для Laravel, Symfony, WordPress, Drupal, Magento и других.

1. Выберите режим маршрутизации, соответствующий вашему 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 с несколькими точками входа PHP (WordPress, OpenCart) ENTRY_FILE не задан (Traditional)
try_files $uri /index.html для одностраничного приложения ENTRY_FILE=index.html (SPA)

2. Соберите образ

Начните с Dockerfile из двух строк в разделе Быстрый старт. Расширения нужно собирать для потокобезопасного PHP — на этапе php:*-zts-alpine с той же минорной версией PHP, что и в образе OxPHP; см. Установка PHP-расширений в продакшене.

Warning

Убедитесь, что каждое используемое вами PECL-расширение поддерживает ZTS. OxPHP работает только с потокобезопасным PHP, поэтому расширение без поддержки ZTS загрузить не получится.

3. Переведите конфигурацию

Образ не загружает php.ini. Храните свои настройки PHP (memory_limit, upload_max_filesize, OPcache, …) в собственном файле в любом месте проекта, например docker/php.ini, и копируйте его в каталог конфигурации PHP внутри образа. Этот каталог — путь внутри контейнера, а не в вашем проекте:

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 читает все файлы .ini в этом каталоге в алфавитном порядке, и действует последнее значение, поэтому префикс zz- гарантирует, что ваш файл применится после собственных файлов образа. Смонтировать файл как том тоже можно; см. Конфигурация PHP.

Остальное отображается на переменные окружения:

nginx / PHP-FPM OxPHP
listen 443 ssl, ssl_certificate TLS_CERT, TLS_KEY, TLS_MIN_VERSION; см. TLS
pm = static / pm = dynamic, pm.max_children PHP_WORKERS=N / PHP_WORKERS=MIN:MAX
php_admin_value[...], php.ini собственный файл .ini, скопированный в образ в /usr/local/etc/php/conf.d/ (см. выше)
gzip on, brotli on включено по умолчанию, настраивается через COMPRESSION_ENCODINGS; см. Сжатие
expires 30d для статических файлов STATIC_MAX_AGE (по умолчанию 30d)
limit_req RATE_LIMIT, RATE_WINDOW_SECONDS (фиксированное окно на IP); см. Ограничение частоты запросов
set_real_ip_from, real_ip_header TRUSTED_PROXIES; см. Доверенные прокси
error_page 404 /404.html ERROR_PAGES_DIR с файлами {status}.html; см. Страницы ошибок
access_log ACCESS_LOG=all или error (JSON-строки); см. Логирование доступа
location ~ ^/uploads/.*\.php$ { deny all; } PHP_DENY_PATHS=/uploads/**; см. Дени-лист выполнения PHP
pm.status_path, ping.path INTERNAL_ADDR с /health и /metrics; см. Проверки работоспособности
fastcgi_finish_request() oxphp_finish_request(); см. Ранний ответ

4. Проверьте различия в поведении

  • max_execution_time учитывает реальное время. В PHP-FPM время ожидания базы данных или HTTP-вызова не засчитывается, а в потокобезопасном PHP — засчитывается. Медленный эндпоинт, который раньше ни разу не упирался в лимит, может начать возвращать 504. См. Таймауты.
  • php_sapi_name() возвращает cli-server. Коду, который ветвится по fpm-fcgi, нужна другая проверка: function_exists('oxphp_request_id').
  • Некоторые ключи $_SERVER отличаются. SERVER_ADDR, PATH_TRANSLATED и REDIRECT_STATUS не устанавливаются, а PHP_AUTH_USER / PHP_AUTH_PW не извлекаются; читайте вместо них HTTP_AUTHORIZATION. См. Отличия от PHP-FPM.
  • Встраивание во фреймы по умолчанию ограничено. Ответы содержат X-Frame-Options: SAMEORIGIN и соответствующую политику frame-ancestors. Задайте FRAME_OPTIONS=off, если управляете встраиванием в другом месте или встраиваете приложение в страницы другого источника; см. Защита от кликджекинга.

5. Разворачивайте рядом со старым стеком

Запустите OxPHP рядом с nginx + PHP-FPM, направьте на него часть трафика через существующий балансировщик нагрузки, используйте /health/readiness для проб и сравните частоту ошибок и задержки по /metrics, прежде чем переводить остальное.

6. Включайте режим воркеров последним

Сначала выполните миграцию в режиме по умолчанию. Затем задайте WORKER_MODE_ENABLED=true и укажите в ENTRY_FILE скрипт воркера. Состояние в статических свойствах и долгоживущие соединения теперь сохраняются между запросами, поэтому перед включением прочитайте Что сбрасывается, а что сохраняется.

С FrankenPHP или RoadRunner

Скрипты воркеров сохраняют свою форму: инициализация один раз, затем обслуживание запросов. Замените цикл frankenphp_handle_request() одним вызовом oxphp_worker($handler). Цикл выполняет OxPHP и вызывает обработчик для каждого запроса; внутри обработчика читайте запрос из суперглобальных переменных или через oxphp_http_request(). У PSR-7-цикла RoadRunner waitRequest() / respond() аналога нет: перенесите обработчик приложения в тот же колбэк. См. oxphp_worker() и Режим воркеров.

Откажитесь от счётчика MAX_REQUESTS

Эквивалента нет, и он вам не нужен: воркер не предназначен для перезапуска каждые N запросов, а такой перезапуск лишь маскирует утечку. Если вы её подозреваете, отслеживайте её:

  • Постройте график oxphp_worker_memory_bytes по каждому воркеру и следите за устойчивым ростом.
  • Задайте WORKER_MAX_MEMORY_MIB как страховку. Воркер, превысивший лимит, заменяется до того, как контейнеру перестанет хватать памяти, и каждый такой перезапуск учитывается в oxphp_worker_recycles_by_reason_total{reason="max_memory"}.
  • Этот лимит измеряет только собственную кучу PHP. В стеках с большим количеством расширений сравнивайте Worker::current()->rss() со своим порогом и вызывайте Worker::scheduleExit().

Что не переносится

  • В OxPHP нет обратного прокси и поддержки upstream: нет proxy_pass и нет WebSocket-сервера. Если nginx обслуживает и другие сервисы, оставьте прокси перед OxPHP для этих маршрутов.
  • У экземпляра один порт и один протокол. Он обслуживает либо обычный HTTP, либо HTTPS, но не оба сразу, поэтому для редиректа с порта 80 на HTTPS нужен прокси или второй экземпляр. Сертификат читается при запуске, поэтому для его обновления нужен перезапуск; см. TLS.
  • HTTP/3 и 103 Early Hints пока недоступны; см. дорожную карту.
  • OxPHP работает только в Linux и распространяется как Docker-образ. Пакетов apt или brew нет. Поддерживаются PHP 8.4 и 8.5.

Промпты для вашего агента

Эти промпты рассчитаны на агента с подключённым MCP-сервером документации OxPHP — нужные страницы он прочитает сам.

prompt
Перенеси это приложение с nginx и PHP-FPM на OxPHP. Прочитай страницу `getting-started/migration` через MCP-сервер документации OxPHP. Переведи серверные блоки nginx, настройки пулов PHP-FPM и переопределения php.ini из этого репозитория в Dockerfile для OxPHP и переменные окружения, выбери режим маршрутизации по правилам `try_files` и перечисли все различия в поведении и неподдерживаемые возможности, которые касаются этого кода, с указанием затронутого файла.
prompt
Перенеси воркер FrankenPHP или RoadRunner из этого репозитория на OxPHP. Прочитай страницу `getting-started/migration` через MCP-сервер документации OxPHP. Перепиши скрипт воркера вокруг единственного вызова `oxphp_worker()`, убери любой счётчик, который перезапускает воркер каждые N запросов, и скажи, какую метрику и какой лимит памяти настроить вместо него.

Что дальше

Нашли ошибку? Сообщите →