変更履歴
サーバー、PHP API、イメージに関するすべての注目すべき変更を、リリースごとに厳選してご紹介します。省略のない完全な履歴は CHANGELOG.md にあります。
ルートスパンでの未処理例外の自動キャプチャ — キャッチされない例外や致命的エラーで失敗して 5xx を返すリクエストは、#[OxPHP\Apm\Trace] 属性も oxphp_apm_error() の呼び出しもなしに、OpenTelemetry の例外イベント(type、message、stacktrace、file、line)をルートの SERVER スパンに付加するようになりました。これにより 500 はエラーバックエンド上で自己記述的になります。独自のエラーページを描画するフレームワークアプリケーションは、従来どおり明示的にこれを記録します。
FRAME_OPTIONS のデフォルトが DENY ではなく SAMEORIGIN になりました — 同一オリジンのフレーム表示(管理画面のプレビュー、ダッシュボードのウィジェット、同一オリジンの決済コンポーネント)がそのまま動作する一方で、クロスオリジンのクリックジャッキングは引き続きブロックされます。これは nginx や Rails と同じ挙動です。すべてのフレーム表示を禁止するには FRAME_OPTIONS=DENY を設定してください。
デフォルトでの権限降格 — www-data アカウントが存在する環境で root として起動された場合、サーバーはリスナーをバインドしたあと、トラフィックを処理する前に www-data へ降格します。公式イメージはもはや root では動作しません。以前の挙動を維持するには --user=root を渡してください。
フェイルクローズな TLS 設定 — 中途半端に設定されたペア(TLS_KEY のない TLS_CERT、またはその逆)や UTF-8 でない証明書・鍵の値は、平文の HTTP を黙って提供する代わりに、問題のある変数名を示して起動を中止するようになりました。
ASYNC_* / SUPERGLOBALS の厳格なパース — ASYNC_WORKERS、ASYNC_QUEUE_CAPACITY、ASYNC_MAX_FIBERS に不正な値が入っている場合、黙ってデフォルトにフォールバックする代わりに起動エラーになります。また oxphp run は不正な SUPERGLOBALS_ENABLED を無視しなくなりました。
Content-Type のない QUERY は 415 ではなく 400 を返します — サーバーが生成する唯一の 415 はなくなりました。このケース向けにカスタマイズした ERROR_PAGES_DIR/415.html は 400.html にリネームする必要があります。
プロファイラーのエクスポートエンベロープの解決 — PROFILER_EXPORT_XHGUI=false は /run/import URL に対しても優先されるようになり、エンベロープの自動検出は各ツールの正規パス(xhgui は /run/import、Buggregator は /api/profiler/store)のみをキーとするようになりました。あいまいな "xhgui" 部分文字列はもう使いません。
Buggregator へのプロファイラーエクスポート — プロファイラーの HTTP プッシュは、Buggregator の POST /api/profiler/store が期待するエンベロープで xhprof データをラップします。PROFILER_EXPORT_BUGGREGATOR で切り替えられ、tags と app_name は環境変数で設定します。
TLS 経由の OTLP トレースエクスポート — https:// の OTEL_EXPORTER_OTLP_ENDPOINT は、gRPC と http/protobuf の両方で暗号化してエクスポートされ、rustls を介してシステムのトラストストアに対して検証されるようになりました。ランタイムイメージは CA バンドルを同梱している必要があります(公式イメージは同梱しています)。
APM スパンでの完全な例外データ — 例外スパンイベントは 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 のデフォルトは、Kubernetes の 30 秒の猶予期間に収まるよう 30 → 25 に引き下げられました。
TLS_MIN_VERSION — 受け入れる最小のプロトコルバージョン。1.2(デフォルト)または 1.3 です。それ以外の値は — "TLSv1.2" のような異なる構文も含めて — 致命的な起動エラーになります。これは TLS が無効な場合でも検証され、/config では tls_min_version として報告されます。
低コストな STATIC_REVALIDATE — ファイルごとに stat() は最大でも 3 秒に 1 回だけ実行されます。静的ファイルのヒットは最大でも 1 回のシステムコールでメモリから提供され、ディスク上の変更は 3 秒以内に反映されます。デフォルトは無効のままです。
より明確な TLS 起動エラー — タイプミスのあるパスは、素の OS エラーではなく "TLS_CERT: cannot read /etc/ssl/cert.pem: No such file or directory" というエラーで失敗するようになりました。PEM のパースエラーや鍵の不一致エラーも同じ形式のプレフィックスが付きます。
ワーカーモードでのキャンセル対象の特定 — クライアントの中断やリクエストタイムアウトが、実際にキャンセルされるべきリクエストではなく、ワーカーで最後に受け付けられたリクエストをキャンセルしてしまい、無関係な処理中のリクエストを中断させることがありました。
finish_request() と多重化されたストリーム — ワーカー上のいずれかのリクエストが oxphp_finish_request() を呼び出すと、そのワーカーに多重化されている他のすべてのストリームが、以降の oxphp_stream_flush() の出力を黙って破棄していました。
タイムアウトした await がタスクをキャンセルします — 放棄されたタスクは強制的にキャンセルされます。パークされたタスクはキャンセル処理に入って再開し、CPU バウンドなタスクはオペコードの境界で中断されます。以前はリクエストの終了まで実行され続けていました。
ネストした oxphp_async() — 非同期タスクは自身のタスクをディスパッチし、ワーカースレッドをブロックすることなく await_all / await_any / await_race で中断できます。「ネストした非同期は不可」という制限はなくなりました。
処理中タスクの上限 — ASYNC_MAX_FIBERS(デフォルト 256)はキュー内および実行中のタスク数を制限します。上限を超えたディスパッチは AsyncException で即座に拒否されるため、ファンアウトがデッドロックすることはありません。
内部サーバーの許可リスト — INTERNAL_ALLOW_IPS は /metrics と /config 向けの CIDR 許可リストです。ヘルスエンドポイントはオーケストレーターから引き続きアクセス可能です。
プロファイラーのサンプリング除外 — PROFILER_EXCLUDE_PATHS はフレームワークの自己トラフィック(/_profiler、/_wdt)を統計的サンプリングから除外します。明示的なトリガーは引き続きプロファイリングを行います。
チャネル送信側のウェイクアップ — 満杯の Shared\Channel でパークされたファイバーの送信待機は、recv_blocking がスロットを解放すると起床するようになりました。これにより sendTimeout は飽和状態で誤って発火しなくなりました。
非同期処理のエッジケース — グレースフルシャットダウン時のクラッシュ、クローズ済みの Promise に対する await が reject されずに停止する問題、await_all が早期に中断した際に Promise を実行したまま放置する問題を修正しました。
ワーカーのルーティング契約 — 静的アセットはディスクから、それ以外はすべてワーカーの ENTRY_FILE へ送られます。任意の .php ファイル、ディレクトリインデックス、ルートの index.php フォールバックは、リクエストごとに解決されなくなりました。
正直な CGI PATH_INFO — Framework モードでは、PATH_INFO は明示的な /index.php/extra リクエストに対してのみ設定されます。PHP_SELF はフロントコントローラーを報告します。ルーティングは REQUEST_URI で行ってください。
Range リクエスト(RFC 9110) — 静的ファイルに対する 206 Partial Content により、動画のシーク、再開可能なダウンロード、PDF の部分読み込みが可能になります。If-Range は強い検証子とともに尊重されます。
HTTP/2 の接続制限 — 5 つの環境変数が接続ごとのリソース使用量を制限します。解決された値は起動時にログ出力されます。
静的ファイルの強い ETag — "<size>-<mtime>" が W/"…" を置き換えます。これは If-Range に必要です。圧縮された表現は弱いタグを維持します。
アプリケーションのヘッダーが優先されます — PHP から設定された X-Content-Type-Options、X-Frame-Options、CSP は上書きされなくなりました。サーバーの値は、存在しない場合にのみ挿入されるフォールバックです。
ストリーミングと圧縮 — flush() でストリーミングされるレスポンスは、スクリプト終了まで brotli レイヤーで完全にバッファリングされる代わりに、非圧縮のまま通過するようになりました。
HTTP/2 の DoS 対策の強化 — Rapid Reset(CVE-2023-44487)、HPACK ボム、ウィンドウストール攻撃が制限されます。死んだ接続は PING/PONG キープアライブによって回収されます。
アクセスログの remote_ip — JSON フィールドの remote_addr は remote_ip にリネームされ、ポートなしの IP を保持します。これは nginx/Apache/Caddy と同じです。
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= から埋められます。
デコレーターのクラッシュ — 関数デコレーターの経路にあった 2 つの SIGSEGV(解放済みのインスタンスキャッシュ、リサイズをまたぐダングリング zval)はなくなりました。ネストのオーバーフローは StackOverflowException をスローするようになりました。
プロファイラー属性が引数を尊重します — #[SlowThreshold(ms:)]、#[MemoryThreshold(kb:)]、#[Mark(label:)] は、共有のデフォルト値に潰れることがなくなりました。APM のスパンイベントは OpenTelemetry に届きます。
アップロードオブジェクト API の接続 — Request::file() / files() は、あらゆる $_FILES の形状に対して null ではなく UploadedFile オブジェクトを返すようになりました。
三分岐の待機 API — Channel と Mutex はすべての待機を try* / 素の動詞 / *Timeout(int $ms) に分割し、mixed|null ではなく値型の SendResult / RecvResult を返します。
await_any は Promise.any を意味します — 最初に fulfilled になったものが優先されます。すべてが rejected の場合は AggregateAsyncException をスローします。以前の「最初に settled したもの」という挙動は、現在は oxphp_async_await_race() です。
正直なキャンセルステータス — タイムアウトは 504、グレースフルなドレインは 503 + Retry-After、クライアントの切断は 499(ログのみ)になります。スーパーバイザーによる kill とユーザーランドのキャンセルは 500 のままです。
シムなしのリネーム — Async\Exception → AsyncException、Shared\Exception → SharedException。Worker は get* プレフィックスを廃止し、Counter は最小限(add/set)になり、Flag は Atomic を反映し、Once は getOrInit()/status() を獲得します。
Shared\Atomic + Ordering — 汎用の int64 アトミックです。明示的なメモリオーダリングを伴う load/store/swap/CAS/fetch* を備え、std::sync::atomic と一対一で対応します。
Shared\Registry — 名前をキーとするプロセスグローバルなハンドルです。すべてのワーカーが同じエントリに収束し、ファクトリは最大でも 1 回だけ実行されます。
タイムアウトの発生源を一本化 — REQUEST_TIMEOUT_SECONDS と oxphp_request_heartbeat() はなくなりました。max_execution_time と set_time_limit() は残ります。
SSE における connection_aborted() — ストリーム途中での切断後に true を返すようになり、while (!connection_aborted()) のループがきれいに終了します。
Shared\* のホットパス — 操作ごとのオーバーヘッドは、素のアトミックロードに対する criterion のノイズの範囲に収まります。8 スレッドで以前の形状と比べて幾何平均で約 4.7 倍です。
厳格なブール環境変数のパース — 受け付けるのは on/true/1/yes と off/false/0/no のみです。ture のようなタイプミスは、黙ってデフォルトにフォールバックする代わりに起動を拒否します。
ENTRY_FILE + WORKER_MODE_ENABLED — 1 つの変数が拡張子によってルーティングモードを選択し、明示的なトグルが永続ワーカーを有効化します。INDEX_FILE / WORKER_FILE は非推奨のエイリアスになります。
OxPHP\Server\Worker クラス — ランタイムのイントロスペクションハンドルです。id、リクエスト数、メモリ、RSS を提供し、さらに scheduleExit() によるアプリケーション主導のリサイクルも可能です。
静的キャッシュのリネーム — STATIC_CACHE_TTL → STATIC_MAX_AGE、STATIC_CACHE → STATIC_REVALIDATE(極性は反転)。デフォルトは変更ありません。
WORKER_MAX_REQUESTS — パースはされますが、起動時の WARN とともに無視されます。WORKER_MAX_MEMORY_MIB または scheduleExit() に移行してください。
PHP 8.5 イメージ — :php8.5、:php8.5-alpine、パッチ固定のタグが 8.4 と並んで提供されます。latest はソーク期間が終わるまで 8.4 のままです。
リクエストをまたぐチャンクドストリーミング — ブリッジのリクエストごとのコンテキストがワーカーのリクエスト間でリークし、最初の flush 以降にチャンクドの Transfer-Encoding が失われていました。
ユーザーファイバー内の Shared\* — Fiber::start() 内の Channel::recv()/send() は、oxphp-fiber の中断経路を通って fiber_await rc=1 でクラッシュすることがなくなりました。
プラグイン API の名前付き引数 — プラグインが定義するクラスや関数が、実際のパラメータ名を公開するようになりました。名前付き引数の呼び出しが機能します。
非同期クロージャ内の共有ハンドル — oxphp_async() クロージャがキャプチャした Channel/Map/Pool は、受信側スレッドで null として届くことがなくなりました。
Async 名前空間 — 非同期クラスは OxPHP\Async\ の下に移動しました。関数群は現在 plugin-async で提供されます。
7 つの Shared\* プリミティブ — Counter、Flag、Once、Mutex、Channel、Map、Pool。ワーカー間で共有されるアトミックな状態を提供し、レジストリによる可観測性とデッドロック検出器を備えます。
リクエストごとのプロファイラー — xhprof / speedscope / pprof / collapsed 形式の出力、cookie・header・query・統計的トリガー、xhgui エンベロープでの HTTP プッシュを提供します。
APM の自動インストルメンテーション — plugin-apm は PHP のトレーシング SDK とエラーキャプチャを備えます。非同期および APM のサブシステムは Rust プラグインへ移行しました。
本番環境向けの統合 — TRUSTED_PROXIES(RFC 7239)、Kubernetes の /readyz + /livez、セキュリティヘッダー、IP ごとのレート制限。すべて環境変数で設定します。
Pool のホットパス — 競合のない acquire/release は Docker 内で約 0.9 µs、スレッドごとのアフィニティを備えます。
ファイバー多重化 — 単一のワーカースレッド内での並行 I/O、W3C Trace Context による分散トレーシングと OpenTelemetry エクスポートを提供します。
oxphp_async() の Promise — 専用のスレッドプール上での PHP の並列実行を提供します。
HTTP オブジェクト API — 遅延アクセサを備えた OxPHP\Http\Request と、属性ベースのデコレーターシステムを提供します。
ポートとバックプレッシャー — デフォルトポートは 8080 → 80(TLS 有効時は 443)、バックプレッシャーのレスポンスは 503 → 529 になりました。
CGI の正確性 — SERVER_PROTOCOL が実際の HTTP バージョンを反映し、REQUEST_TIME はリクエスト開始時刻になり、IPv6 の Host パース、リクエストタイムアウト時の 408 に対応しました。
非同期 HTTP コア — グレースフルシャットダウンを備えた Hyper + Tokio サーバー、完全なスーパーグローバルを備えたカスタム oxphp SAPI、上限付きキューと 529 バックプレッシャーを備えた ZTS ワーカープールを提供します。
3 つのルーティングモード — Traditional、Framework、SPA。インメモリキャッシュ、ETag/304、brotli、TLS を備えた静的ファイルを提供します。
ワーカーモード — oxphp_worker() による永続ワーカー、早期レスポンスの完了、SSE ストリーミング、協調的タイムアウトを提供します。
可観測性 — /metrics、/health、/config、構造化された JSON アクセスログ、リクエストID を提供します。
Docker ファースト — マルチステージの Alpine ビルド、GHCR 上の amd64/arm64 イメージを提供します。