版本发布

更新日志

服务器、PHP API 和镜像的每一处重要变更——每个版本精选亮点。完整、未删减的历史记录见 CHANGELOG.md。

最新 v0.11.0 · 2026-08-20 · 11 个版本 · AGPL-3.0 CHANGELOG.md →
oxphp — releases bash
$ docker pull ghcr.io/oxphp/oxphp:0.11.0
0.11.0: Pulling from oxphp/oxphp
Status: Image is up to date
# opt-in PHP 8.5 preview:
$ docker pull ghcr.io/oxphp/oxphp:php8.5
v0.11.0
2026-08-20
LATEST
⚠ breaking changes
Release notes →
BREAKING

FRAME_OPTIONS 默认值改为 SAMEORIGIN — 跨源框架嵌入仍被阻止,同源嵌入(管理后台预览、仪表盘小组件)开箱即用;依赖内置 DENY 的部署现在必须显式设置它。

工作进程模式:请求内不允许事件循环 — 工作进程模式的请求现在是一个纤程,而 Revolt 拒绝在纤程内运行其循环:在处理器里调用 Revolt\EventLoop::run() 会抛出异常。请把它移到工作进程引导阶段;defer()、delay() 和 getSuspension() 照常工作。

工作进程模式:请求状态随请求终结 — ini_set()、set_time_limit() 和 error_reporting() 在请求之间回滚到引导基线;filter_input() 只为发问的请求作答;跨请求保留的 php://input 句柄读起来是已关闭的资源,而不是另一个请求的请求体。

query() 和 cookie() 会解码它们的值 — 曾用 rawurldecode() 补偿的代码现在会解码两次,必须去掉多余的调用。发送为空的 cookie 读作空字符串而不是 null,query() 最多解析 1000 个参数。

严格的 MAX_CONNECTIONS / QUEUE_CAPACITY 解析 — 格式错误的值会中止启动,而不是悄悄回落到默认值;QUEUE_CAPACITY=0 现在表示自动(workers × 128),而不是零容量队列。

ADDED

队列等待预算 — 到达已满队列的请求现在会等待空位,最长 QUEUE_WAIT_TIMEOUT_MS(默认 1 秒),而不是立即 529;到达时盖上的一个截止时间同时覆盖等空位和在队列中的等待。QUEUE_MAX_WAITING 和 QUEUE_MAX_WAITING_BYTES 分别以请求数和请求体字节数约束等待集合。

RUNTIME_HOOKS=streams — 阻塞的 tcp:// 套接字读取和 stream_select() 会挂起纤程而不是工作线程,无需改动代码即可覆盖 mysqlnd(PDO_MySQL、mysqli)、phpredis 和 HTTP 流封装器。并发纤程之间共享的连接按往来交换逐次认领,因此请求不会读到彼此的应答。

oxphp_admission_refused_total — 在到达工作进程之前被卸除的请求现在会留下服务器侧的痕迹,reason 标签把四种过载原因(529)与 shutting_down(503)和 pool_unavailable(500)区分开。

根 span 异常自动捕获 — 以 5xx 应答的未处理异常或致命错误,会在全部四种服务模式下把一个 exception 事件(type、message、stacktrace、file、line)附加到请求的根 span 上——不需要属性标注,也不需要调用 oxphp_apm_error()。

数据库 span 属性 — PDO 和 mysqli 的 span 携带 db.statement(字面值脱敏为 ?)、db.operation、db.system、server.address/port、db.name 和 oxphp.db.slow;mysqli_stmt::prepare 作为第 34 个函数加入被挂钩集合。

CHANGED

oxphp_queue_wait_us 量程达到一秒 — 四个新分桶(100 毫秒到 1 秒)把接近默认预算的等待量化出来,而不是并入 +Inf;被拒绝的请求不计入该直方图。

工作进程回收只统计真正毁坏工作进程的失败 — 只有致命错误、内存耗尽和栈溢出计入连续三次失败的回收;未捕获的异常和被取消的请求是中性的。这个断路器现在在同步派发路径上也会触发——此前它在那里从不生效。

config --check 容量建议 — 当 PHP_WORKERS + QUEUE_CAPACITY + QUEUE_MAX_WAITING 达到 MAX_CONNECTIONS 时发出警告(但不判失败)——越过这一点后,过载的回应将是毫无响应而不是 529。

文档目录扁平化 — 仓库文档从 docs/en/ 上移到 docs/;俄语和中文翻译迁至 oxphp.dev,法语、波兰语和日语已在那里。

FIXED

工作进程模式:$_POST 和 $_FILES 得到填充 — 此前工作进程服务的每个请求里两者都是空的,HTML 表单和文件上传因此悄无声息地不可用;读取 php://input 的 JSON API 不受影响——这也是问题一直未被发现的原因。

filter_input() 跨请求泄漏 — filter 扩展自己的输入副本会在工作进程的整个生命期内累积,用另一个客户端的查询值、会话 cookie 和请求体字段回答别的请求;现在它会在每个请求开始时被交还。

Sleep 定时器突发 — 单次调度器轮询中到期的定时器超过 32 个时,多出来的休眠者会被永远停放;大突发现在按 32 个一批唤醒。

关闭共享连接 — 在另一个请求停着读取的连接上调用 fclose()、$mysqli->close() 或重连助手,会把那个请求恢复到已释放的内存里;现在它会立即以 500 失败,并点名发生了什么。

致命错误不再清空共享状态 — 展开被中止的请求时会双重释放 include 变量,悄悄重置工作进程模式应用存放跨请求状态的数组和对象。

异步池节奏 — 任务交接不再受固定 1 毫秒空闲定时器的节拍限制:Shared\Channel 传输从每项 0.69 毫秒降到 0.14 毫秒,空闲唤醒从 777 次/秒降到 92 次/秒。

暂停的请求保留自己的开始时间 — $request->startTime() 和 oxphp_server_info()[request_time] 在一次挂起之后不再读到 0 或某个并发请求的值。

请求体读取警告到达正确的请求 — post_max_size 和上传限制的警告现在在发送该请求体的请求内部触发,错误处理器因此能看到它的超全局变量;抛出异常的处理器会以 500 应答,而不是什么都不带地关闭连接。

v0.10.0
2026-07-12
⚠ breaking changes
Release notes →
BREAKING

默认降权 — 在存在 www-data 账户的环境下以 root 启动时,服务器会绑定其监听套接字,并在开始处理任何流量之前降权到 www-data;官方镜像不再以 root 运行。传入 --user=root 可保留旧行为。

Fail-closed 的 TLS 配置 — 配置不完整的一对(有 TLS_CERT 却没有 TLS_KEY,或反之)或非 UTF-8 的证书/密钥值,现在会中止启动,而不是悄悄以明文 HTTP 提供服务,并会指明出问题的变量。

严格的 ASYNC_* / SUPERGLOBALS 解析 — ASYNC_WORKERS、ASYNC_QUEUE_CAPACITY 或 ASYNC_MAX_FIBERS 中的无效内容会导致启动错误,而不是悄悄回落到默认值,并且 oxphp run 不再忽略无效的 SUPERGLOBALS_ENABLED。

没有 Content-Type 的 QUERY 返回 400,而非 415 — 唯一由服务器生成的 415 已移除;针对此情况的自定义 ERROR_PAGES_DIR/415.html 必须重命名为 400.html。

性能分析器导出信封的判定 — PROFILER_EXPORT_XHGUI=false 现在即使在 /run/import URL 上也会生效,且信封自动检测仅依据每个工具的规范路径(xhgui /run/import、Buggregator /api/profiler/store),不再依据模糊的 "xhgui" 子串。

ADDED

Buggregator 性能分析器导出 — 性能分析器的 HTTP push 会把 xhprof 数据包装进 Buggregator 的 POST /api/profiler/store 所期望的信封中;PROFILER_EXPORT_BUGGREGATOR 用于开关此功能,tags 和 app_name 通过环境变量设置。

通过 TLS 的 OTLP 追踪导出 — https:// 的 OTEL_EXPORTER_OTLP_ENDPOINT 现在在 gRPC 和 http/protobuf 上都会加密导出,并通过 rustls 对照系统信任库进行校验;运行时镜像必须自带 CA 包(官方镜像已包含)。

APM span 上的完整异常数据 — 异常 span 事件现在除了 exception.type 之外还携带 exception.message 和 exception.stacktrace,因此 New Relic Errors Inbox、Jaeger 和 Tempo 能渲染出消息和堆栈;受 OTEL_APM_MESSAGE_MAX_BYTES / OTEL_APM_STACKTRACE_MAX_BYTES 限制。

长连接的优雅排空 — 收到 SIGTERM 时,服务器会发送 GOAWAY,关闭空闲的 keep-alive 连接,并及时结束打开的 SSE/flush 流;DRAIN_TIMEOUT_SECONDS 默认值从 30 → 25 下调,以适配 Kubernetes 的 30 秒宽限期。

TLS_MIN_VERSION — 可接受的最低协议版本:1.2(默认)或 1.3。其他任何值——包括像 "TLSv1.2" 这样的异类语法——都会导致硬性启动错误,即使 TLS 被禁用也会校验,并在 /config 中以 tls_min_version 上报。

CHANGED

低开销的 STATIC_REVALIDATE — 每个文件最多每 3 秒执行一次 stat();静态命中会以最多一次系统调用从内存提供,磁盘上的改动会在 3 秒内可见。默认仍为关闭。

更清晰的 TLS 启动错误 — 路径拼写错误时会以 "TLS_CERT: cannot read /etc/ssl/cert.pem: No such file or directory" 报错,而不是一条裸的操作系统错误;PEM 解析错误和密钥不匹配错误也会加上同样的前缀。

FIXED

工作进程模式取消定位 — 客户端中止或请求超时可能会取消工作进程上最近接受的那个请求,而不是实际应被取消的请求,从而中止一个不相关的处理中请求。

finish_request() 与多路复用流 — 一旦工作进程上有任何请求调用了 oxphp_finish_request(),多路复用到该工作进程上的其他所有流都会悄悄丢弃其后续的 oxphp_stream_flush() 输出。

v0.9.0
2026-06-25
⚠ breaking changes
Release notes →
BREAKING

超时的 await 会取消任务 — 被放弃的任务会被强制取消:已停放的任务恢复后进入取消流程,CPU 密集型任务会在操作码边界被中断。此前它会一直运行到请求结束。

ADDED

嵌套的 oxphp_async() — 一个异步任务可以派发它自己的任务,并在 await_all / await_any / await_race 上挂起,而不会阻塞其工作线程。“禁止嵌套 async”的限制已移除。

处理中任务上限 — ASYNC_MAX_FIBERS(默认 256)限制排队中加运行中的任务数;超过上限的派发会立即以 AsyncException 拒绝,因此扇出不会死锁。

内部服务器允许列表 — INTERNAL_ALLOW_IPS:用于 /metrics 和 /config 的 CIDR 允许列表;健康检查端点对编排器保持可达。

性能分析器采样排除 — PROFILER_EXCLUDE_PATHS 会把框架自身流量(/_profiler、/_wdt)排除在统计采样之外;显式触发仍会进行性能分析。

FIXED

Channel 发送方唤醒 — 停放在已满 Shared\Channel 上的纤程发送等待者,会在 recv_blocking 腾出一个槽位时被唤醒——sendTimeout 在饱和状态下不再被误触发。

异步边界情况 — 优雅关闭时的崩溃;对已关闭 promise 的 await 卡住而非拒绝;await_all 在提前退出时留下 promise 仍在运行。

v0.8.0
2026-06-13
⚠ breaking changes
Release notes →
BREAKING

工作进程路由约定 — 静态资源从磁盘提供,其余一切都交给工作进程的 ENTRY_FILE——任意 .php 文件、目录索引和根 index.php 回退不再按请求逐一解析。

诚实的 CGI PATH_INFO — 在 Framework 模式下,PATH_INFO 仅在显式的 /index.php/extra 请求时才设置;PHP_SELF 报告前端控制器。请基于 REQUEST_URI 路由。

ADDED

Range 请求(RFC 9110)— 静态文件的 206 Partial Content:视频拖动定位、断点续传下载、PDF 部分加载;配合强校验器支持 If-Range。

HTTP/2 连接限制 — 五个环境变量限制每连接的资源占用;解析后的值会在启动时记录到日志。

CHANGED

强静态 ETag — "<size>-<mtime>" 取代 W/"…"——这是 If-Range 所必需的;压缩表示形式仍保留弱标签。

FIXED

应用头部优先 — 由 PHP 设置的 X-Content-Type-Options、X-Frame-Options 和 CSP 不再被覆盖——服务器提供的值只是在缺失时才插入的回退。

流式传输与压缩 — 用 flush() 流式输出的响应会以未压缩的形式直接透传,而不再被 brotli 层完整缓冲直到脚本结束。

SECURITY

HTTP/2 DoS 加固 — Rapid Reset(CVE-2023-44487)、HPACK 炸弹和窗口停滞攻击都受到限制;死连接通过 PING/PONG keepalive 回收。

v0.7.0
2026-06-05
⚠ breaking changes
Release notes →
BREAKING

访问日志中的 remote_ip — JSON 字段 remote_addr 更名为 remote_ip,且携带不含端口的 IP——与 nginx/Apache/Caddy 保持一致。

ADDED

PHP 风格的 CLI — oxphp run script.php 以 CLI 语义执行一个文件,底层使用完整引擎;#!/usr/bin/env oxphp 脚本可直接运行;接受 -d ini 覆盖。

--user 降权 — 以 root 绑定特权端口,然后在任何请求处理线程创建之前,不可逆地降权到非特权用户。

尊重转发端口 — 在 TRUSTED_PROXIES 之后,SERVER_PORT 会尊重 X-Forwarded-Port;当代理提供时,REMOTE_PORT 会从 RFC 7239 的 for= 填充。

FIXED

装饰器崩溃 — 函数装饰器路径中的两处 SIGSEGV(已释放的实例缓存;跨扩容的悬空 zval)已修复;嵌套溢出现在会抛出 StackOverflowException。

性能分析器属性尊重参数 — #[SlowThreshold(ms:)]、#[MemoryThreshold(kb:)] 和 #[Mark(label:)] 不再退化为共享的默认值;APM span 事件能够抵达 OpenTelemetry。

上传对象 API 已接通 — Request::file() / files() 现在会为每种 $_FILES 形态返回 UploadedFile 对象,而不是 null。

v0.6.0
2026-05-28
⚠ breaking changes
Release notes →
BREAKING

三分式等待 API — Channel 和 Mutex 把每个等待都拆分为 try* / 裸动词 / *Timeout(int $ms),返回值类型化的 SendResult / RecvResult,而不是 mixed|null。

await_any 即 Promise.any — 第一个兑现者胜出;全部拒绝则抛出 AggregateAsyncException。旧的“第一个敲定”的行为现在是 oxphp_async_await_race()。

诚实的取消状态码 — 超时 → 504,优雅排空 → 503 + Retry-After,客户端断开 → 499(仅记录日志)。监督进程杀死和用户态取消仍为 500。

没有兼容垫片的重命名 — Async\Exception → AsyncException,Shared\Exception → SharedException;Worker 去掉 get* 前缀;Counter 变得精简(add/set);Flag 与 Atomic 对齐;Once 新增 getOrInit()/status()。

ADDED

Shared\Atomic + Ordering — 一个通用的 int64 原子类型:load/store/swap/CAS/fetch*,带有显式的内存排序,与 std::sync::atomic 一一对应。

Shared\Registry — 以名称为键的进程全局句柄:每个工作进程都收敛到同一个条目;工厂最多运行一次。

REMOVED

单一超时来源 — REQUEST_TIMEOUT_SECONDS 和 oxphp_request_heartbeat() 已移除——max_execution_time 和 set_time_limit() 保留。

FIXED

SSE 中的 connection_aborted() — 在流中途断开后返回 true,因此 while (!connection_aborted()) 循环能干净地退出。

PERFORMANCE

Shared\* 热路径 — 每次操作的开销落在原始原子加载的 criterion 噪声范围之内;在 8 线程下相比此前的形态几何平均约提升 4.7×。

v0.5.0
2026-05-05
⚠ breaking changes
Release notes →
BREAKING

严格的布尔环境变量解析 — 只接受 on/true/1/yes 和 off/false/0/no;像 ture 这样的拼写错误会拒绝启动,而不是悄悄回落到默认值。

ADDED

ENTRY_FILE + WORKER_MODE_ENABLED — 一个变量按扩展名选择路由模式;一个显式开关启用持久化工作进程。INDEX_FILE / WORKER_FILE 成为已弃用的别名。

OxPHP\Server\Worker 类 — 一个运行时自省句柄:id、请求计数、内存、RSS——外加通过 scheduleExit() 实现的应用驱动回收。

CHANGED

静态缓存重命名 — STATIC_CACHE_TTL → STATIC_MAX_AGE;STATIC_CACHE → STATIC_REVALIDATE,极性翻转。默认值不变。

DEPRECATED

WORKER_MAX_REQUESTS — 会被解析并忽略,同时在启动时发出 WARN——请迁移到 WORKER_MAX_MEMORY_MIB 或 scheduleExit()。

v0.4.0
2026-05-02
Release notes →
ADDED

PHP 8.5 镜像 — :php8.5、:php8.5-alpine 和固定补丁版本的标签与 8.4 一同发布;latest 会保持在 8.4,直到观察期结束。

FIXED

跨请求的分块流式传输 — 桥接层的每请求上下文在多个工作进程请求之间发生泄漏,导致首次 flush 之后丢失分块的 Transfer-Encoding。

用户纤程内的 Shared\* — Fiber::start() 内部的 Channel::recv()/send() 不再走 oxphp-fiber 挂起路径并以 fiber_await rc=1 崩溃。

插件 API 的具名参数 — 插件定义的类和函数现在会公布真实的参数名——具名参数调用可以正常工作。

异步闭包中的 Shared 句柄 — 被 oxphp_async() 闭包捕获的 Channel/Map/Pool 不再在接收线程上以 null 到达。

v0.3.0
2026-04-22
⚠ breaking changes
Release notes →
BREAKING

Async 命名空间 — 异步类已移动到 OxPHP\Async\ 之下;这些函数现在随 plugin-async 一起发布。

ADDED

七种 Shared\* 原语 — Counter、Flag、Once、Mutex、Channel、Map、Pool——跨工作进程共享的原子状态,带有注册表可观测性和死锁检测器。

按请求的性能分析器 — xhprof / speedscope / pprof / collapsed 输出;cookie、header、query 和统计触发;带 xhgui 信封的 HTTP push。

APM 自动埋点 — plugin-apm 提供 PHP 追踪 SDK 和错误捕获;异步和 APM 子系统已迁移到 Rust 插件中。

生产集成 — TRUSTED_PROXIES(RFC 7239)、Kubernetes /readyz + /livez、安全头部、按 IP 限流——全部通过环境变量配置。

PERFORMANCE

Pool 热路径 — 在 Docker 中无竞争的获取/释放约 0.9 µs,带有每线程亲和性。

v0.2.0
2026-03-27
Release notes →
ADDED

纤程多路复用 — 单个工作线程内的并发 I/O;使用 W3C Trace Context 的分布式追踪和 OpenTelemetry 导出。

oxphp_async() promise — 在专用线程池上并行执行 PHP。

HTTP 对象 API — 带惰性访问器的 OxPHP\Http\Request,外加基于属性的装饰器系统。

CHANGED

端口与背压 — 默认端口 8080 → 80(启用 TLS 时为 443);背压响应 503 → 529。

FIXED

CGI 正确性 — SERVER_PROTOCOL 反映真实的 HTTP 版本;REQUEST_TIME 为请求开始时间;IPv6 Host 解析;请求超时返回 408。

v0.1.0
2026-03-08
Release notes →
ADDED

异步 HTTP 核心 — 一个支持优雅关闭的 Hyper + Tokio 服务器,一个带完整超全局变量的自定义 oxphp SAPI,一个带有界队列和 529 背压的 ZTS 工作进程池。

三种路由模式 — Traditional、Framework 和 SPA;静态文件带内存缓存、ETag/304、brotli、TLS。

工作进程模式 — 通过 oxphp_worker() 实现持久化工作进程、提前响应完成、SSE 流式传输、协作式超时。

可观测性 — /metrics、/health、/config、结构化 JSON 访问日志、请求 ID。

Docker 优先 — 多阶段 Alpine 构建;GHCR 上的 amd64/arm64 镜像。