迁移

本指南介绍如何把现有的 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(框架模式)
try_files $uri $uri/ /index.php,且有多个 PHP 入口点(WordPress、OpenCart) 不设置 ENTRY_FILE(传统模式)
单页应用的 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() 读取请求。RoadRunner 基于 PSR-7 的 waitRequest() / respond() 循环在这里没有对应物:把应用的处理逻辑移进同一个回调即可。参见 oxphp_worker() 和工作进程模式。

去掉 MAX_REQUESTS 计数器

OxPHP 没有对应的设置,你也不需要它:工作进程本就不该每处理 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 不支持反向代理或上游:没有 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。

给你的智能体的提示词

这些提示词假定你的智能体已连接 OxPHP 文档 MCP 服务器——需要哪些页面它会自己去读。

prompt
把这个应用从 nginx 和 PHP-FPM 迁移到 OxPHP。通过 OxPHP 文档 MCP 服务器读取 `getting-started/migration` 页面。 把这个仓库中的 nginx server 块、PHP-FPM 进程池设置和 php.ini 覆盖项转换成 OxPHP 的 Dockerfile 和环境变量,根据 `try_files` 规则选择路由模式,并列出适用于这份代码的每一项行为差异和不支持的功能,注明各自影响的文件。
prompt
把这个仓库中的 FrankenPHP 或 RoadRunner 工作进程移植到 OxPHP。通过 OxPHP 文档 MCP 服务器读取 `getting-started/migration` 页面。 围绕一次 `oxphp_worker()` 调用重写工作进程脚本,删除任何每 N 个请求就重启工作进程的计数器,并告诉我应当改为设置哪个指标和哪个内存限制。

下一步

  • 路由 —— Traditional、Framework、SPA 和 Worker 四种路由模式详解
  • Docker 指南 —— 开发与生产环境的 Dockerfile、Compose 配置、PHP ini 挂载
  • 配置 —— 完整的环境变量参考
  • 工作进程模式 —— 只引导一次即可处理多个请求的持久化 PHP 工作进程
  • 部署示例 —— Laravel、Symfony、WordPress、Drupal、Magento 等应用的完整方案
发现错误?报告问题 →