From 0bb474579b139695aca48d8dd2c379dd7eb27353 Mon Sep 17 00:00:00 2001 From: "v.razuvaev" Date: Sun, 6 Sep 2026 19:49:13 +0300 Subject: [PATCH] Write down three divergences instead of changing behaviour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All three come from the full-cycle review, and none of them changes a verdict. The one worth the paragraph is the delimiter: both specifications say `|` and space MUST be percent-encoded inside a value, so `?ids=a%7Cb` is one element containing a pipe. This package folds the encoded form into the delimiter and reads two. Fixing that would buy agreement with the text at the cost of agreement with every PHP application that decodes and then splits — and agreeing with the application is what a validator is for. `deepObject` is not in the same position: there, PHP folds the brackets too. --- CHANGELOG.md | 9 +++++++++ README.md | 11 +++++++++++ README.ru.md | 11 +++++++++++ llms.txt | 8 ++++++++ 4 files changed, 39 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca76228..a46121e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- **Documentation.** Three divergences from the specification are now written + down rather than merely true: a percent-encoded delimiter inside a + `pipeDelimited`/`spaceDelimited` value is folded into the delimiter, a Header + Object carrying `name`/`in` is ignored rather than refused, and `example` + with `examples` are both kept instead of being refused as mutually exclusive. + No behaviour changed: the first agrees with how a PHP application reads the + same query, and changing it would break that agreement to gain agreement with + the text. + - **Fixed.** A YAML document under a kilobyte could exhaust memory and die with a fatal error rather than an `InvalidContract`. Anchors and aliases expand inside the parser, before any budget measures anything, and the byte budget diff --git a/README.md b/README.md index 2a99e17..c324952 100644 --- a/README.md +++ b/README.md @@ -369,6 +369,17 @@ REQUIRED `description`, or an `encoding` declared on a media type the specification does not apply it to — accepted, because neither changes a verdict. Reach for a linter for the rest. +Three divergences from the specification are deliberate and pinned: + +| Where | The specification | This package | +|---|---|---| +| A percent-encoded delimiter inside a `pipeDelimited` or `spaceDelimited` value | `\|` and space MUST be percent-encoded inside a value, so `?ids=a%7Cb` is the single element `a\|b` | folds the encoded form into the delimiter and reads two elements — which is what a PHP application reading the same query does, and agreeing with the application is the point of a validator. A value containing the delimiter cannot be expressed | +| A Header Object carrying `name` or `in` | both MUST NOT be specified | ignored, not refused: the header's name comes from the map key either way | +| `example` and `examples` on the same object | mutually exclusive | both are kept as annotations and handed to consumers; the validator reads neither | + +`deepObject` is not among them: `f%5Ba%5D=1` and `f[a]=1` are the same +parameter here and in PHP's own query parsing. + ## Security **Declared `security` is not enforced.** Requirements are compiled, and a diff --git a/README.ru.md b/README.ru.md index 04078ed..f016fb5 100644 --- a/README.ru.md +++ b/README.ru.md @@ -375,6 +375,17 @@ instance path равен `$`; параметр печатается, но каж применяет, — принимаются, потому что вердикт от них не зависит. Для остального есть линтер. +Три расхождения со спекой сделаны осознанно и зафиксированы: + +| Где | Спека | Пакет | +|---|---|---| +| Percent-encoded разделитель внутри значения `pipeDelimited`/`spaceDelimited` | `\|` и пробел внутри значения MUST быть percent-encoded, поэтому `?ids=a%7Cb` — один элемент `a\|b` | складывает закодированную форму в разделитель и читает два элемента — ровно так же читает PHP-приложение, разбирающее тот же query, а согласие с приложением и есть смысл валидатора. Значение с разделителем внутри выразить нельзя | +| Header Object с `name` или `in` | оба MUST NOT be specified | игнорируются, а не отвергаются: имя заголовка в любом случае берётся из ключа карты | +| `example` и `examples` на одном объекте | взаимоисключающие | оба сохраняются как аннотации и отдаются потребителю; валидация не читает ни одно | + +`deepObject` в этот список не входит: `f%5Ba%5D=1` и `f[a]=1` — один и тот же +параметр и здесь, и в собственном разборе query у PHP. + ## Безопасность **Объявленный `security` не проверяется.** Требования компилируются, и diff --git a/llms.txt b/llms.txt index 2be7fff..e96664b 100644 --- a/llms.txt +++ b/llms.txt @@ -103,6 +103,14 @@ Rules: - `format: date-time` (and the other supported formats) are asserted, not treated as annotations — a pinned divergence from league/openapi-psr7-validator; +- three pinned divergences from the specification: a percent-encoded `|` or + space inside a `pipeDelimited`/`spaceDelimited` value is folded into the + delimiter (as PHP's own query parsing does, so `?ids=a%7Cb` is two elements + here and one per the spec — a value containing the delimiter cannot be + expressed); a Header Object carrying `name`/`in` is ignored rather than + refused; `example` and `examples` together are both kept instead of being + refused as mutually exclusive. `deepObject` is NOT one of them — + `f%5Ba%5D=1` and `f[a]=1` agree with PHP; - multi-file resolution is differentially tested against cebe/php-openapi (dev-only oracle, OAS 3.0): pinned divergences are the depth budget on deep `$ref` chains and fast rejection of cross-file cycles.