Skip to content

Commit 1d60a2e

Browse files
committed
wip
1 parent 27acb59 commit 1d60a2e

15 files changed

+1584
-558
lines changed

src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,11 @@ public function getTypes(string $class, string $property, array $context = []):
118118
trigger_deprecation('symfony/property-info', '7.1', 'The "%s()" method is deprecated. Use "%s::getType()" instead.', __METHOD__, self::class);
119119

120120
if (null === $type = $this->getType($class, $property, $context)) {
121-
return $type;
121+
return null;
122+
}
123+
124+
if ('mixed' === (string) $type) {
125+
return null;
122126
}
123127

124128
$type = Type::convertFromTypeInfoType($type);
@@ -195,7 +199,11 @@ public function getTypesFromConstructor(string $class, string $property): ?array
195199
trigger_deprecation('symfony/property-info', '7.1', 'The "%s()" method is deprecated. Use "%s::getTypeFromConstructor()" instead.', __METHOD__, self::class);
196200

197201
if (null === $type = $this->getTypeFromConstructor($class, $property)) {
198-
return $type;
202+
return null;
203+
}
204+
205+
if ('mixed' === (string) $type) {
206+
return null;
199207
}
200208

201209
$type = Type::convertFromTypeInfoType($type);

src/Symfony/Component/PropertyInfo/Extractor/PhpStanExtractor.php

Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,24 @@
1111

1212
namespace Symfony\Component\PropertyInfo\Extractor;
1313

14+
use Symfony\Component\TypeInfo\Exception\UnsupportedException;
15+
use Symfony\Component\TypeInfo\TypeContext\TypeContextFactory;
16+
use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver;
1417
use phpDocumentor\Reflection\Types\ContextFactory;
1518
use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
1619
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
1720
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
1821
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
1922
use PHPStan\PhpDocParser\Lexer\Lexer;
23+
use PHPStan\PhpDocParser\Parser\ConstExprParser;
2024
use PHPStan\PhpDocParser\Parser\PhpDocParser;
2125
use PHPStan\PhpDocParser\Parser\TokenIterator;
26+
use PHPStan\PhpDocParser\Parser\TypeParser;
27+
use Symfony\Component\PropertyInfo\PhpStan\NameScopeFactory;
2228
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
2329
use Symfony\Component\PropertyInfo\Type;
2430
use Symfony\Component\PropertyInfo\Util\PhpStanTypeHelper;
2531
use Symfony\Component\TypeInfo\Type as TypeInfoType;
26-
use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver;
27-
use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface;
2832

2933
/**
3034
* Extracts data using PHPStan parser.
@@ -37,7 +41,12 @@ final class PhpStanExtractor implements PropertyTypeExtractorInterface, Construc
3741
private const ACCESSOR = 1;
3842
private const MUTATOR = 2;
3943

40-
private TypeResolverInterface $stringTypeResolver;
44+
private PhpDocParser $phpDocParser;
45+
private Lexer $lexer;
46+
private NameScopeFactory $nameScopeFactory;
47+
48+
private StringTypeResolver $stringTypeResolver;
49+
private TypeContextFactory $typeContextFactory;
4150

4251
/** @var array<string, array{PhpDocNode|null, int|null, string|null, string|null}> */
4352
private array $docBlocks = [];
@@ -66,15 +75,26 @@ public function __construct(array $mutatorPrefixes = null, array $accessorPrefix
6675
$this->accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes;
6776
$this->arrayMutatorPrefixes = $arrayMutatorPrefixes ?? ReflectionExtractor::$defaultArrayMutatorPrefixes;
6877

78+
$this->phpDocParser = new PhpDocParser(new TypeParser(new ConstExprParser()), new ConstExprParser());
79+
$this->lexer = new Lexer();
80+
$this->nameScopeFactory = new NameScopeFactory();
6981
$this->stringTypeResolver = new StringTypeResolver();
82+
$this->typeContextFactory = new TypeContextFactory($this->stringTypeResolver);
7083
}
7184

7285
public function getTypes(string $class, string $property, array $context = []): ?array
7386
{
7487
trigger_deprecation('symfony/property-info', '7.1', 'The "%s()" method is deprecated. Use "%s::getType()" instead.', __METHOD__, self::class);
7588

76-
if (null === $type = $this->getType($class, $property, $context)) {
77-
return $type;
89+
try {
90+
$type = $this->getType($class, $property, $context);
91+
} catch (UnsupportedException) {
92+
// workaround to handle void
93+
return [new Type(Type::BUILTIN_TYPE_NULL)];
94+
}
95+
96+
if (null === $type || 'mixed' === (string) $type) {
97+
return null;
7898
}
7999

80100
$type = Type::convertFromTypeInfoType($type);
@@ -90,27 +110,23 @@ public function getType(string $class, string $property, array $context = []): ?
90110
{
91111
/** @var PhpDocNode|null $docNode */
92112
[$docNode, $source, $prefix, $declaringClass] = $this->getDocBlock($class, $property);
93-
$nameScope = $this->nameScopeFactory->create($class, $declaringClass);
113+
94114
if (null === $docNode) {
95115
return null;
96116
}
97117

98-
switch ($source) {
99-
case self::PROPERTY:
100-
$tag = '@var';
101-
break;
102-
103-
case self::ACCESSOR:
104-
$tag = '@return';
105-
break;
118+
$nameScope = $this->nameScopeFactory->create($class, $declaringClass);
119+
$typeContext = $this->typeContextFactory->createFromClassName($class, $declaringClass);
106120

107-
case self::MUTATOR:
108-
$tag = '@param';
109-
break;
110-
}
121+
$tag = match ($source) {
122+
self::PROPERTY => '@var',
123+
self::ACCESSOR => '@return',
124+
self::MUTATOR => '@param',
125+
default => null,
126+
};
111127

112-
$parentClass = null;
113128
$types = [];
129+
114130
foreach ($docNode->getTagsByName($tag) as $tagDocNode) {
115131
if ($tagDocNode->value instanceof InvalidTagValueNode) {
116132
continue;
@@ -124,37 +140,22 @@ public function getType(string $class, string $property, array $context = []): ?
124140
continue;
125141
}
126142

127-
foreach ($this->phpStanTypeHelper->getTypes($tagDocNode->value, $nameScope) as $type) {
128-
switch ($type->getClassName()) {
129-
case 'self':
130-
case 'static':
131-
$resolvedClass = $class;
132-
break;
133-
134-
case 'parent':
135-
if (false !== $resolvedClass = $parentClass ??= get_parent_class($class)) {
136-
break;
137-
}
138-
// no break
139-
140-
default:
141-
$types[] = $type;
142-
continue 2;
143-
}
144-
145-
$types[] = new Type(Type::BUILTIN_TYPE_OBJECT, $type->isNullable(), $resolvedClass, $type->isCollection(), $type->getCollectionKeyTypes(), $type->getCollectionValueTypes());
146-
}
143+
$types[] = $this->stringTypeResolver->resolve((string) $tagDocNode->value->type, $typeContext);
147144
}
148145

149-
if (!isset($types[0])) {
146+
if (null === ($type = $types[0] ?? null)) {
150147
return null;
151148
}
152149

153-
if (!\in_array($prefix, $this->arrayMutatorPrefixes, true)) {
154-
return $types;
150+
if (!\in_array($prefix, $this->arrayMutatorPrefixes)) {
151+
return $type;
155152
}
156153

157-
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $types[0])];
154+
return TypeInfoType::list($type);
155+
}
156+
157+
public function getTypeFromConstructor(string $class, string $property): ?TypeInfoType
158+
{
158159
}
159160

160161
public function getTypesFromConstructor(string $class, string $property): ?array

src/Symfony/Component/PropertyInfo/Tests/Extractor/ConstructorExtractorTest.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\PropertyInfo\Extractor\ConstructorExtractor;
1616
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyExtractor;
17-
use Symfony\Component\PropertyInfo\Type;
17+
use Symfony\Component\PropertyInfo\Type as LegacyType;
18+
use Symfony\Component\TypeInfo\Type;
1819

1920
/**
2021
* @author Dmitrii Poddubnyi <[email protected]>
@@ -33,11 +34,28 @@ public function testInstanceOf()
3334
$this->assertInstanceOf(\Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface::class, $this->extractor);
3435
}
3536

37+
public function testGetType()
38+
{
39+
$this->assertEquals(Type::string(), $this->extractor->getType('Foo', 'bar', []));
40+
}
41+
42+
public function testGetTypeIfNoExtractors()
43+
{
44+
$extractor = new ConstructorExtractor([]);
45+
$this->assertNull($extractor->getType('Foo', 'bar', []));
46+
}
47+
48+
/**
49+
* @group legacy
50+
*/
3651
public function testGetTypes()
3752
{
38-
$this->assertEquals([new Type(Type::BUILTIN_TYPE_STRING)], $this->extractor->getTypes('Foo', 'bar', []));
53+
$this->assertEquals([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)], $this->extractor->getTypes('Foo', 'bar', []));
3954
}
4055

56+
/**
57+
* @group legacy
58+
*/
4159
public function testGetTypesIfNoExtractors()
4260
{
4361
$extractor = new ConstructorExtractor([]);

0 commit comments

Comments
 (0)