Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions src/Analyzer/DocblockTypesResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private function resolvePropertyTypes(Node $node): void
$arrayItemType = $docblock->getVarTagTypes();
$arrayItemType = array_pop($arrayItemType);

if (null !== $arrayItemType) {
if ($this->isTypeClass($arrayItemType)) {
$node->type = $this->resolveName(new Name($arrayItemType), Stmt\Use_::TYPE_NORMAL);

return;
Expand Down Expand Up @@ -129,7 +129,8 @@ private function resolveFunctionTypes(Node $node): void

$type = $docblock->getParamTagTypesByName('$'.$param->var->name);

if (null === $type) {
// we ignore any type which is not a class
if (!$this->isTypeClass($type)) {
continue;
}

Expand All @@ -141,7 +142,8 @@ private function resolveFunctionTypes(Node $node): void
$type = $docblock->getReturnTagTypes();
$type = array_pop($type);

if (null === $type) {
// we ignore any type which is not a class
if (!$this->isTypeClass($type)) {
return;
}

Expand Down Expand Up @@ -216,4 +218,18 @@ private function isTypeArray($type): bool
{
return null !== $type && isset($type->name) && 'array' === $type->name;
}

/**
* @psalm-assert-if-true string $fqcn
*/
private function isTypeClass(?string $fqcn): bool
{
if (null === $fqcn) {
return false;
}

$validFqcn = '/^[a-zA-Z0-9_\x7f-\xff\\\\]*[a-zA-Z0-9_\x7f-\xff]$/';

return (bool) preg_match($validFqcn, $fqcn);
}
}
12 changes: 10 additions & 2 deletions tests/Unit/Analyzer/DocblockParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public function test_it_should_exctract_types_from_param_tag(): void
* @param array<User> $user
* @param int $aValue
* @param MyPlainDto $plainDto
* @param array<int, int|string> $unionType
* @param array<int, array<int, int|string>> $nestedUnionType
*/
PHP;

Expand All @@ -34,6 +36,8 @@ public function test_it_should_exctract_types_from_param_tag(): void

self::assertEquals('int', $db->getParamTagTypesByName('$aValue'));
self::assertEquals('MyPlainDto', $db->getParamTagTypesByName('$plainDto'));
self::assertEquals('(int | string)', $db->getParamTagTypesByName('$unionType'));
self::assertEquals('array<int, (int | string)>', $db->getParamTagTypesByName('$nestedUnionType'));
}

public function test_it_should_extract_return_type_from_return_tag(): void
Expand All @@ -48,19 +52,21 @@ public function test_it_should_extract_return_type_from_return_tag(): void
* @return array<User>
* @return int
* @return MyPlainDto
* @return array<int, int|string>
*/
PHP;

$db = $parser->parse($code);

$returnTypes = $db->getReturnTagTypes();
self::assertCount(6, $returnTypes);
self::assertCount(7, $returnTypes);
self::assertEquals('MyDto', $returnTypes[0]);
self::assertEquals('MyOtherDto', $returnTypes[1]);
self::assertEquals('ValueObject', $returnTypes[2]);
self::assertEquals('User', $returnTypes[3]);
self::assertEquals('int', $returnTypes[4]);
self::assertEquals('MyPlainDto', $returnTypes[5]);
self::assertEquals('(int | string)', $returnTypes[6]);
}

public function test_it_should_extract_types_from_var_tag(): void
Expand All @@ -75,19 +81,21 @@ public function test_it_should_extract_types_from_var_tag(): void
* @var array<User> $user
* @var int $aValue
* @var MyPlainDto $plainDto
* @var array<int, int|string> $unionType
*/
PHP;

$db = $parser->parse($code);

$varTags = $db->getVarTagTypes();
self::assertCount(6, $varTags);
self::assertCount(7, $varTags);
self::assertEquals('MyDto', $varTags[0]);
self::assertEquals('MyOtherDto', $varTags[1]);
self::assertEquals('ValueObject', $varTags[2]);
self::assertEquals('User', $varTags[3]);
self::assertEquals('int', $varTags[4]);
self::assertEquals('MyPlainDto', $varTags[5]);
self::assertEquals('(int | string)', $varTags[6]);
}

public function test_it_should_extract_doctrine_like_annotations(): void
Expand Down
34 changes: 26 additions & 8 deletions tests/Unit/Analyzer/DocblockTypesResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

class DocblockTypesResolverTest extends TestCase
{
public function test_it_should_boh(): void
public function test_it_should_collect_dependencies_defined_in_docblock(): void
{
$parser = new FileParser(
new NodeTraverser(),
Expand All @@ -36,6 +36,13 @@ public function test_it_should_boh(): void

class MyClass
{
/** @var array<int, int|string> */
public array $myArray;

/** @var array<int, User> */
public array $users;


/**
* @param MyDto[] $dtoList
* @param int $var2
Expand All @@ -52,19 +59,30 @@ public function __construct(string $var1, array $dtoList, $var2, array $voList)
public function myMethod(array $users, array $products, MyOtherClass $other): void
{
}

/**
*
* @param array<int, int|string> $aParam
* @param array<int, User> $users
*
* @return array<int, int|string>
*/
public function myMethod2(array $aParam, array $users): array
}
EOF;

$parser->parse($code, 'boh');
$parser->parse($code, 'src/path/file.php');

$cd = $parser->getClassDescriptions()[0];
$dep = $cd->getDependencies();

self::assertCount(5, $cd->getDependencies());
self::assertEquals('Application\MyDto', $dep[0]->getFQCN()->toString());
self::assertEquals('Domain\ValueObject', $dep[1]->getFQCN()->toString());
self::assertEquals('Application\Model\User', $dep[2]->getFQCN()->toString());
self::assertEquals('Application\Model\Product', $dep[3]->getFQCN()->toString());
self::assertEquals('Domain\Foo\MyOtherClass', $dep[4]->getFQCN()->toString());
self::assertCount(7, $cd->getDependencies());
self::assertEquals('Application\Model\User', $dep[0]->getFQCN()->toString());
self::assertEquals('Application\MyDto', $dep[1]->getFQCN()->toString());
self::assertEquals('Domain\ValueObject', $dep[2]->getFQCN()->toString());
self::assertEquals('Application\Model\User', $dep[3]->getFQCN()->toString());
self::assertEquals('Application\Model\Product', $dep[4]->getFQCN()->toString());
self::assertEquals('Domain\Foo\MyOtherClass', $dep[5]->getFQCN()->toString());
self::assertEquals('Application\Model\User', $dep[6]->getFQCN()->toString());
}
}