fix resolving parent/self/static type annotations

This commit is contained in:
Christian Flothmann 2021-02-16 11:56:12 +01:00 committed by Nicolas Grekas
parent 08c789c97b
commit e9f2ece991
8 changed files with 114 additions and 10 deletions

View File

@ -142,11 +142,31 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property
break; break;
} }
$parentClass = null;
$types = []; $types = [];
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */ /** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
foreach ($docBlock->getTagsByName($tag) as $tag) { foreach ($docBlock->getTagsByName($tag) as $tag) {
if ($tag && !$tag instanceof InvalidTag && null !== $tag->getType()) { if ($tag && !$tag instanceof InvalidTag && null !== $tag->getType()) {
$types = array_merge($types, $this->phpDocTypeHelper->getTypes($tag->getType())); foreach ($this->phpDocTypeHelper->getTypes($tag->getType()) as $type) {
switch ($type->getClassName()) {
case 'self':
case 'static':
$resolvedClass = $class;
break;
case 'parent':
if (false !== $resolvedClass = $parentClass ?? $parentClass = get_parent_class($class)) {
break;
}
// no break
default:
$types[] = $type;
continue 2;
}
$types[] = new Type(Type::BUILTIN_TYPE_OBJECT, $type->isNullable(), $resolvedClass, $type->isCollection(), $type->getCollectionKeyType(), $type->getCollectionValueType());
}
} }
} }

View File

@ -17,6 +17,7 @@ use phpDocumentor\Reflection\Types\Collection;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy; use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\DummyUsedInTrait; use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\DummyUsedInTrait;
use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\DummyUsingTrait; use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\DummyUsingTrait;
use Symfony\Component\PropertyInfo\Type; use Symfony\Component\PropertyInfo\Type;
@ -120,6 +121,7 @@ class PhpDocExtractorTest extends TestCase
['staticGetter', null, null, null], ['staticGetter', null, null, null],
['staticSetter', null, null, null], ['staticSetter', null, null, null],
['emptyVar', null, $this->isPhpDocumentorV5() ? 'This should not be removed.' : null, null], ['emptyVar', null, $this->isPhpDocumentorV5() ? 'This should not be removed.' : null, null],
['self', [new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)], null, null],
]; ];
} }
@ -293,6 +295,38 @@ class PhpDocExtractorTest extends TestCase
]; ];
} }
/**
* @dataProvider propertiesStaticTypeProvider
*/
public function testPropertiesStaticType(string $class, string $property, Type $type)
{
$this->assertEquals([$type], $this->extractor->getTypes($class, $property));
}
public function propertiesStaticTypeProvider(): array
{
return [
[ParentDummy::class, 'propertyTypeStatic', new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)],
[Dummy::class, 'propertyTypeStatic', new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)],
];
}
/**
* @dataProvider propertiesParentTypeProvider
*/
public function testPropertiesParentType(string $class, string $property, ?array $types)
{
$this->assertEquals($types, $this->extractor->getTypes($class, $property));
}
public function propertiesParentTypeProvider(): array
{
return [
[ParentDummy::class, 'parentAnnotationNoParent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'parent')]],
[Dummy::class, 'parentAnnotation', [new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)]],
];
}
protected function isPhpDocumentorV5() protected function isPhpDocumentorV5()
{ {
if (class_exists(InvalidTag::class)) { if (class_exists(InvalidTag::class)) {

View File

@ -20,6 +20,8 @@ use Symfony\Component\PropertyInfo\Tests\Fixtures\NotInstantiable;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php71Dummy; use Symfony\Component\PropertyInfo\Tests\Fixtures\Php71Dummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyExtended2; use Symfony\Component\PropertyInfo\Tests\Fixtures\Php71DummyExtended2;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php74Dummy; use Symfony\Component\PropertyInfo\Tests\Fixtures\Php74Dummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php7Dummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php7ParentDummy;
use Symfony\Component\PropertyInfo\Type; use Symfony\Component\PropertyInfo\Type;
/** /**
@ -57,12 +59,15 @@ class ReflectionExtractorTest extends TestCase
'iteratorCollection', 'iteratorCollection',
'iteratorCollectionWithKey', 'iteratorCollectionWithKey',
'nestedIterators', 'nestedIterators',
'parentAnnotation',
'foo', 'foo',
'foo2', 'foo2',
'foo3', 'foo3',
'foo4', 'foo4',
'foo5', 'foo5',
'files', 'files',
'propertyTypeStatic',
'parentAnnotationNoParent',
'a', 'a',
'DOB', 'DOB',
'Id', 'Id',
@ -105,12 +110,15 @@ class ReflectionExtractorTest extends TestCase
'iteratorCollection', 'iteratorCollection',
'iteratorCollectionWithKey', 'iteratorCollectionWithKey',
'nestedIterators', 'nestedIterators',
'parentAnnotation',
'foo', 'foo',
'foo2', 'foo2',
'foo3', 'foo3',
'foo4', 'foo4',
'foo5', 'foo5',
'files', 'files',
'propertyTypeStatic',
'parentAnnotationNoParent',
'date', 'date',
'c', 'c',
'd', 'd',
@ -143,12 +151,15 @@ class ReflectionExtractorTest extends TestCase
'iteratorCollection', 'iteratorCollection',
'iteratorCollectionWithKey', 'iteratorCollectionWithKey',
'nestedIterators', 'nestedIterators',
'parentAnnotation',
'foo', 'foo',
'foo2', 'foo2',
'foo3', 'foo3',
'foo4', 'foo4',
'foo5', 'foo5',
'files', 'files',
'propertyTypeStatic',
'parentAnnotationNoParent',
], ],
$noPrefixExtractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy') $noPrefixExtractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')
); );
@ -184,20 +195,21 @@ class ReflectionExtractorTest extends TestCase
/** /**
* @dataProvider php7TypesProvider * @dataProvider php7TypesProvider
*/ */
public function testExtractPhp7Type($property, array $type = null) public function testExtractPhp7Type(string $class, string $property, array $type = null)
{ {
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php7Dummy', $property, [])); $this->assertEquals($type, $this->extractor->getTypes($class, $property, []));
} }
public function php7TypesProvider() public function php7TypesProvider()
{ {
return [ return [
['foo', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)]], [Php7Dummy::class, 'foo', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)]],
['bar', [new Type(Type::BUILTIN_TYPE_INT)]], [Php7Dummy::class, 'bar', [new Type(Type::BUILTIN_TYPE_INT)]],
['baz', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))]], [Php7Dummy::class, 'baz', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))]],
['buz', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\Php7Dummy')]], [Php7Dummy::class, 'buz', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\Php7Dummy')]],
['biz', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'stdClass')]], [Php7Dummy::class, 'biz', [new Type(Type::BUILTIN_TYPE_OBJECT, false, Php7ParentDummy::class)]],
['donotexist', null], [Php7Dummy::class, 'donotexist', null],
[Php7ParentDummy::class, 'parent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, \stdClass::class)]],
]; ];
} }

View File

@ -130,6 +130,11 @@ class Dummy extends ParentDummy
*/ */
public $nestedIterators; public $nestedIterators;
/**
* @var parent
*/
public $parentAnnotation;
public static function getStatic() public static function getStatic()
{ {
} }

View File

@ -48,6 +48,16 @@ class ParentDummy
*/ */
public $files; public $files;
/**
* @var static
*/
public $propertyTypeStatic;
/**
* @var parent
*/
public $parentAnnotationNoParent;
/** /**
* @return bool|null * @return bool|null
*/ */

View File

@ -14,7 +14,7 @@ namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
/** /**
* @author Kévin Dunglas <dunglas@gmail.com> * @author Kévin Dunglas <dunglas@gmail.com>
*/ */
class Php7Dummy extends \stdClass class Php7Dummy extends Php7ParentDummy
{ {
public function getFoo(): array public function getFoo(): array
{ {

View File

@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
class Php7ParentDummy extends \stdClass
{
public function getParent(): parent
{
}
}

View File

@ -160,6 +160,10 @@ final class PhpDocTypeHelper
return [$docType, null]; return [$docType, null];
} }
if (\in_array($docType, ['parent', 'self', 'static'], true)) {
return ['object', $docType];
}
return ['object', substr($docType, 1)]; return ['object', substr($docType, 1)];
} }
} }