迁移
本指南介绍如何把现有的 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 扩展。
请确认你使用的每个 PECL 扩展都支持 ZTS。OxPHP 只运行线程安全的 PHP,因此不支持 ZTS 的扩展无法加载。
3. 转换配置
该镜像不加载任何 php.ini。请把你的 PHP 设置(memory_limit、upload_max_filesize、OPcache……)放在你自己的一个文件里,放在项目中的任意位置均可,例如 docker/php.ini,然后把它复制到镜像的 PHP 配置目录中。该目录是容器内的路径,而不是你项目里的路径:
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/publicPHP 会按字母顺序读取该目录下的每个 .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 服务器——需要哪些页面它会自己去读。
把这个应用从 nginx 和 PHP-FPM 迁移到 OxPHP。通过 OxPHP 文档 MCP 服务器读取 `getting-started/migration` 页面。
把这个仓库中的 nginx server 块、PHP-FPM 进程池设置和 php.ini 覆盖项转换成 OxPHP 的 Dockerfile 和环境变量,根据 `try_files` 规则选择路由模式,并列出适用于这份代码的每一项行为差异和不支持的功能,注明各自影响的文件。把这个仓库中的 FrankenPHP 或 RoadRunner 工作进程移植到 OxPHP。通过 OxPHP 文档 MCP 服务器读取 `getting-started/migration` 页面。
围绕一次 `oxphp_worker()` 调用重写工作进程脚本,删除任何每 N 个请求就重启工作进程的计数器,并告诉我应当改为设置哪个指标和哪个内存限制。