背景
PR #257 (issue #256 / #258 — response 側の malformed-schema ガード)の multi-agent レビュー(/pr-review-toolkit:review-pr)で silent-failure-hunter が指摘。#256 /#258 以前から存在する既存ギャップ で、#256 (content/schema 階層)・#258 (responses[$status] 階層)が per-response の malformed ガードに限定されているのに対し、本 issue は spec 走査経路(paths / path item / operation / responses マップ)の堅牢化という別の関心事のためスコープ外として切り出し。
現状
OpenApiResponseValidator::validate() は spec を段階的に走査するが、各段階で構造ノードが非配列スカラーのケースをガードしていない。?? [] は キー不在 には効くが、スカラー値が存在する 場合は素通しする。
// src/OpenApiResponseValidator.php
$ specPaths = array_keys ($ spec ['paths ' ] ?? []); // (2) line 112
$ pathSpec = $ spec ['paths ' ][$ matchedPath ] ?? []; // (3) line 123
if (!isset ($ pathSpec [$ lowerMethod ])) { . .. } // (3)/(4) line 125
$ responses = $ pathSpec [$ lowerMethod ]['responses ' ] ?? []; // (1)/(4) line 132
$ matchedResponseKey = SpecResponseKeyResolver::resolve ($ statusCodeStr , $ responses ); // (1) line 166
問題
malformed spec(YAML/JSON が想定外のスカラーに decode される、未解決 $ref 等)で、以下が 未捕捉 TypeError になる。TypeError extends Error(RuntimeException ではない)ため validator のエラーハンドリングを素通りし、読みやすい spec エラーではなくスタックトレースのみのクラッシュになる:
非配列 responses マップ自体 (例: responses: "scalar")— $responses がスカラーのまま SpecResponseKeyResolver::resolve(string, array $responses) / warnSuspiciousKeys(..., array $responses) の array 型ヒントに渡り TypeError。tech-debt(validation) — OpenApiResponseValidator が非配列の responses[$status] エントリで未捕捉 TypeError になる #258 で塞いだ responses[$status] の直上階層。
非配列 paths (例: paths: "scalar")— array_keys($spec['paths'] ?? []) でスカラーが ?? を素通り → array_keys(scalar) で TypeError。
非配列 path item (paths["/x"] がスカラー)— $pathSpec がスカラー → isset($pathSpec[$lowerMethod]) でスカラーへの非整数オフセットアクセス → TypeError。
非配列 operation (paths["/x"]["get"] がスカラー)— $pathSpec[$lowerMethod]['responses'] でスカラーへのオフセットアクセス → TypeError。
contract-testing ツールの「malformed spec は隠さず loud に surface する」原則に反する。#256 /#258 が per-response 階層で確立した不変条件を、その上位の走査経路にも広げる必要がある。
やること
ゴール
malformed spec の構造ノードが非配列でも TypeError でクラッシュせず、RequestBodyValidator と同じ粒度の loud な spec エラーとして surface する
「malformed spec から未捕捉 TypeError を出さない」保証が response validator の走査経路全体で成立する
関連
背景
PR #257(issue #256 / #258 — response 側の malformed-schema ガード)の multi-agent レビュー(
/pr-review-toolkit:review-pr)で silent-failure-hunter が指摘。#256/#258 以前から存在する既存ギャップで、#256(content/schema階層)・#258(responses[$status]階層)が per-response の malformed ガードに限定されているのに対し、本 issue は spec 走査経路(paths/ path item / operation /responsesマップ)の堅牢化という別の関心事のためスコープ外として切り出し。現状
OpenApiResponseValidator::validate()は spec を段階的に走査するが、各段階で構造ノードが非配列スカラーのケースをガードしていない。?? []は キー不在 には効くが、スカラー値が存在する 場合は素通しする。問題
malformed spec(YAML/JSON が想定外のスカラーに decode される、未解決
$ref等)で、以下が 未捕捉TypeErrorになる。TypeError extends Error(RuntimeExceptionではない)ため validator のエラーハンドリングを素通りし、読みやすい spec エラーではなくスタックトレースのみのクラッシュになる:responsesマップ自体(例:responses: "scalar")—$responsesがスカラーのままSpecResponseKeyResolver::resolve(string, array $responses)/warnSuspiciousKeys(..., array $responses)のarray型ヒントに渡り TypeError。tech-debt(validation) — OpenApiResponseValidator が非配列の responses[$status] エントリで未捕捉 TypeError になる #258 で塞いだresponses[$status]の直上階層。paths(例:paths: "scalar")—array_keys($spec['paths'] ?? [])でスカラーが??を素通り →array_keys(scalar)で TypeError。paths["/x"]がスカラー)—$pathSpecがスカラー →isset($pathSpec[$lowerMethod])でスカラーへの非整数オフセットアクセス → TypeError。paths["/x"]["get"]がスカラー)—$pathSpec[$lowerMethod]['responses']でスカラーへのオフセットアクセス → TypeError。contract-testing ツールの「malformed spec は隠さず loud に surface する」原則に反する。#256/#258 が per-response 階層で確立した不変条件を、その上位の走査経路にも広げる必要がある。
やること
OpenApiResponseValidator::validate()の各 traversal 段階でis_array()ガードを追加し、非配列ならMalformed 'paths'/Malformed 'paths["/x"]'/Malformed 'paths["/x"].get'/Malformed 'paths["/x"].get.responses'形式の loud な spec エラーをOpenApiValidationResult::failure()で返すRequestBodyValidatorおよび tech-debt(validation) — ResponseBodyValidator に malformed-schema ガードが無く、非配列の schema が診断なく素通り / TypeError になる #256/tech-debt(validation) — OpenApiResponseValidator が非配列の responses[$status] エントリで未捕捉 TypeError になる #258 の malformed ガード(expected object, got scalar文言)と対称TypeErrorではなくisValid() === false+ spec エラーが返ることを検証する失敗テストを先に追加OpenApiRequestValidator)の traversal 経路にも必要か併せて確認するゴール
TypeErrorでクラッシュせず、RequestBodyValidatorと同じ粒度の loud な spec エラーとして surface する関連
ResponseBodyValidatorの非配列content/schemaガードresponses[$status]エントリのガード(本 issue の直下階層)src/Validation/Request/RequestBodyValidator.phpの malformed ガード(対称実装の参照元)