Skip to content

Commit 23c581f

Browse files
mtarldKorbeil
andcommitted
[TypeInfo][PropertyInfo] Deprecate PropertyInfo Type
Co-authored-by: Baptiste Leduc <[email protected]>
1 parent 41c48f2 commit 23c581f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+3090
-1597
lines changed

src/Symfony/Component/PropertyInfo/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.1
5+
---
6+
7+
* Deprecate the `Type` class, use `Symfony\Component\TypeInfo\Type` class of `symfony/type-info` component instead
8+
49
6.4
510
---
611

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111

1212
namespace Symfony\Component\PropertyInfo\Extractor;
1313

14-
use Symfony\Component\PropertyInfo\Type;
14+
use Symfony\Component\PropertyInfo\Type as LegacyType;
15+
use Symfony\Component\TypeInfo\Type;
1516

1617
/**
1718
* Infers the constructor argument type.
@@ -25,9 +26,18 @@ interface ConstructorArgumentTypeExtractorInterface
2526
/**
2627
* Gets types of an argument from constructor.
2728
*
28-
* @return Type[]|null
29+
* @deprecated since Symfony 7.1, use "getTypeFromConstructor" instead.
30+
*
31+
* @return LegacyType[]|null
2932
*
3033
* @internal
3134
*/
3235
public function getTypesFromConstructor(string $class, string $property): ?array;
36+
37+
/**
38+
* Gets type of an argument from constructor.
39+
*
40+
* @internal
41+
*/
42+
public function getTypeFromConstructor(string $class, string $property): ?Type;
3343
}

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
namespace Symfony\Component\PropertyInfo\Extractor;
1313

1414
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
15+
use Symfony\Component\PropertyInfo\Util\BackwardCompatibilityHelper;
16+
use Symfony\Component\TypeInfo\Type;
1517

1618
/**
1719
* Extracts the constructor argument type using ConstructorArgumentTypeExtractorInterface implementations.
@@ -28,15 +30,22 @@ public function __construct(
2830
) {
2931
}
3032

31-
public function getTypes(string $class, string $property, array $context = []): ?array
33+
public function getType(string $class, string $property, array $context = []): ?Type
3234
{
3335
foreach ($this->extractors as $extractor) {
34-
$value = $extractor->getTypesFromConstructor($class, $property);
36+
$value = $extractor->getTypeFromConstructor($class, $property);
3537
if (null !== $value) {
3638
return $value;
3739
}
3840
}
3941

4042
return null;
4143
}
44+
45+
public function getTypes(string $class, string $property, array $context = []): ?array
46+
{
47+
trigger_deprecation('symfony/property-info', '7.1', 'The "%s()" method is deprecated. Use "%s::getType()" instead.', __METHOD__, self::class);
48+
49+
return BackwardCompatibilityHelper::convertTypeToLegacyTypes($this->getType($class, $property, $context));
50+
}
4251
}

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

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
use phpDocumentor\Reflection\Types\ContextFactory;
2020
use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface;
2121
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
22-
use Symfony\Component\PropertyInfo\Type;
22+
use Symfony\Component\PropertyInfo\Util\BackwardCompatibilityHelper;
2323
use Symfony\Component\PropertyInfo\Util\PhpDocTypeHelper;
24+
use Symfony\Component\TypeInfo\Type;
25+
use Symfony\Component\TypeInfo\Type\ObjectType;
2426

2527
/**
2628
* Extracts data using a PHPDoc parser.
@@ -111,7 +113,7 @@ public function getLongDescription(string $class, string $property, array $conte
111113
return '' === $contents ? null : $contents;
112114
}
113115

114-
public function getTypes(string $class, string $property, array $context = []): ?array
116+
public function getType(string $class, string $property, array $context = []): ?Type
115117
{
116118
/** @var $docBlock DocBlock */
117119
[$docBlock, $source, $prefix] = $this->getDocBlock($class, $property);
@@ -130,41 +132,54 @@ public function getTypes(string $class, string $property, array $context = []):
130132
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
131133
foreach ($docBlock->getTagsByName($tag) as $tag) {
132134
if ($tag && !$tag instanceof InvalidTag && null !== $tag->getType()) {
133-
foreach ($this->phpDocTypeHelper->getTypes($tag->getType()) as $type) {
134-
switch ($type->getClassName()) {
135-
case 'self':
136-
case 'static':
137-
$resolvedClass = $class;
138-
break;
135+
$type = $this->phpDocTypeHelper->getType($tag->getType());
136+
137+
if (!$type instanceof ObjectType) {
138+
$types[] = $type;
139+
140+
continue;
141+
}
139142

140-
case 'parent':
141-
if (false !== $resolvedClass = $parentClass ??= get_parent_class($class)) {
142-
break;
143-
}
144-
// no break
143+
switch ($type->getClassName()) {
144+
case 'self':
145+
case 'static':
146+
$resolvedClass = $class;
147+
break;
145148

146-
default:
147-
$types[] = $type;
148-
continue 2;
149-
}
149+
case 'parent':
150+
if (false !== $resolvedClass = $parentClass ??= get_parent_class($class)) {
151+
break;
152+
}
153+
// no break
150154

151-
$types[] = new Type(Type::BUILTIN_TYPE_OBJECT, $type->isNullable(), $resolvedClass, $type->isCollection(), $type->getCollectionKeyTypes(), $type->getCollectionValueTypes());
155+
default:
156+
$types[] = $type;
157+
continue 2;
152158
}
159+
160+
$types[] = $type->isNullable() ? Type::nullable(Type::object($resolvedClass)) : Type::object($resolvedClass);
153161
}
154162
}
155163

156-
if (!isset($types[0])) {
164+
if (null === ($type = $types[0] ?? null)) {
157165
return null;
158166
}
159167

160168
if (!\in_array($prefix, $this->arrayMutatorPrefixes)) {
161-
return $types;
169+
return $type;
162170
}
163171

164-
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $types[0])];
172+
return Type::list($type);
165173
}
166174

167-
public function getTypesFromConstructor(string $class, string $property): ?array
175+
public function getTypes(string $class, string $property, array $context = []): ?array
176+
{
177+
trigger_deprecation('symfony/property-info', '7.1', 'The "%s()" method is deprecated. Use "%s::getType()" instead.', __METHOD__, self::class);
178+
179+
return BackwardCompatibilityHelper::convertTypeToLegacyTypes($this->getType($class, $property, $context));
180+
}
181+
182+
public function getTypeFromConstructor(string $class, string $property): ?Type
168183
{
169184
$docBlock = $this->getDocBlockFromConstructor($class, $property);
170185

@@ -176,15 +191,18 @@ public function getTypesFromConstructor(string $class, string $property): ?array
176191
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
177192
foreach ($docBlock->getTagsByName('param') as $tag) {
178193
if ($tag && null !== $tag->getType()) {
179-
$types[] = $this->phpDocTypeHelper->getTypes($tag->getType());
194+
$types[] = $this->phpDocTypeHelper->getType($tag->getType());
180195
}
181196
}
182197

183-
if (!isset($types[0]) || [] === $types[0]) {
184-
return null;
185-
}
198+
return $types[0] ?? null;
199+
}
200+
201+
public function getTypesFromConstructor(string $class, string $property): ?array
202+
{
203+
trigger_deprecation('symfony/property-info', '7.1', 'The "%s()" method is deprecated. Use "%s::getTypeFromConstructor()" instead.', __METHOD__, self::class);
186204

187-
return array_merge([], ...$types);
205+
return BackwardCompatibilityHelper::convertTypeToLegacyTypes($this->getTypeFromConstructor($class, $property));
188206
}
189207

190208
private function getDocBlockFromConstructor(string $class, string $property): ?DocBlock

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

Lines changed: 58 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@
2121
use PHPStan\PhpDocParser\Parser\PhpDocParser;
2222
use PHPStan\PhpDocParser\Parser\TokenIterator;
2323
use PHPStan\PhpDocParser\Parser\TypeParser;
24-
use Symfony\Component\PropertyInfo\PhpStan\NameScopeFactory;
2524
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
26-
use Symfony\Component\PropertyInfo\Type;
27-
use Symfony\Component\PropertyInfo\Util\PhpStanTypeHelper;
25+
use Symfony\Component\PropertyInfo\Util\BackwardCompatibilityHelper;
26+
use Symfony\Component\TypeInfo\Exception\LogicException;
27+
use Symfony\Component\TypeInfo\Exception\UnsupportedException;
28+
use Symfony\Component\TypeInfo\Type;
29+
use Symfony\Component\TypeInfo\TypeContext\TypeContextFactory;
30+
use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver;
2831

2932
/**
3033
* Extracts data using PHPStan parser.
@@ -39,11 +42,12 @@ final class PhpStanExtractor implements PropertyTypeExtractorInterface, Construc
3942

4043
private PhpDocParser $phpDocParser;
4144
private Lexer $lexer;
42-
private NameScopeFactory $nameScopeFactory;
45+
46+
private StringTypeResolver $stringTypeResolver;
47+
private TypeContextFactory $typeContextFactory;
4348

4449
/** @var array<string, array{PhpDocNode|null, int|null, string|null, string|null}> */
4550
private array $docBlocks = [];
46-
private PhpStanTypeHelper $phpStanTypeHelper;
4751
private array $mutatorPrefixes;
4852
private array $accessorPrefixes;
4953
private array $arrayMutatorPrefixes;
@@ -63,103 +67,100 @@ public function __construct(array $mutatorPrefixes = null, array $accessorPrefix
6367
throw new \LogicException(sprintf('Unable to use the "%s" class as the "phpstan/phpdoc-parser" package is not installed. Try running composer require "phpstan/phpdoc-parser".', __CLASS__));
6468
}
6569

66-
$this->phpStanTypeHelper = new PhpStanTypeHelper();
6770
$this->mutatorPrefixes = $mutatorPrefixes ?? ReflectionExtractor::$defaultMutatorPrefixes;
6871
$this->accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes;
6972
$this->arrayMutatorPrefixes = $arrayMutatorPrefixes ?? ReflectionExtractor::$defaultArrayMutatorPrefixes;
7073

7174
$this->phpDocParser = new PhpDocParser(new TypeParser(new ConstExprParser()), new ConstExprParser());
7275
$this->lexer = new Lexer();
73-
$this->nameScopeFactory = new NameScopeFactory();
76+
$this->stringTypeResolver = new StringTypeResolver();
77+
$this->typeContextFactory = new TypeContextFactory($this->stringTypeResolver);
7478
}
7579

76-
public function getTypes(string $class, string $property, array $context = []): ?array
80+
public function getType(string $class, string $property, array $context = []): ?Type
7781
{
82+
$backwardCompatible = \func_get_args()[3] ?? false;
83+
7884
/** @var PhpDocNode|null $docNode */
7985
[$docNode, $source, $prefix, $declaringClass] = $this->getDocBlock($class, $property);
80-
$nameScope = $this->nameScopeFactory->create($class, $declaringClass);
86+
8187
if (null === $docNode) {
8288
return null;
8389
}
8490

85-
switch ($source) {
86-
case self::PROPERTY:
87-
$tag = '@var';
88-
break;
89-
90-
case self::ACCESSOR:
91-
$tag = '@return';
92-
break;
91+
$typeContext = $this->typeContextFactory->createFromClassName($class, $declaringClass);
9392

94-
case self::MUTATOR:
95-
$tag = '@param';
96-
break;
97-
}
93+
$tag = match ($source) {
94+
self::PROPERTY => '@var',
95+
self::ACCESSOR => '@return',
96+
self::MUTATOR => '@param',
97+
default => null,
98+
};
9899

99-
$parentClass = null;
100100
$types = [];
101+
101102
foreach ($docNode->getTagsByName($tag) as $tagDocNode) {
102103
if ($tagDocNode->value instanceof InvalidTagValueNode) {
103104
continue;
104105
}
105106

106-
if (
107-
$tagDocNode->value instanceof ParamTagValueNode
108-
&& null === $prefix
109-
&& $tagDocNode->value->parameterName !== '$'.$property
110-
) {
107+
if ($tagDocNode->value instanceof ParamTagValueNode && null === $prefix && $tagDocNode->value->parameterName !== '$'.$property) {
111108
continue;
112109
}
113110

114-
foreach ($this->phpStanTypeHelper->getTypes($tagDocNode->value, $nameScope) as $type) {
115-
switch ($type->getClassName()) {
116-
case 'self':
117-
case 'static':
118-
$resolvedClass = $class;
119-
break;
120-
121-
case 'parent':
122-
if (false !== $resolvedClass = $parentClass ??= get_parent_class($class)) {
123-
break;
124-
}
125-
// no break
126-
127-
default:
128-
$types[] = $type;
129-
continue 2;
111+
try {
112+
$types[] = $this->stringTypeResolver->resolve((string) $tagDocNode->value->type, $typeContext, $backwardCompatible);
113+
} catch (UnsupportedException) {
114+
// BC layer to handle "void" type in "getTypes"
115+
if ('void' === (string) $tagDocNode->value->type && $backwardCompatible) {
116+
return Type::template('void');
117+
}
118+
} catch (LogicException $e) {
119+
// BC layer to handle "parent" type without existing parent
120+
if ('parent' === (string) $tagDocNode->value->type && $backwardCompatible) {
121+
return Type::object('parent');
130122
}
131123

132-
$types[] = new Type(Type::BUILTIN_TYPE_OBJECT, $type->isNullable(), $resolvedClass, $type->isCollection(), $type->getCollectionKeyTypes(), $type->getCollectionValueTypes());
124+
throw $e;
133125
}
134126
}
135127

136-
if (!isset($types[0])) {
128+
if (null === ($type = $types[0] ?? null)) {
137129
return null;
138130
}
139131

140-
if (!\in_array($prefix, $this->arrayMutatorPrefixes, true)) {
141-
return $types;
132+
if (!\in_array($prefix, $this->arrayMutatorPrefixes)) {
133+
return $type;
142134
}
143135

144-
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $types[0])];
136+
return Type::list($type);
145137
}
146138

147-
public function getTypesFromConstructor(string $class, string $property): ?array
139+
public function getTypes(string $class, string $property, array $context = []): ?array
140+
{
141+
trigger_deprecation('symfony/property-info', '7.1', 'The "%s()" method is deprecated. Use "%s::getType()" instead.', __METHOD__, self::class);
142+
143+
return BackwardCompatibilityHelper::convertTypeToLegacyTypes($this->getType($class, $property, $context, true));
144+
}
145+
146+
public function getTypeFromConstructor(string $class, string $property): ?Type
148147
{
148+
$backwardCompatible = \func_get_args()[2] ?? false;
149+
149150
if (null === $tagDocNode = $this->getDocBlockFromConstructor($class, $property)) {
150151
return null;
151152
}
152153

153-
$types = [];
154-
foreach ($this->phpStanTypeHelper->getTypes($tagDocNode, $this->nameScopeFactory->create($class)) as $type) {
155-
$types[] = $type;
156-
}
154+
$typeContext = $this->typeContextFactory->createFromClassName($class);
157155

158-
if (!isset($types[0])) {
159-
return null;
160-
}
156+
return $this->stringTypeResolver->resolve((string) $tagDocNode->type, $typeContext, $backwardCompatible);
157+
}
158+
159+
public function getTypesFromConstructor(string $class, string $property): ?array
160+
{
161+
trigger_deprecation('symfony/property-info', '7.1', 'The "%s()" method is deprecated. Use "%s::getTypeFromConstructor()" instead.', __METHOD__, self::class);
161162

162-
return $types;
163+
return BackwardCompatibilityHelper::convertTypeToLegacyTypes($this->getTypeFromConstructor($class, $property, true));
163164
}
164165

165166
private function getDocBlockFromConstructor(string $class, string $property): ?ParamTagValueNode

0 commit comments

Comments
 (0)