fix(reflection): resolve short docblock names in getIterableType - #2305
Open
radoslav-grencik wants to merge 2 commits into
Open
radoslav-grencik wants to merge 2 commits into
radoslav-grencik wants to merge 2 commits into
Conversation
radoslav-grencik
requested review from
aidan-casey and
brendt
as code owners
September 24, 2026 08:53
Benchmark ResultsComparison of Open to see the benchmark results
Generated by phpbench against commit 5e6fcfc |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PropertyReflector::getIterableType()regex-captured the bare short name from iterable docblocks (Filter[],Book[],Chapter[]) and returned aTypeReflectorfor a class literally namedFilter/Book/Chapter— ignoring the declaring file's namespace andusestatements. Everything downstream that consumes the iterable type resolved against a class that does not exist:/** @var Book[] */onAuthor::$books)/** @var Chapter[] */onBookRequest::$chapters)getIterableType()now resolves docblock names through PHP namespace andusesemantics.Change
packages/reflection/src/PropertyReflector.php:int[],array, …) pass through unchanged.\App\Foo[]) resolve as before — the leading\is dropped, the FQCN is used verbatim.DocblockRelativeTarget).use App\Foo;) and aliases (use App\Foo as Bar;) resolve both the exact class and alias-prefixed paths (use A\B as C;→C\D→A\B\D).use App\{Foo, Bar as Baz};) resolve first-class and aliased members, including a shared group prefix.list<Type>andarray<Type>docblock forms go through the same resolution.The parser is dependency-free (
token_get_all) and deliberately skips things that are not imports:useblocks (brace-depth tracking),use function ...anduse const ...statements,Parsed imports are cached per declaring file in a method-local
static, so repeated property reflection per process re-parses once.End-to-end coverage
Two existing FQCN fixture docblocks were intentionally converted to short names:
Author::$books→/** @var Book[] */— relation hydration resolves and loadsBookmodels.BookRequest::$chapters→/** @var Chapter[] */— request-body array mapping producesChapterobjects viaArrayToObjectCollectionCaster, which is selected automatically from the resolved iterable type.New tests:
packages/reflection/tests/PropertyReflectorTest.php— unit coverage for imports, aliases, grouped imports (plain + aliased + prefixed), relative names, FQCN,list<…>/array<…>forms, and the no-docblocknullcase with a dedicated fixture model + bucket targets.ArrayToObjectMapperTest::map_collection_of_objects_from_short_docblock_name— maps['items' => [['name' => 'a'], …]]intoLibraryIteminstances from a shortLibraryItem[]docblock (Library/Children\LibraryItemfixtures).