Repository navigation
v0.9.0
-
Fixed.
spaceDelimitedandpipeDelimitedquery parameters generate
again (#58). The serializer refuses a value carrying the style's own
separator — the style has no escape for it — but nothing constrained the
generated strings, andGen::stringOf()draws from printable ASCII, which
contains both a space and a|. Four materializations in five aborted with
an error that cannot shrink and never reaches the corpus. The alphabet is
narrowed at compile time instead, an unusableenummember is dropped and a
constfails closed; the run-time filter now only guards apattern, whose
alphabet neither can see. Measured over 200 runs, the abort rate goes from
163/200 and 144/200 to 0. -
Fixed.
allowReservedno longer changes what the wire says (#59). The
reserved set is style-aware: a character the chosen style uses as a separator
stays percent-encoded, so a form list carrying a comma is no longer read back
as two items. Across every style, explode and value shape — 212 combinations
serialized here and parsed byopenapi-contract— the round trip went from
12 broken to none. The flag is also applied where the specification defines
it,in: query, rather than everywhere but the path. -
Fixed. A parameter whose type is an OAS 3.1 union containing
null
generates (#62). A parameter travels as text and has no representation for
the null branch, which is why 3.0nullableis dropped; the 3.1 spelling was
not, so a free-formtype: ["object", "null"]reached the generator and the
wire conversion then rejected the map it produced.Internal\SchemaShape
reads a type union as membership rather than identity, which is what it
exists to decide in one place. -
Changed. Requires
rasuvaeff/openapi-contract^0.6, which fixes the
contract-side halves of this wave — among them OAS 3.0 documents using a
booleanadditionalProperties, and properties that were silently not
validated at all. -
Internal. The zoo gains
delimited.get,reserved.getandunions.get,
andZooContracts::suite()derives its selection fromVALID_OPERATIONS
instead of repeating it — an operation added to one list and not the other
was selected nowhere, and the property that exercises it aborted before it
could say why. -
Fixed. A required multipart array property is generated non-empty (#60).
An empty array became zero parts, and a multipart entity with no parts is not
one — RFC 2046 §5.1.1 requires at least one, andopenapi-contractrejects
the payload withrequest.body.decode, as does
league/openapi-psr7-validator. About 9% of runs therefore broke the
invariant this package states about itself: a materialized valid case must
passContract::validateRequest(). Form bodies already forced a required
container non-empty; multipart does now too. Measured over 80 runs, empty
bodies go from 7 to 0. -
Fixed. Every supported request body media type is generated, not only the
first one recognised (#66). A body declaringapplication/jsonbeside
application/x-www-form-urlencodedwas always exercised through one of them,
so the other was declared and never sent. -
Internal. The zoo gains
uploads.createanddual.create, and the
end-to-end property gates on both media types of the dual body actually being
chosen. -
Fixed. A property whose name is numeric is generated, materialized and
serialized (#61). PHP normalizes the array key"2020"to an integer, and
every place that read a member name checkedis_string(): the compiler
dropped such a property from the generated object, and the JSON and form
encoders refused the case outright — with a message sayingpropertiesmust
be an object, which it was. The name is cast back where it goes on the wire,
which is the only place PHP can carry it as a string. -
Fixed.
array_merge($result, [$key => $value]), the idiom used to quiet
psalm'sMixedAssignment, is replaced byarray_replace(#65).
array_mergerenumbers integer-like keys, so the very names the fix above
restores were the ones the idiom lost:['5' => 'a', '3' => 'b']came out as
[0 => 'a', 1 => 'b']. Where the value's type can be stated instead, it is —
a precise return type removes the warning at its source, which is what
openapi-contractdid for the same idiom. -
Fixed.
Internal\DirectionalSchemasdrops the last property together
withpropertiesitself, asopenapi-contract's effective schema does, and
its docblock now describes what it does rather than what the contract used to
(#67). An emptypropertiesforbids nothing, so the two readings agreed in
effect — but the sentence claiming they were identical was not true, and a
reader following it drew the wrong conclusion. -
Fixed.
RedactionPolicy::bodyPathsapplies to form and multipart bodies,
not only JSON (#63). It was a silent no-op for two of the three encodings, in
a feature whose only job is to keep a secret out of the reproducer: a
passwordfield of a form body was rendered in full in the curl command. -
Fixed. A redacted parameter keeps its shape (#64). The marker replaced
the whole value with a string, sodeepObject— which requires an object —
and the delimited styles — which require a list — could no longer be
serialized:reproduce()threw, and the caller printed
(no reproducer: …). Protecting a secret removed the reproducer for exactly
the failure it was needed on. A string is redacted to a string, a list to a
single-element list, an object to an object with redacted values. -
Fixed. A query credential that an exploded object query parameter would
swallow fails closed instead of silently changing the request (#68). The
parameter's style claims every pair a sibling parameter does not, so the
credential became one of its members: the operation was exercised with a
value other than the case recorded, and where the object is constrained the
case this package had called valid came back reported as invalid. OpenAPI
leaves that ambiguity to the style, so there is nothing to resolve — the
honest answer is to name both and stop, rather than send a request that means
something else. -
Changed. An anonymous security alternative — the empty requirement object
OpenAPI uses for "authentication is optional" — is a fallback rather than the
first choice (#69). Taken as soon as it was seen, and it usually is listed
first, it meant the suite never exercised the authenticated path of such an
operation at all. An alternative the provider can satisfy is preferred; the
anonymous one answers only when none can. -
Internal. The zoo's end-to-end property runs 900 cases rather than 450.
ItsClassify::covergates are absolute fractions of a mixed population —
each names a condition only one zoo operation can satisfy — so every
operation added to the zoo dilutes all of them. At 450 over 16 operations one
gate landed a tenth of a point under its threshold purely on the draw.
Known limit. An object whose member names run 0, 1, … without a gap cannot
be told from a list in a JSON-compatible PHP value, and a list is exactly what
a negative case sends when it means to violate an object schema. That shape
stays encoded as an array; it is pinned by a test so the limit is a decision
rather than a surprise.