diff --git a/src/Metadata/Driver/DocBlockDriver/DocBlockTypeResolver.php b/src/Metadata/Driver/DocBlockDriver/DocBlockTypeResolver.php index d5fad5b64..b1ec7ad3c 100644 --- a/src/Metadata/Driver/DocBlockDriver/DocBlockTypeResolver.php +++ b/src/Metadata/Driver/DocBlockDriver/DocBlockTypeResolver.php @@ -7,6 +7,8 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasImportTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; @@ -265,7 +267,7 @@ private function expandClassNameUsingUseStatements(string $typeHint, \Reflection } if ($declaringClass->getDocComment()) { - $phpstanArrayType = $this->getPhpstanType($declaringClass, $typeHint, $reflector); + $phpstanArrayType = $this->getPhpstanArrayType($declaringClass, $typeHint, $reflector); if ($phpstanArrayType) { return $phpstanArrayType; @@ -401,29 +403,41 @@ private function resolveTypeFromDocblock($reflector): array /** * @param \ReflectionMethod|\ReflectionProperty $reflector */ - private function getPhpstanType(\ReflectionClass $declaringClass, string $typeHint, $reflector): ?string + private function getPhpstanArrayType(\ReflectionClass $declaringClass, string $typeHint, $reflector): ?string { $tokens = $this->lexer->tokenize($declaringClass->getDocComment()); $phpDocNode = $this->phpDocParser->parse(new TokenIterator($tokens)); $self = $this; foreach ($phpDocNode->children as $node) { - if ($node instanceof PhpDocTagNode && '@phpstan-type' === $node->name) { - $phpstanType = (string) $node->value; - preg_match_all(self::PHPSTAN_ARRAY_SHAPE, $phpstanType, $foundPhpstanArray); - if (isset($foundPhpstanArray[1][0]) && $foundPhpstanArray[1][0] === $typeHint) { + if ( + $node instanceof PhpDocTagNode + && $node->value instanceof TypeAliasTagValueNode + && $node->value->alias === $typeHint + ) { + $phpstanType = $node->value->__toString(); + preg_match(self::PHPSTAN_ARRAY_SHAPE, $phpstanType, $foundPhpstanArray); + if (isset($foundPhpstanArray[0])) { return 'array'; } - preg_match_all(self::PHPSTAN_ARRAY_TYPE, $phpstanType, $foundPhpstanArray); - if (isset($foundPhpstanArray[2][0]) && $foundPhpstanArray[1][0] === $typeHint) { - $types = explode(',', $foundPhpstanArray[2][0]); + preg_match(self::PHPSTAN_ARRAY_TYPE, $phpstanType, $foundPhpstanArray); + if (isset($foundPhpstanArray[0])) { + $types = explode(',', $foundPhpstanArray[2]); return sprintf('array<%s>', implode( ',', array_map(static fn (string $type) => $self->resolveType(trim($type), $reflector), $types), )); } + } elseif ($node instanceof PhpDocTagNode && $node->value instanceof TypeAliasImportTagValueNode) { + $importedFromFqn = $this->resolveType($node->value->importedFrom->name, $reflector); + + return $this->getPhpstanArrayType( + new \ReflectionClass($importedFromFqn), + $node->value->importedAlias, + $reflector, + ); } } diff --git a/tests/Fixtures/DocBlockType/Phpstan/PhpstanArrayShapeImported.php b/tests/Fixtures/DocBlockType/Phpstan/PhpstanArrayShapeImported.php new file mode 100644 index 000000000..d0df2cfa4 --- /dev/null +++ b/tests/Fixtures/DocBlockType/Phpstan/PhpstanArrayShapeImported.php @@ -0,0 +1,24 @@ +resolve(PhpstanArrayShapeImported::class); + + self::assertEquals( + ['name' => 'array', 'params' => []], + $m->propertyMetadata['data']->type, + ); + + self::assertEquals( + ['name' => 'array', 'params' => []], + $m->propertyMetadata['dataAliased']->type, + ); + } + public function testInferTypeForPhpstanNestedArrayShape() { $m = $this->resolve(PhpstanNestedArrayShape::class);