Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit10fc1c4

Browse files
committed
[TypeInfo] Add PhpDocAwareReflectionTypeResolver
1 parent0f4cf9b commit10fc1c4

File tree

8 files changed

+222
-34
lines changed

8 files changed

+222
-34
lines changed

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
useComposer\InstalledVersions;
1515
useHttp\Client\HttpAsyncClient;
1616
useHttp\Client\HttpClient;
17+
useSymfony\Component\TypeInfo\TypeResolver\PhpDocAwareReflectionTypeResolver;
1718
usephpDocumentor\Reflection\DocBlockFactoryInterface;
1819
usephpDocumentor\Reflection\Types\ContextFactory;
1920
usePhpParser\Parser;
@@ -1974,11 +1975,21 @@ private function registerTypeInfoConfiguration(ContainerBuilder $container, PhpF
19741975
if (ContainerBuilder::willBeAvailable('phpstan/phpdoc-parser', PhpDocParser::class, ['symfony/framework-bundle','symfony/type-info'])) {
19751976
$container->register('type_info.resolver.string', StringTypeResolver::class);
19761977

1978+
$container->register('type_info.resolver.reflection_parameter.phpdoc_aware', PhpDocAwareReflectionTypeResolver::class)
1979+
->setArguments([newReference('type_info.resolver.reflection_parameter'),newReference('type_info.resolver.string'),newReference('type_info.type_context_factory')]);
1980+
$container->register('type_info.resolver.reflection_property.phpdoc_aware', PhpDocAwareReflectionTypeResolver::class)
1981+
->setArguments([newReference('type_info.resolver.reflection_property'),newReference('type_info.resolver.string'),newReference('type_info.type_context_factory')]);
1982+
$container->register('type_info.resolver.reflection_return.phpdoc_aware', PhpDocAwareReflectionTypeResolver::class)
1983+
->setArguments([newReference('type_info.resolver.reflection_return'),newReference('type_info.resolver.string'),newReference('type_info.type_context_factory')]);
1984+
19771985
/** @var ServiceLocatorArgument $resolversLocator */
19781986
$resolversLocator =$container->getDefinition('type_info.resolver')->getArgument(0);
1979-
$resolversLocator->setValues($resolversLocator->getValues() +[
1987+
$resolversLocator->setValues([
19801988
'string' =>newReference('type_info.resolver.string'),
1981-
]);
1989+
\ReflectionParameter::class =>newReference('type_info.resolver.reflection_parameter.phpdoc_aware'),
1990+
\ReflectionProperty::class =>newReference('type_info.resolver.reflection_property.phpdoc_aware'),
1991+
\ReflectionFunctionAbstract::class =>newReference('type_info.resolver.reflection_return.phpdoc_aware'),
1992+
] +$resolversLocator->getValues());
19821993
}
19831994
}
19841995

‎src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
useSymfony\Component\String\Inflector\InflectorInterface;
2525
useSymfony\Component\TypeInfo\Exception\UnsupportedException;
2626
useSymfony\Component\TypeInfo\Type;
27+
useSymfony\Component\TypeInfo\TypeContext\TypeContextFactory;
28+
useSymfony\Component\TypeInfo\TypeResolver\ReflectionParameterTypeResolver;
29+
useSymfony\Component\TypeInfo\TypeResolver\ReflectionPropertyTypeResolver;
30+
useSymfony\Component\TypeInfo\TypeResolver\ReflectionReturnTypeResolver;
31+
useSymfony\Component\TypeInfo\TypeResolver\ReflectionTypeResolver;
2732
useSymfony\Component\TypeInfo\Type\CollectionType;
2833
useSymfony\Component\TypeInfo\TypeIdentifier;
2934
useSymfony\Component\TypeInfo\TypeResolver\TypeResolver;
@@ -102,7 +107,14 @@ public function __construct(
102107
$this->methodReflectionFlags =$this->getMethodsFlags($accessFlags);
103108
$this->propertyReflectionFlags =$this->getPropertyFlags($accessFlags);
104109
$this->inflector =$inflector ??newEnglishInflector();
105-
$this->typeResolver = TypeResolver::create();
110+
111+
$typeContextFactory =newTypeContextFactory();
112+
$this->typeResolver = TypeResolver::create([
113+
\ReflectionType::class =>$reflectionTypeResolver =newReflectionTypeResolver(),
114+
\ReflectionParameter::class =>newReflectionParameterTypeResolver($reflectionTypeResolver,$typeContextFactory),
115+
\ReflectionProperty::class =>newReflectionPropertyTypeResolver($reflectionTypeResolver,$typeContextFactory),
116+
\ReflectionFunctionAbstract::class =>newReflectionReturnTypeResolver($reflectionTypeResolver,$typeContextFactory),
117+
]);
106118

107119
$this->arrayMutatorPrefixesFirst =array_merge($this->arrayMutatorPrefixes,array_diff($this->mutatorPrefixes,$this->arrayMutatorPrefixes));
108120
$this->arrayMutatorPrefixesLast =array_reverse($this->arrayMutatorPrefixesFirst);

‎src/Symfony/Component/TypeInfo/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.2
5+
---
6+
7+
* Add`PhpDocAwareReflectionTypeResolver` resolver
8+
49
7.1
510
---
611

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespaceSymfony\Component\TypeInfo\Tests\Fixtures;
4+
5+
finalclass DummyWithPhpDoc
6+
{
7+
/**
8+
* @var array<Dummy>
9+
*/
10+
publicmixed$arrayOfDummies = [];
11+
12+
/**
13+
* @param Dummy $dummy
14+
*
15+
* @return Dummy
16+
*/
17+
publicfunctiongetNextDummy(mixed$dummy):mixed
18+
{
19+
thrownew \BadMethodCallException(sprintf('"%s" is not implemented.',__METHOD__));
20+
}
21+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\TypeInfo\Tests\TypeResolver;
13+
14+
usePHPUnit\Framework\TestCase;
15+
useSymfony\Component\TypeInfo\Tests\Fixtures\Dummy;
16+
useSymfony\Component\TypeInfo\Tests\Fixtures\DummyWithPhpDoc;
17+
useSymfony\Component\TypeInfo\Type;
18+
useSymfony\Component\TypeInfo\TypeContext\TypeContextFactory;
19+
useSymfony\Component\TypeInfo\TypeResolver\PhpDocAwareReflectionTypeResolver;
20+
useSymfony\Component\TypeInfo\TypeResolver\StringTypeResolver;
21+
useSymfony\Component\TypeInfo\TypeResolver\TypeResolver;
22+
23+
class PhpDocAwareReflectionTypeResolverTestextends TestCase
24+
{
25+
publicfunctiontestReadPhpDoc()
26+
{
27+
$resolver =newPhpDocAwareReflectionTypeResolver(TypeResolver::create(),newStringTypeResolver(),newTypeContextFactory());
28+
$reflection =new \ReflectionClass(DummyWithPhpDoc::class);
29+
30+
$this->assertEquals(Type::array(Type::object(Dummy::class)),$resolver->resolve($reflection->getProperty('arrayOfDummies')));
31+
$this->assertEquals(Type::object(Dummy::class),$resolver->resolve($reflection->getMethod('getNextDummy')));
32+
$this->assertEquals(Type::object(Dummy::class),$resolver->resolve($reflection->getMethod('getNextDummy')->getParameters()[0]));
33+
}
34+
35+
publicfunctiontestFallbackWhenNoPhpDoc()
36+
{
37+
$resolver =newPhpDocAwareReflectionTypeResolver(TypeResolver::create(),newStringTypeResolver(),newTypeContextFactory());
38+
$reflection =new \ReflectionClass(Dummy::class);
39+
40+
$this->assertEquals(Type::int(),$resolver->resolve($reflection->getProperty('id')));
41+
$this->assertEquals(Type::int(),$resolver->resolve($reflection->getMethod('getId')));
42+
$this->assertEquals(Type::int(),$resolver->resolve($reflection->getMethod('setId')->getParameters()[0]));
43+
}
44+
}

‎src/Symfony/Component/TypeInfo/Tests/TypeResolver/TypeResolverTest.php

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespaceSymfony\Component\TypeInfo\Tests\TypeResolver;
1313

1414
usePHPUnit\Framework\TestCase;
15-
useSymfony\Component\DependencyInjection\ServiceLocator;
1615
useSymfony\Component\TypeInfo\Exception\UnsupportedException;
1716
useSymfony\Component\TypeInfo\Tests\Fixtures\Dummy;
1817
useSymfony\Component\TypeInfo\Type;
@@ -38,7 +37,7 @@ public function testCannotFindResolver()
3837
$this->expectException(UnsupportedException::class);
3938
$this->expectExceptionMessage('Cannot find any resolver for "int" type.');
4039

41-
$resolver =newTypeResolver(newServiceLocator([]));
40+
$resolver = TypeResolver::create([]);
4241
$resolver->resolve(1);
4342
}
4443

@@ -59,13 +58,13 @@ public function testUseProperResolver()
5958
$reflectionReturnTypeResolver =$this->createMock(TypeResolverInterface::class);
6059
$reflectionReturnTypeResolver->method('resolve')->willReturn(Type::template('REFLECTION_RETURN_TYPE'));
6160

62-
$resolver =newTypeResolver(newServiceLocator([
63-
'string' =>fn () =>$stringResolver,
64-
\ReflectionType::class =>fn () =>$reflectionTypeResolver,
65-
\ReflectionParameter::class =>fn () =>$reflectionParameterResolver,
66-
\ReflectionProperty::class =>fn () =>$reflectionPropertyResolver,
67-
\ReflectionFunctionAbstract::class =>fn () =>$reflectionReturnTypeResolver,
68-
]));
61+
$resolver = TypeResolver::create([
62+
'string' =>$stringResolver,
63+
\ReflectionType::class =>$reflectionTypeResolver,
64+
\ReflectionParameter::class =>$reflectionParameterResolver,
65+
\ReflectionProperty::class =>$reflectionPropertyResolver,
66+
\ReflectionFunctionAbstract::class =>$reflectionReturnTypeResolver,
67+
]);
6968

7069
$this->assertEquals(Type::template('STRING'),$resolver->resolve('foo'));
7170
$this->assertEquals(
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\TypeInfo\TypeResolver;
13+
14+
usePHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
15+
usePHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
16+
usePHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
17+
usePHPStan\PhpDocParser\Lexer\Lexer;
18+
usePHPStan\PhpDocParser\Parser\ConstExprParser;
19+
usePHPStan\PhpDocParser\Parser\PhpDocParser;
20+
usePHPStan\PhpDocParser\Parser\TokenIterator;
21+
usePHPStan\PhpDocParser\Parser\TypeParser;
22+
useSymfony\Component\TypeInfo\Exception\UnsupportedException;
23+
useSymfony\Component\TypeInfo\Type;
24+
useSymfony\Component\TypeInfo\TypeContext\TypeContext;
25+
useSymfony\Component\TypeInfo\TypeContext\TypeContextFactory;
26+
useSymfony\Component\TypeInfo\TypeResolver\TypeResolverInterface;
27+
28+
/**
29+
* Resolves type on reflection prioriziting PHP documentation.
30+
*
31+
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
32+
*
33+
* @internal
34+
*/
35+
finalreadonlyclass PhpDocAwareReflectionTypeResolverimplements TypeResolverInterface
36+
{
37+
privatePhpDocParser$phpDocParser;
38+
privateLexer$lexer;
39+
40+
publicfunction__construct(
41+
privateTypeResolverInterface$reflectionTypeResolver,
42+
privateTypeResolverInterface$stringTypeResolver,
43+
privateTypeContextFactory$typeContextFactory,
44+
) {
45+
$this->phpDocParser =newPhpDocParser(newTypeParser(),newConstExprParser());
46+
$this->lexer =newLexer();
47+
}
48+
49+
publicfunctionresolve(mixed$subject, ?TypeContext$typeContext =null):Type
50+
{
51+
if (!$subjectinstanceof \ReflectionProperty && !$subjectinstanceof \ReflectionParameter && !$subjectinstanceof \ReflectionFunctionAbstract) {
52+
thrownewUnsupportedException(sprintf('Expected subject to be a "ReflectionProperty", a "ReflectionParameter" or a "ReflectionFunctionAbstract", "%s" given.',get_debug_type($subject)),$subject);
53+
}
54+
55+
$docComment =match (true) {
56+
$subjectinstanceof \ReflectionProperty =>$subject->getDocComment(),
57+
$subjectinstanceof \ReflectionParameter =>$subject->getDeclaringFunction()->getDocComment(),
58+
$subjectinstanceof \ReflectionFunctionAbstract =>$subject->getDocComment(),
59+
};
60+
61+
if (!$docComment) {
62+
return$this->reflectionTypeResolver->resolve($subject);
63+
}
64+
65+
$typeContext ??=$this->typeContextFactory->createFromReflection($subject);
66+
67+
$tagName =match (true) {
68+
$subjectinstanceof \ReflectionProperty =>'@var',
69+
$subjectinstanceof \ReflectionParameter =>'@param',
70+
$subjectinstanceof \ReflectionFunctionAbstract =>'@return',
71+
};
72+
73+
$tokens =newTokenIterator($this->lexer->tokenize($docComment));
74+
$docNode =$this->phpDocParser->parse($tokens);
75+
76+
foreach ($docNode->getTagsByName($tagName)as$tag) {
77+
$tagValue =$tag->value;
78+
79+
if (
80+
$tagValueinstanceof VarTagValueNode
81+
||$tagValueinstanceof ParamTagValueNode &&$tagName &&'$'.$subject->getName() ===$tagValue->parameterName
82+
||$tagValueinstanceof ReturnTagValueNode
83+
) {
84+
return$this->stringTypeResolver->resolve((string)$tagValue,$typeContext);
85+
}
86+
}
87+
88+
return$this->reflectionTypeResolver->resolve($subject);
89+
}
90+
}

‎src/Symfony/Component/TypeInfo/TypeResolver/TypeResolver.php

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -61,29 +61,35 @@ public function resolve(mixed $subject, ?TypeContext $typeContext = null): Type
6161
return$resolver->resolve($subject,$typeContext);
6262
}
6363

64-
publicstaticfunctioncreate():self
64+
/**
65+
* @param array<string, TypeResolverInterface>|null $resolvers
66+
*/
67+
publicstaticfunctioncreate(?array$resolvers =null):self
6568
{
66-
$resolvers =newclass()implements ContainerInterface {
67-
privatereadonlyarray$resolvers;
69+
if (null ===$resolvers) {
70+
$stringTypeResolver =class_exists(PhpDocParser::class) ?newStringTypeResolver() :null;
71+
$typeContextFactory =newTypeContextFactory($stringTypeResolver);
72+
$reflectionTypeResolver =newReflectionTypeResolver();
73+
74+
$resolvers = [
75+
\ReflectionType::class =>$reflectionTypeResolver,
76+
\ReflectionParameter::class =>newReflectionParameterTypeResolver($reflectionTypeResolver,$typeContextFactory),
77+
\ReflectionProperty::class =>newReflectionPropertyTypeResolver($reflectionTypeResolver,$typeContextFactory),
78+
\ReflectionFunctionAbstract::class =>newReflectionReturnTypeResolver($reflectionTypeResolver,$typeContextFactory),
79+
];
80+
81+
if (null !==$stringTypeResolver) {
82+
$resolvers['string'] =$stringTypeResolver;
83+
$resolvers[\ReflectionParameter::class] =newPhpDocAwareReflectionTypeResolver($resolvers[\ReflectionParameter::class],$stringTypeResolver,$typeContextFactory);
84+
$resolvers[\ReflectionProperty::class] =newPhpDocAwareReflectionTypeResolver($resolvers[\ReflectionProperty::class],$stringTypeResolver,$typeContextFactory);
85+
$resolvers[\ReflectionFunctionAbstract::class] =newPhpDocAwareReflectionTypeResolver($resolvers[\ReflectionFunctionAbstract::class],$stringTypeResolver,$typeContextFactory);
86+
}
87+
}
6888

69-
publicfunction__construct()
70-
{
71-
$stringTypeResolver =class_exists(PhpDocParser::class) ?newStringTypeResolver() :null;
72-
$typeContextFactory =newTypeContextFactory($stringTypeResolver);
73-
$reflectionTypeResolver =newReflectionTypeResolver();
74-
75-
$resolvers = [
76-
\ReflectionType::class =>$reflectionTypeResolver,
77-
\ReflectionParameter::class =>newReflectionParameterTypeResolver($reflectionTypeResolver,$typeContextFactory),
78-
\ReflectionProperty::class =>newReflectionPropertyTypeResolver($reflectionTypeResolver,$typeContextFactory),
79-
\ReflectionFunctionAbstract::class =>newReflectionReturnTypeResolver($reflectionTypeResolver,$typeContextFactory),
80-
];
81-
82-
if (null !==$stringTypeResolver) {
83-
$resolvers['string'] =$stringTypeResolver;
84-
}
85-
86-
$this->resolvers =$resolvers;
89+
$resolversContainer =newclass($resolvers)implements ContainerInterface {
90+
publicfunction__construct(
91+
privatereadonlyarray$resolvers,
92+
) {
8793
}
8894

8995
publicfunctionhas(string$id):bool
@@ -97,6 +103,6 @@ public function get(string $id): TypeResolverInterface
97103
}
98104
};
99105

100-
returnnewself($resolvers);
106+
returnnewself($resolversContainer);
101107
}
102108
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp