TypeStringParserTest
class TypeStringParserTest extends TestCase (View source)
Boundary tests for the structural type-string grammar.
This is the single implementation used by the serializers, the stub type converter and the
validators, and it had no direct test at all — a regression here silently changes every parsed
type in the project. The tests below pin each grammar branch and, in particular, the two
precedence rules that are easy to break: | is examined before & so DNF strings are not
misrouted to intersection parsing, and the nullable shorthand is only applied when the non-null
part is not a parenthesised group.
Methods
No description
No description
No description
| must be examined before &. If the order were reversed, "int|(A&B)" would be routed to
intersection parsing and the union structure would be lost entirely.
No description
The parenthesised-intersection branch is guarded by "contains no |". Without that guard, "(A&B)|(C&D)" starts with '(' and ends with ')' and would be mis-parsed as one intersection of the whole inner text.
A nullable DNF type must stay a UnionType: the nullable shorthand cannot represent an intersection as its basic type, since NullableType only accepts a StandaloneType.
Three parts including null is a union, not a nullable — nullable is the exactly-two case.
No description
No description
'null' in a two-part union is matched literally and case-sensitively, so 'NULL' does not trigger the nullable collapse. Pinned because the surrounding code lowercases elsewhere, making this a plausible thing to "harmonise" and thereby change behaviour.
Intersection members are trimmed, so a spaced source type hint parses the same.
Union parts are trimmed too, via splitUnionParts().
A DNF group survives alongside plain members in a three-part union.
splitUnionParts() tracks parenthesis depth, so a '|' inside a group is not a separator.
A trailing separator yields no empty trailing member (splitUnionParts drops it).
toString() renders in a form the parser accepts again, and re-parsing is stable. Note the
?Foo shorthand normalises to Foo|null on the way out, so the round trip is idempotent
from the second pass onward rather than character-preserving on the first.
No description
Details
protected void
setUp()
No description
testGrammarProducesTheExpectedTypeShape(string $input, string $expectedClass, string $expectedString)
No description
static array
grammarCases()
No description
testDnfUnionKeepsItsIntersectionMembers()
| must be examined before &. If the order were reversed, "int|(A&B)" would be routed to
intersection parsing and the union structure would be lost entirely.
testDnfUnionOfTwoIntersectionGroups()
No description
testParenthesisedBranchDoesNotSwallowAWholeDnfString()
The parenthesised-intersection branch is guarded by "contains no |". Without that guard, "(A&B)|(C&D)" starts with '(' and ends with ')' and would be mis-parsed as one intersection of the whole inner text.
testNullableIntersectionStaysAUnionRatherThanCollapsing()
A nullable DNF type must stay a UnionType: the nullable shorthand cannot represent an intersection as its basic type, since NullableType only accepts a StandaloneType.
testUnionWithNullAndTwoOthersIsNotCollapsedToNullable()
Three parts including null is a union, not a nullable — nullable is the exactly-two case.
testEveryLeafNamePassesThroughTheResolver(string $input, string $expected)
No description
static array
everyLeafIsResolvedCases()
No description
testNullMatchingInTheNullableCollapseIsCaseSensitive()
'null' in a two-part union is matched literally and case-sensitively, so 'NULL' does not trigger the nullable collapse. Pinned because the surrounding code lowercases elsewhere, making this a plausible thing to "harmonise" and thereby change behaviour.
testIntersectionMembersAreTrimmed()
Intersection members are trimmed, so a spaced source type hint parses the same.
testUnionPartsAreTrimmed()
Union parts are trimmed too, via splitUnionParts().
testDnfGroupSurvivesAlongsidePlainUnionMembers()
A DNF group survives alongside plain members in a three-part union.
testUnionSplittingRespectsParenthesisDepth()
splitUnionParts() tracks parenthesis depth, so a '|' inside a group is not a separator.
No valid PHP type reaches this: DNF forbids a union inside parentheses, and every
well-formed input has only '&' inside its groups. So the depth counter can only be pinned
with a synthetic string — and it must be pinned structurally, because toString() renders
'(A|B)' either way whether the group was kept whole or split into '(A' and 'B)'. Dropping
the depth check passes every other test in this class, so without this one the counter is
unguarded.
testTrailingSeparatorDoesNotProduceAnEmptyMember()
A trailing separator yields no empty trailing member (splitUnionParts drops it).
testRenderedTypesReparseToTheSameShapeAndString(string $input)
toString() renders in a form the parser accepts again, and re-parsing is stable. Note the
?Foo shorthand normalises to Foo|null on the way out, so the round trip is idempotent
from the second pass onward rather than character-preserving on the first.
static array
roundTripCases()
No description