安装
OxPHP 以 Docker 镜像的形式分发,这是开始为 PHP 应用提供服务最快、也最推荐的方式。该镜像在 Alpine Linux 上打包了服务器二进制文件、PHP 8.4 或 8.5 ZTS、OxPHP 扩展以及全部运行时依赖。默认的 :0.13.0 和 :latest 标签附带 PHP 8.5;如需 PHP 8.4,请拉取 :0.13.0-php8.4、:php8.4 或任意 *-php8.4* 标签变体。
Docker(推荐)
从 GitHub Container Registry 拉取官方镜像:
docker pull ghcr.io/oxphp/oxphp:0.13.0镜像包含:
- OxPHP 服务器二进制文件 —— 异步 HTTP 服务器
- PHP ZTS 运行时 —— 8.4 或 8.5,取决于所拉取的标签;用于多工作进程执行的线程安全 PHP
- OxPHP PHP 扩展(
oxphp_sapi.so)—— 提供oxphp_request_id()、oxphp_server_info()、oxphp_worker()及其他内置函数 - 桥接库(
liboxphp_bridge.so)—— 连接 Rust 服务器与 PHP 运行时 - Alpine Linux 基础镜像 —— 最小化的运行时体积(另有每夜发布的 Debian 构建,参见 Debian(glibc)镜像)
- 无
USER指令 —— 镜像以 root 身份启动(与nginx:alpine/php-fpm:alpine/frankenphp:alpine一致),以便绑定特权端口,但随后oxphp serve/run会在提供服务前默认降权到www-data,因此开箱即用时流量不会以 root 身份处理。www-data用户(UID 82,GID 82;在 Debian 镜像中为 33)已预先创建,且构建时/var/www/html已 chown 给它。如需指定特定 uid 或额外的纵深防御,请在编排层显式固定运行时身份:docker run --user www-data ghcr.io/oxphp/oxphp:0.13.0- Compose:
services.app.user: www-data - Kubernetes:
securityContext.runAsUser: 82
Debian(glibc)镜像
每夜构建流水线还会发布同一个服务器基于 Debian trixie(而非 Alpine)构建的版本:ghcr.io/oxphp/oxphp:nightly-trixie(PHP 8.4)、:php8.4-nightly-trixie 和 :php8.5-nightly-trixie,支持 linux/amd64 和 linux/arm64。
正式发布的标签只有 Alpine 版本。每夜标签从 main 构建,并且只在 main 自上一次完全成功的运行以来有变动的夜晚才会构建,不提供任何稳定性承诺。如果某一晚 Debian 构建失败,:nightly-trixie 和 :php<minor>-nightly-trixie 会停留在最后一次成功的构建上,因此它们可能落后于 :nightly。
Debian 镜像处理每个请求消耗的 CPU 更少,原因有二:
- 线程局部存储。 线程安全的 PHP 通过一个线程局部指针访问引擎的每线程状态。基于 musl 构建时,PHP 让该指针停留在 global-dynamic TLS 模型上,因此
libphp.so中的代码要通过调用 TLS 运行时才能访问它;基于 glibc 时则使用 initial-exec,即一次相对于线程指针的加载。 - 内存分配器。 扩展以及 libpq 之类的库会在 PHP 自身的内存管理器之外使用 musl 的分配器,它会为 libpq 的查询结果映射新的内存,并在这些结果被释放时解除映射。在 x86-64 上,这会在服务器的各个线程之间引发 TLB shootdown。
下表是 OxPHP 容器每个响应的 CPU 时间,Debian 镜像对比 Alpine 镜像,OxPHP 修订版和 PHP 8.5 补丁版本均相同:一个 Yii3 应用,在 wrk -c64 下运行,取六次交替运行的中位数。每一行都在同一台主机上测得:x86-64 在一台 16 vCPU 的云虚拟机(AMD EPYC 9575F)上,arm64 在 Apple 芯片上的 Docker Desktop 中。不包括 Postgres 自身的 CPU。
| Yii3 页面 | 读取 Postgres 的页面 | |
|---|---|---|
| x86-64,经典模式 | −18.9% | −14.1% |
| x86-64,工作进程模式 | −18.1% | −18.6% |
| arm64,经典模式 | −8.5% | −9.0% |
基于它构建的 Dockerfile 需要做出以下改变:
- 软件包通过
apt-get update && apt-get install -y --no-install-recommends安装,而不是apk,并且使用 Debian 的包名:例如构建intl和pdo_pgsql需要libicu-dev和libpq-dev,运行时需要libicu76和libpq5。 - 在
php:<minor>-zts-trixie阶段中构建扩展。在-zts-alpine阶段中基于 musl 构建的.so与该镜像不兼容,反之亦然。 www-data的 UID/GID 是 33,而不是 82:securityContext.runAsUser: 33,任何按数字 chown 的地方都必须使用 33。- 镜像中没有
wget,但有curl。为 Alpine 镜像编写的健康检查wget --spider http://localhost:9090/health要改为curl -fsS -o /dev/null http://localhost:9090/health。 - 镜像更大:PHP 基础镜像压缩后约 170–180 MiB,而 Alpine 约为 45–50 MiB。
镜像结构
运行时镜像的文件布局:
/usr/local/
├── bin/
│ └── oxphp # server binary
├── lib/
│ ├── libphp.so # PHP ZTS runtime (8.4 or 8.5, matches the image tag)
│ ├── liboxphp_bridge.so # C bridge library
│ └── php/extensions/no-debug-zts-<ABI>/
│ ├── oxphp_sapi.so # OxPHP PHP extension
│ └── sodium.so, ... # from the PHP base image; the set depends on the tag
└── etc/php/
└── conf.d/
├── docker-php-ext-*.ini # from the PHP base image; the set depends on the tag
└── extension.ini # extension=oxphp_sapi.so<ABI> 的值取决于 PHP 的次版本号。 PHP 8.4 使用 20240924,PHP 8.5 使用 20250925。下面的示例固定使用 20250925,是因为它们的 FROM 行以 php:8.5-zts-alpine3.23 为目标,即默认的 :0.13.0 标签所用的 PHP —— 一旦切换 FROM,就必须同时切换该日期。若要在构建过程中可移植地推导出它:
php -r 'echo ini_get("extension_dir");'
# /usr/local/lib/php/extensions/no-debug-zts-20250925在 shell 命令中使用 $(php -r 'echo ini_get("extension_dir");'),以避免硬编码。
镜像本身不带 php.ini(除 conf.d/ 外,/usr/local/etc/php/ 中只有上游的 php.ini-development 和 php.ini-production 示例文件,PHP 两者都不会加载),也不带任何应用设置。请将你自己的设置作为 .ini 文件放到 conf.d 下,通过挂载或复制的方式提供;参见 Docker 指南。
文件名决定了 .ini 是否被读取以及何时被读取。PHP 按字母顺序解析 conf.d 中的每个 .ini 文件,并静默跳过任何其他名称的文件。对于普通设置,最后读取到的值生效,因此用于覆盖某个设置的文件,其名称必须排在设置该值的文件之后。extension= 和 zend_extension= 则不同:它们会从每个文件中收集并全部加载,因此后读取的 extension= 永远不会替换先前的那个。
OxPHP 的三个组件及其用途,大小在 0.11.0 镜像中测得。大小在 linux/amd64 与 linux/arm64 之间有所不同,并随版本变化:
| 组件 | 大小 | 用途 |
|---|---|---|
oxphp |
~12 到 15 MB | HTTP 服务器、路由、插件、指标 |
liboxphp_bridge.so |
~150 到 170 KB | 将服务器链接到 PHP 运行时的共享桥接库 |
oxphp_sapi.so |
~700 KB | PHP 函数(oxphp_request_id()、OxPHP\Http\Request 等) |
依赖链:
graph LR oxphp["oxphp"] --> libphp["libphp.so"] libphp --> deps["libxml2, libcurl, libsqlite3, libonig, ..."] oxphp --> bridge["liboxphp_bridge.so"] sapi["oxphp_sapi.so"] --> bridge
oxphp 二进制文件链接到 libphp.so 和 liboxphp_bridge.so。PHP 扩展 oxphp_sapi.so 也链接到桥接库,从而使每个请求的状态可供你的 PHP 代码使用。
最小 Dockerfile
基础镜像 php:8.5-zts-alpine3.23(或 php:8.4-zts-alpine3.23)已经包含 libphp.so 及其全部依赖。请让 FROM 中的 PHP 次版本号与你所复制的 OxPHP 标签相匹配,C 库也要匹配:来自 -trixie 标签的构件应放到 php:<minor>-zts-trixie 上,而不是 Alpine 基础镜像上。你只需复制三个 OxPHP 构件,并添加 libgcc —— 由 Rust 构建的 oxphp 二进制文件链接了它,而 Alpine PHP 镜像并不附带它:
FROM php:8.5-zts-alpine3.23
RUN apk add --no-cache libgcc
COPY --from=ghcr.io/oxphp/oxphp:0.13.0 /usr/local/bin/oxphp /usr/local/bin/oxphp
COPY --from=ghcr.io/oxphp/oxphp:0.13.0 /usr/local/lib/liboxphp_bridge.so /usr/local/lib/
COPY --from=ghcr.io/oxphp/oxphp:0.13.0 /usr/local/lib/php/extensions/no-debug-zts-20250925/oxphp_sapi.so /usr/local/lib/php/extensions/no-debug-zts-20250925/
RUN echo "extension=oxphp_sapi.so" > /usr/local/etc/php/conf.d/oxphp.ini
COPY --chown=www-data:www-data . /var/www/html/public
EXPOSE 80 443
CMD ["oxphp"]这种方式便于开发:PHP CLI、docker-php-ext-install 和 pecl 都可用。Composer 和 Xdebug 不属于 PHP 基础镜像。请像 Docker 指南 中的 dev 目标那样,通过 COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer 和 pecl install xdebug 自行添加它们。
生产环境 Dockerfile
官方 OxPHP 镜像保留了其所基于的 php:<minor>-zts-alpine3.23 镜像中的 PHP CLI 和扩展工具(php、phpize、pecl、docker-php-ext-install),因此你可以直接在其中安装扩展;参见在生产环境安装 PHP 扩展。改为在单独的阶段中构建扩展,则可以让 icu-dev 之类仅用于构建的软件包不进入最终镜像。你是否需要额外的 PHP 扩展,决定了应采用以下两种构建方式中的哪一种。
如果你的应用需要额外的扩展(pdo_mysql、intl 等),请在单独的构建阶段中编译它们,再复制到最终镜像中:
# Extension build stage
FROM php:8.5-zts-alpine3.23 AS extensions
RUN apk add --no-cache icu-dev postgresql-dev \
&& docker-php-ext-install pdo pdo_mysql pdo_pgsql intl
# Production
FROM ghcr.io/oxphp/oxphp:0.13.0
# Runtime dependencies for extensions
USER root
RUN apk add --no-cache icu-libs libpq
# Copy compiled extensions
COPY --from=extensions /usr/local/lib/php/extensions/no-debug-zts-20250925/*.so /usr/local/lib/php/extensions/no-debug-zts-20250925/
# Enable extensions
RUN { \
echo "extension=pdo.so"; \
echo "extension=pdo_mysql.so"; \
echo "extension=pdo_pgsql.so"; \
echo "extension=intl.so"; \
} > /usr/local/etc/php/conf.d/app-extensions.ini
USER www-data
COPY --chown=www-data:www-data . /var/www/html/public如果你的应用不需要额外的扩展,以下内容就足够了:
FROM ghcr.io/oxphp/oxphp:0.13.0
COPY --chown=www-data:www-data . /var/www/html/public构建并运行:
docker build -t my-app .
docker run -p 80:80 my-app服务器默认监听 80 端口。文档根目录为 /var/www/html/public,上面的代码片段直接将项目复制到其中。对于 Laravel、Symfony 或其他本身就带有 public/ 子目录的框架,请改用 COPY --chown=www-data:www-data . /var/www/html,让框架自带的 public/ 与默认目录对齐。如果你的目录结构差异更大,请用 DOCUMENT_ROOT 环境变量覆盖文档根目录。
源码构建(不含 PHP)
在禁用 PHP 功能的情况下从源码构建 OxPHP,仅提供静态文件服务:
cargo build --release --no-default-features二进制文件位于 target/release/oxphp。它使用一个桩执行器(stub executor),对 PHP 请求返回占位响应,同时正常提供静态文件服务。这种模式适合在没有 PHP 运行时的情况下测试服务器。
源码构建(含 PHP)
构建具备完整 PHP 支持的 OxPHP,需要先编译并安装桥接库和 PHP 扩展。
前置条件
- Rust 工具链(1.91.1 或更高版本)
- 启用了 ZTS(Zend Thread Safety,Zend 线程安全)的 PHP 8.4 或 8.5
- C 编译器(gcc 或 clang)
phpize和 PHP 开发头文件
构建步骤
-
构建并安装桥接库。
cd ext/bridge make && sudo make install -
构建并安装 PHP 扩展。
cd ../ phpize && ./configure --enable-oxphp-sapi && make && sudo make install -
构建 OxPHP。 默认功能包含 php。
cargo build --release
该二进制文件在运行时要求共享库位于库搜索路径中:
export LD_LIBRARY_PATH=/usr/local/lib
./target/release/oxphp在部署到 Alpine Linux 时,请在与 PHP 运行时相同的 php:{8.4,8.5}-zts-alpine 镜像中构建 —— 要与你所交付的 OxPHP 镜像的次版本号相匹配。混用 glibc 和 musl 构建会导致运行时错误。官方 Docker 镜像已正确处理这一点。
验证安装
启动 OxPHP 后,结构化的 JSON 日志输出可确认服务器正在运行:
{"timestamp":"...","level":"INFO","message":"OxPHP HTTP server starting","listen_addr":"0.0.0.0:80",...}
{"timestamp":"...","level":"INFO","message":"Server listening","addr":"0.0.0.0:80"}测试服务器是否响应:
curl http://localhost/如果你通过 INTERNAL_ADDR 启用了内部服务器,请验证健康检查端点:
curl http://localhost:9090/health健康的服务器会返回 200 及 JSON 状态。降级的服务器会返回 503。