diff --git a/CHANGELOG.md b/CHANGELOG.md index 7461b59..b414305 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.3] - 2025-01-25 + +### Changed +- Upgrade `phpdocumentor/reflection-docblock` from ^5.3 to ^6.0 +- Upgrade `phpdocumentor/type-resolver` from ^1.6.1 to ^2.0 +- Inject `DocBlockFactoryInterface` via constructor in `ReturnEntity` + +### Fixed +- Fix `extractValueType` to correctly return value type for multi-parameter generics (`array`) + ## [1.0.1] - 2025-11-23 ### Fixed @@ -170,6 +180,7 @@ Stable release. Major update to PHP 8 attributes. Please refer to the git history for changes in earlier versions. +[1.0.3]: https://github.com/ray-di/Ray.MediaQuery/compare/1.0.1...1.0.3 [1.0.1]: https://github.com/ray-di/Ray.MediaQuery/compare/1.0.0...1.0.1 [1.0.0]: https://github.com/ray-di/Ray.MediaQuery/compare/1.0.0-rc2...1.0.0 [1.0.0-rc2]: https://github.com/ray-di/Ray.MediaQuery/compare/1.0.0-rc1...1.0.0-rc2 diff --git a/composer.json b/composer.json index 598e235..a74fa49 100644 --- a/composer.json +++ b/composer.json @@ -17,8 +17,8 @@ "koriym/csv-entities": "^1.0", "koriym/null-object": "^1.0.1", "pagerfanta/pagerfanta": "^3.5 || ^4.7", - "phpdocumentor/reflection-docblock": "^5.3", - "phpdocumentor/type-resolver": "^1.6.1", + "phpdocumentor/reflection-docblock": "^6.0", + "phpdocumentor/type-resolver": "^2.0", "ray/aop": "^2.19", "ray/aura-sql-module": "^1.17", "ray/di": "^2.19", diff --git a/src/MediaQueryDbModule.php b/src/MediaQueryDbModule.php index 72df3bd..928d53e 100644 --- a/src/MediaQueryDbModule.php +++ b/src/MediaQueryDbModule.php @@ -5,6 +5,8 @@ namespace Ray\MediaQuery; use Override; +use phpDocumentor\Reflection\DocBlockFactory; +use phpDocumentor\Reflection\DocBlockFactoryInterface; use Ray\Di\AbstractModule; use Ray\MediaQuery\Annotation\DbQuery; use Ray\MediaQuery\Annotation\Qualifier\FactoryMethod; @@ -38,6 +40,7 @@ protected function configure(): void [DbQueryInterceptor::class], ); $this->bind()->annotatedWith(SqlDir::class)->toInstance($this->configs->sqlDir); + $this->bind(DocBlockFactoryInterface::class)->toInstance(DocBlockFactory::createInstance()); $this->bind(ReturnEntityInterface::class)->to(ReturnEntity::class); $this->bind(FetchFactoryInterface::class)->to(FetchFactory::class); $this->bind()->annotatedWith(FactoryMethod::class)->toInstance('factory'); diff --git a/src/ReturnEntity.php b/src/ReturnEntity.php index e8a8a21..b846bba 100644 --- a/src/ReturnEntity.php +++ b/src/ReturnEntity.php @@ -6,15 +6,17 @@ use Override; use phpDocumentor\Reflection\DocBlock\Tags\Return_; -use phpDocumentor\Reflection\DocBlockFactory; +use phpDocumentor\Reflection\DocBlockFactoryInterface; +use phpDocumentor\Reflection\PseudoTypes\Generic; +use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\Types\Array_; -use phpDocumentor\Reflection\Types\Collection; use phpDocumentor\Reflection\Types\ContextFactory; use phpDocumentor\Reflection\Types\Object_; use ReflectionMethod; use ReflectionNamedType; use ReflectionType; +use function array_key_last; use function assert; use function class_exists; use function is_a; @@ -22,6 +24,11 @@ final class ReturnEntity implements ReturnEntityInterface { + public function __construct( + private DocBlockFactoryInterface $docBlockFactory, + ) { + } + /** @inheritDoc */ #[Override] public function __invoke(ReflectionMethod $method): string|null @@ -49,17 +56,32 @@ private function getReturnTypeName(ReflectionType $reflectionType): string return (string) $reflectionType; } + private function extractValueType(Type|null $type): Type|null + { + if ($type instanceof Array_) { + return $type->getValueType(); + } + + if ($type instanceof Generic) { + $types = $type->getTypes(); + $lastKey = array_key_last($types); + + return $lastKey !== null ? $types[$lastKey] : null; + } + + return null; + } + /** @return ?class-string */ private function docblock(ReflectionMethod $method): string|null { - $factory = DocBlockFactory::createInstance(); $context = (new ContextFactory())->createFromReflector($method); $docComment = $method->getDocComment(); if ($docComment === false) { return null; } - $docblock = $factory->create($docComment, $context); + $docblock = $this->docBlockFactory->create($docComment, $context); $returns = $docblock->getTagsByName('return'); if (! isset($returns[0])) { return null; @@ -68,11 +90,8 @@ private function docblock(ReflectionMethod $method): string|null $return = $returns[0]; assert($return instanceof Return_); $type = $return->getType(); - if (! $type instanceof Array_ && ! $type instanceof Collection) { - return null; - } - $valueType = $type->getValueType(); + $valueType = $this->extractValueType($type); if (! $valueType instanceof Object_) { return null; } diff --git a/tests/ReturnTypeTest.php b/tests/ReturnTypeTest.php index 98740f5..d4cc85a 100644 --- a/tests/ReturnTypeTest.php +++ b/tests/ReturnTypeTest.php @@ -4,16 +4,24 @@ namespace Ray\MediaQuery; +use phpDocumentor\Reflection\DocBlockFactory; use PHPUnit\Framework\TestCase; use Ray\MediaQuery\Entity\FakeEntity; use ReflectionMethod; class ReturnTypeTest extends TestCase { + private ReturnEntity $returnEntity; + + protected function setUp(): void + { + $this->returnEntity = new ReturnEntity(DocBlockFactory::createInstance()); + } + public function testReturnItem(): void { $method = new ReflectionMethod(new FakeReturn(), 'item'); - $entity = (new ReturnEntity())($method); + $entity = ($this->returnEntity)($method); $this->assertSame(FakeEntity::class, $entity); } @@ -21,7 +29,7 @@ public function testReturnItem(): void public function testReturnList(): void { $method = new ReflectionMethod(new FakeReturn(), 'list'); - $entity = (new ReturnEntity())($method); + $entity = ($this->returnEntity)($method); $this->assertSame(FakeEntity::class, $entity); } @@ -29,7 +37,7 @@ public function testReturnList(): void public function testNoReturnType(): void { $method = new ReflectionMethod(new FakeReturn(), 'noReturn'); - $entity = (new ReturnEntity())($method); + $entity = ($this->returnEntity)($method); $this->assertSame(null, $entity); } @@ -37,7 +45,7 @@ public function testNoReturnType(): void public function testnoPhpDoc(): void { $method = new ReflectionMethod(new FakeReturn(), 'noPhpDoc'); - $entity = (new ReturnEntity())($method); + $entity = ($this->returnEntity)($method); $this->assertSame(null, $entity); } @@ -45,7 +53,7 @@ public function testnoPhpDoc(): void public function testNoPhpDocFakePages(): void { $method = new ReflectionMethod(new FakeReturn(), 'noPhpDocFakePages'); - $entity = (new ReturnEntity())($method); + $entity = ($this->returnEntity)($method); $this->assertSame(null, $entity); } @@ -53,7 +61,7 @@ public function testNoPhpDocFakePages(): void public function testNoReturnDoc(): void { $method = new ReflectionMethod(new FakeReturn(), 'noReturnDoc'); - $entity = (new ReturnEntity())($method); + $entity = ($this->returnEntity)($method); $this->assertSame(null, $entity); } @@ -61,7 +69,7 @@ public function testNoReturnDoc(): void public function testNoReturnDocFakePages(): void { $method = new ReflectionMethod(new FakeReturn(), 'noReturnDocFakePages'); - $entity = (new ReturnEntity())($method); + $entity = ($this->returnEntity)($method); $this->assertSame(null, $entity); } @@ -69,7 +77,7 @@ public function testNoReturnDocFakePages(): void public function testNonEntityGeneric(): void { $method = new ReflectionMethod(new FakeReturn(), 'nonEntityGeneric'); - $entity = (new ReturnEntity())($method); + $entity = ($this->returnEntity)($method); $this->assertSame(null, $entity); } @@ -77,7 +85,7 @@ public function testNonEntityGeneric(): void public function testInvalidReturnType(): void { $method = new ReflectionMethod(new FakeReturn(), 'invalidReturnType'); - $entity = (new ReturnEntity())($method); + $entity = ($this->returnEntity)($method); $this->assertSame(null, $entity); } @@ -85,7 +93,7 @@ public function testInvalidReturnType(): void public function testReturnArray(): void { $method = new ReflectionMethod(new FakeReturn(), 'returnArray'); - $entity = (new ReturnEntity())($method); + $entity = ($this->returnEntity)($method); $this->assertSame(null, $entity); }