diff --git a/lib/Bridge/Phpactor/Docblock.php b/lib/Bridge/Phpactor/Docblock.php index 569fd6efa..e264d7812 100644 --- a/lib/Bridge/Phpactor/Docblock.php +++ b/lib/Bridge/Phpactor/Docblock.php @@ -5,11 +5,13 @@ use Phpactor\Docblock\DocblockType; use Phpactor\Docblock\DocblockTypes; use Phpactor\Docblock\Tag\MethodTag; +use Phpactor\Docblock\Tag\MixinTag; use Phpactor\WorseReflection\Bridge\TolerantParser\Reflection\ReflectionInterface; use Phpactor\WorseReflection\Core\Deprecation; use Phpactor\WorseReflection\Core\DocBlock\DocBlock as CoreDocblock; use Phpactor\Docblock\Docblock as PhpactorDocblock; use Phpactor\WorseReflection\Core\DocBlock\DocBlockVars; +use Phpactor\WorseReflection\Core\Name; use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionMethodCollection; use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionPropertyCollection; use Phpactor\WorseReflection\Core\Reflection\ReflectionClassLike; @@ -113,6 +115,16 @@ public function vars(): DocBlockVars return new DocBlockVars($vars); } + /** + * @return Name[] + */ + public function mixins(): array + { + return array_map(function (MixinTag $tag) { + return Name::fromString($tag->fqn()); + }, iterator_to_array($this->docblock->tags()->byName('mixin'))); + } + public function inherits(): bool { return 0 !== $this->docblock->tags()->byName('inheritDoc')->count(); diff --git a/lib/Bridge/Phpactor/MemberProvider/MixinMemberProvider.php b/lib/Bridge/Phpactor/MemberProvider/MixinMemberProvider.php new file mode 100644 index 000000000..7e7ba6c7a --- /dev/null +++ b/lib/Bridge/Phpactor/MemberProvider/MixinMemberProvider.php @@ -0,0 +1,26 @@ +docblock()->mixins() as $name) { + $fqn = $class->scope()->resolveLocalName($name); + $mixinClass = $locator->reflector()->reflectClass($fqn); + + $collection = $collection->merge($mixinClass->members()); + } + + return $collection; + } +} diff --git a/lib/Core/DocBlock/DocBlock.php b/lib/Core/DocBlock/DocBlock.php index 647fff6bb..82157857b 100644 --- a/lib/Core/DocBlock/DocBlock.php +++ b/lib/Core/DocBlock/DocBlock.php @@ -3,6 +3,7 @@ namespace Phpactor\WorseReflection\Core\DocBlock; use Phpactor\WorseReflection\Core\Deprecation; +use Phpactor\WorseReflection\Core\Name; use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionMethodCollection; use Phpactor\WorseReflection\Core\Reflection\Collection\ReflectionPropertyCollection; use Phpactor\WorseReflection\Core\Reflection\ReflectionClassLike; @@ -33,4 +34,9 @@ public function vars(): DocBlockVars; public function inherits(): bool; public function deprecation(): Deprecation; + + /** + * @return Name[] + */ + public function mixins(): array; } diff --git a/tests/Integration/Bridge/TolerantParser/Reflection/ReflectionClassTest.php b/tests/Integration/Bridge/TolerantParser/Reflection/ReflectionClassTest.php index 6e487f8a7..8659bdfeb 100644 --- a/tests/Integration/Bridge/TolerantParser/Reflection/ReflectionClassTest.php +++ b/tests/Integration/Bridge/TolerantParser/Reflection/ReflectionClassTest.php @@ -2,6 +2,7 @@ namespace Phpactor\WorseReflection\Tests\Integration\Bridge\TolerantParser\Reflection; +use Generator; use Phpactor\WorseReflection\Core\Visibility; use Phpactor\WorseReflection\Tests\Integration\IntegrationTestCase; use Phpactor\WorseReflection\Core\ClassName; @@ -771,7 +772,10 @@ public function testVirtualMethods(string $source, string $class, \Closure $asse } - public function provideVirtualMethods() + /** + * @return Generator + */ + public function provideVirtualMethods(): Generator { yield 'virtual methods' => [ <<<'EOT' @@ -1154,4 +1158,46 @@ function (ReflectionClass $class) { } ]; } + + /** + * @dataProvider provideMixins + */ + public function testMixins(string $source, string $class, \Closure $assertion) + { + $class = $this->createReflector($source)->reflectClassLike(ClassName::fromString($class)); + $assertion($class); + } + + /** + * @return Generator + */ + public function provideMixins(): Generator + { + yield 'mixin' => [ + <<<'EOT' +assertEquals(1, $class->methods()->count()); + $this->assertEquals('foobar', $class->methods()->first()->name()); + } + ]; + } } diff --git a/tests/Integration/IntegrationTestCase.php b/tests/Integration/IntegrationTestCase.php index 8426ee6ee..8095bf40f 100644 --- a/tests/Integration/IntegrationTestCase.php +++ b/tests/Integration/IntegrationTestCase.php @@ -4,6 +4,7 @@ use Phpactor\TestUtils\Workspace; use Phpactor\WorseReflection\Bridge\Phpactor\MemberProvider\DocblockMemberProvider; +use Phpactor\WorseReflection\Bridge\Phpactor\MemberProvider\MixinMemberProvider; use Phpactor\WorseReflection\Core\SourceCodeLocator\StubSourceLocator; use Phpactor\WorseReflection\Reflector; use PHPUnit\Framework\TestCase; @@ -29,6 +30,7 @@ public function createReflector(string $source): Reflector return ReflectorBuilder::create() ->addSource($source) ->addMemberProvider(new DocblockMemberProvider()) + ->addMemberProvider(new MixinMemberProvider()) ->withLogger($this->logger())->build(); } diff --git a/tests/Unit/Bridge/Phpactor/DocblockTest.php b/tests/Unit/Bridge/Phpactor/DocblockTest.php index d6626b2c3..bed07fa81 100644 --- a/tests/Unit/Bridge/Phpactor/DocblockTest.php +++ b/tests/Unit/Bridge/Phpactor/DocblockTest.php @@ -5,6 +5,7 @@ use PHPUnit\Framework\TestCase; use Phpactor\WorseReflection\Bridge\Phpactor\DocblockFactory; use Phpactor\WorseReflection\Core\DocBlock\DocBlock; +use Phpactor\WorseReflection\Core\Name; class DocblockTest extends TestCase { @@ -68,6 +69,15 @@ public function testInherits() $this->assertTrue($docblock->inherits()); } + public function testMixins(): void + { + $docblock = $this->create('/** @mixin Foobar */'); + $mixins = $docblock->mixins(); + self::assertCount(1, $mixins); + $mixin = reset($mixins); + self::assertInstanceOf(Name::class, $mixin); + } + public function testDeprecated(): void { $docblock = $this->create('/** Hello */');