Skip to content

Navigation Menu

Sign in
Appearance settings
Sign up
Appearance settings

Commit 12d4a45

Browse filesBrowse the repository at this point in the historyBrowse files
Merge branch '7.3' into 7.4
* 7.3: [PropertyInfo] Conflict with phpdocumentor/reflection-docblock >= 6 [PropertyInfo] Fix resolution of self/parent types in inherited DocBlocks [PropertyInfo] Fix resolution of self/parent types in inherited DocBlocks chore(translation): remove state needs-review-translation fixes symfony#59413
2 parents b93ee45 + ab43c05 commit 12d4a45
Copy full SHA for 12d4a45

10 files changed

+255-63Lines changed: 255 additions & 63 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎composer.json‎

Copy file name to clipboardExpand all lines: composer.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@
180180
"doctrine/orm": "<2.15",
181181
"egulias/email-validator": "~3.0.0",
182182
"masterminds/html5": "<2.6",
183-
"phpdocumentor/reflection-docblock": "<5.2",
183+
"phpdocumentor/reflection-docblock": "<5.2|>=6",
184184
"phpdocumentor/type-resolver": "<1.5.1",
185185
"phpunit/phpunit": "<7.5|9.1.2",
186186
"symfony/flex": "<2.10"
Collapse file

‎src/Symfony/Bridge/Twig/composer.json‎

Copy file name to clipboardExpand all lines: src/Symfony/Bridge/Twig/composer.json
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"require-dev": {
2525
"egulias/email-validator": "^2.1.10|^3|^4",
2626
"league/html-to-markdown": "^5.0",
27-
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
27+
"phpdocumentor/reflection-docblock": "^5.2",
2828
"symfony/asset": "^6.4|^7.0|^8.0",
2929
"symfony/asset-mapper": "^6.4|^7.0|^8.0",
3030
"symfony/dependency-injection": "^6.4|^7.0|^8.0",
@@ -57,8 +57,8 @@
5757
"twig/markdown-extra": "^3"
5858
},
5959
"conflict": {
60-
"phpdocumentor/reflection-docblock": "<3.2.2",
61-
"phpdocumentor/type-resolver": "<1.4.0",
60+
"phpdocumentor/reflection-docblock": "<5.2|>=6",
61+
"phpdocumentor/type-resolver": "<1.5.1",
6262
"symfony/console": "<6.4",
6363
"symfony/form": "<6.4.32|>7,<7.3.10|>7.4,<7.4.4|>8.0,<8.0.4",
6464
"symfony/http-foundation": "<6.4",
Collapse file

‎src/Symfony/Bundle/FrameworkBundle/composer.json‎

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/composer.json
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"require-dev": {
3737
"doctrine/persistence": "^1.3|^2|^3",
3838
"dragonmantank/cron-expression": "^3.1",
39+
"phpdocumentor/reflection-docblock": "^5.2",
3940
"seld/jsonlint": "^1.10",
4041
"symfony/asset": "^6.4|^7.0|^8.0",
4142
"symfony/asset-mapper": "^6.4|^7.0|^8.0",
@@ -76,13 +77,12 @@
7677
"symfony/uid": "^6.4|^7.0|^8.0",
7778
"symfony/web-link": "^6.4|^7.0|^8.0",
7879
"symfony/webhook": "^7.2|^8.0",
79-
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
8080
"twig/twig": "^3.12"
8181
},
8282
"conflict": {
8383
"doctrine/persistence": "<1.3",
84-
"phpdocumentor/reflection-docblock": "<3.2.2",
85-
"phpdocumentor/type-resolver": "<1.4.0",
84+
"phpdocumentor/reflection-docblock": "<5.2|>=6",
85+
"phpdocumentor/type-resolver": "<1.5.1",
8686
"symfony/asset": "<6.4",
8787
"symfony/asset-mapper": "<6.4",
8888
"symfony/clock": "<6.4",
Collapse file

‎src/Symfony/Component/Mime/composer.json‎

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mime/composer.json
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"require-dev": {
2525
"egulias/email-validator": "^2.1.10|^3.1|^4",
2626
"league/html-to-markdown": "^5.0",
27-
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
27+
"phpdocumentor/reflection-docblock": "^5.2",
2828
"symfony/dependency-injection": "^6.4|^7.0|^8.0",
2929
"symfony/process": "^6.4|^7.0|^8.0",
3030
"symfony/property-access": "^6.4|^7.0|^8.0",
@@ -33,8 +33,8 @@
3333
},
3434
"conflict": {
3535
"egulias/email-validator": "~3.0.0",
36-
"phpdocumentor/reflection-docblock": "<3.2.2",
37-
"phpdocumentor/type-resolver": "<1.4.0",
36+
"phpdocumentor/reflection-docblock": "<5.2|>=6",
37+
"phpdocumentor/type-resolver": "<1.5.1",
3838
"symfony/mailer": "<6.4",
3939
"symfony/serializer": "<6.4.3|>7.0,<7.0.3"
4040
},
Collapse file

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php
+35-27Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
namespace Symfony\Component\PropertyInfo\Extractor;
1313

1414
use phpDocumentor\Reflection\DocBlock;
15-
use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod;
16-
use phpDocumentor\Reflection\DocBlock\Tags\Generic;
1715
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
1816
use phpDocumentor\Reflection\DocBlockFactory;
1917
use phpDocumentor\Reflection\DocBlockFactoryInterface;
@@ -43,7 +41,7 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property
4341
public const MUTATOR = 2;
4442

4543
/**
46-
* @var array<string, array{DocBlock|null, int|null, string|null}>
44+
* @var array<string, array{DocBlock|null, int|null, string|null, string|null}>
4745
*/
4846
private array $docBlocks = [];
4947

@@ -71,10 +69,6 @@ public function __construct(?DocBlockFactoryInterface $docBlockFactory = null, ?
7169
throw new \LogicException(\sprintf('Unable to use the "%s" class as the "phpdocumentor/reflection-docblock" package is not installed. Try running composer require "phpdocumentor/reflection-docblock".', __CLASS__));
7270
}
7371

74-
if (!is_subclass_of(Generic::class, StaticMethod::class)) {
75-
throw new \LogicException('symfony/property-info v6 does not support phpdocumentor/reflection-docblock v6. Please stick to ^5.2 in your composer.json file.');
76-
}
77-
7872
$this->docBlockFactory = $docBlockFactory ?: DocBlockFactory::createInstance();
7973
$this->contextFactory = new ContextFactory();
8074
$this->typeContextFactory = new TypeContextFactory();
@@ -129,7 +123,8 @@ public function getTypes(string $class, string $property, array $context = []):
129123
{
130124
trigger_deprecation('symfony/property-info', '7.3', 'The "%s()" method is deprecated, use "%s::getType()" instead.', __METHOD__, self::class);
131125

132-
[$docBlock, $source, $prefix] = $this->findDocBlock($class, $property);
126+
/** @var DocBlock $docBlock */
127+
[$docBlock, $source, $prefix, $declaringClass] = $this->findDocBlock($class, $property);
133128
if (!$docBlock) {
134129
return null;
135130
}
@@ -148,12 +143,15 @@ public function getTypes(string $class, string $property, array $context = []):
148143
foreach ($this->phpDocTypeHelper->getTypes($tag->getType()) as $type) {
149144
switch ($type->getClassName()) {
150145
case 'self':
146+
$resolvedClass = $declaringClass ?? $class;
147+
break;
148+
151149
case 'static':
152150
$resolvedClass = $class;
153151
break;
154152

155153
case 'parent':
156-
if (false !== $resolvedClass = $parentClass ??= get_parent_class($class)) {
154+
if (false !== $resolvedClass = $parentClass ??= get_parent_class($declaringClass ?? $class)) {
157155
break;
158156
}
159157
// no break
@@ -210,7 +208,7 @@ public function getTypesFromConstructor(string $class, string $property): ?array
210208
public function getType(string $class, string $property, array $context = []): ?Type
211209
{
212210
/** @var DocBlock $docBlock */
213-
[$docBlock, $source, $prefix] = $this->findDocBlock($class, $property);
211+
[$docBlock, $source, $prefix, $declaringClass] = $this->findDocBlock($class, $property);
214212
if (!$docBlock) {
215213
return null;
216214
}
@@ -222,7 +220,7 @@ public function getType(string $class, string $property, array $context = []): ?
222220
};
223221

224222
$types = [];
225-
$typeContext = $this->typeContextFactory->createFromClassName($class);
223+
$typeContext = $this->typeContextFactory->createFromClassName($class, $declaringClass ?? $class);
226224

227225
/** @var DocBlock\Tags\Var_|DocBlock\Tags\Return_|DocBlock\Tags\Param $tag */
228226
foreach ($docBlock->getTagsByName($tag) as $tag) {
@@ -322,7 +320,7 @@ private function filterDocBlockParams(DocBlock $docBlock, string $allowedParam):
322320
}
323321

324322
/**
325-
* @return array{DocBlock|null, int|null, string|null}
323+
* @return array{DocBlock|null, int|null, string|null, string|null}
326324
*/
327325
private function findDocBlock(string $class, string $property): array
328326
{
@@ -342,30 +340,35 @@ private function findDocBlock(string $class, string $property): array
342340

343341
switch (true) {
344342
case $reflectionProperty?->isPromoted() && $docBlock = $this->getDocBlockFromConstructor($class, $property):
345-
$data = [$docBlock, self::MUTATOR, null];
343+
$data = [$docBlock, self::MUTATOR, null, $reflectionProperty->getDeclaringClass()->getName()];
346344
break;
347345

348-
case $docBlock = $this->getDocBlockFromProperty($class, $property):
349-
$data = [$docBlock, self::PROPERTY, null];
346+
case [$docBlock, $declaringClass] = $this->getDocBlockFromProperty($class, $property):
347+
$data = [$docBlock, self::PROPERTY, null, $declaringClass];
350348
break;
351349

352-
case [$docBlock] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR):
353-
$data = [$docBlock, self::ACCESSOR, null];
350+
case [$docBlock, , $declaringClass] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR):
351+
$data = [$docBlock, self::ACCESSOR, null, $declaringClass];
354352
break;
355353

356-
case [$docBlock, $prefix] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR):
357-
$data = [$docBlock, self::MUTATOR, $prefix];
354+
case [$docBlock, $prefix, $declaringClass] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR):
355+
$data = [$docBlock, self::MUTATOR, $prefix, $declaringClass];
358356
break;
359357

360358
default:
361-
$data = [null, null, null];
359+
$data = [null, null, null, null];
362360
}
363361

364362
return $this->docBlocks[$propertyHash] = $data;
365363
}
366364

367-
private function getDocBlockFromProperty(string $class, string $property): ?DocBlock
365+
/**
366+
* @return array{DocBlock, string}|null
367+
*/
368+
private function getDocBlockFromProperty(string $class, string $property, ?string $originalClass = null): ?array
368369
{
370+
$originalClass ??= $class;
371+
369372
// Use a ReflectionProperty instead of $class to get the parent class if applicable
370373
try {
371374
$reflectionProperty = new \ReflectionProperty($class, $property);
@@ -377,22 +380,25 @@ private function getDocBlockFromProperty(string $class, string $property): ?DocB
377380

378381
foreach ($reflector->getTraits() as $trait) {
379382
if ($trait->hasProperty($property)) {
380-
return $this->getDocBlockFromProperty($trait->getName(), $property);
383+
return $this->getDocBlockFromProperty($trait->getName(), $property, $reflector->isTrait() ? $originalClass : $reflector->getName());
381384
}
382385
}
383386

384387
try {
385-
return $this->docBlockFactory->create($reflectionProperty, $this->createFromReflector($reflector));
388+
$declaringClass = $reflector->isTrait() ? $originalClass : $reflector->getName();
389+
390+
return [$this->docBlockFactory->create($reflectionProperty, $this->createFromReflector($reflector)), $declaringClass];
386391
} catch (\InvalidArgumentException|\RuntimeException) {
387392
return null;
388393
}
389394
}
390395

391396
/**
392-
* @return array{DocBlock, string}|null
397+
* @return array{DocBlock, string, string}|null
393398
*/
394-
private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type): ?array
399+
private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type, ?string $originalClass = null): ?array
395400
{
401+
$originalClass ??= $class;
396402
$prefixes = self::ACCESSOR === $type ? $this->accessorPrefixes : $this->mutatorPrefixes;
397403
$prefix = null;
398404
$method = null;
@@ -429,12 +435,14 @@ private function getDocBlockFromMethod(string $class, string $ucFirstProperty, i
429435

430436
foreach ($reflector->getTraits() as $trait) {
431437
if ($trait->hasMethod($methodName)) {
432-
return $this->getDocBlockFromMethod($trait->getName(), $ucFirstProperty, $type);
438+
return $this->getDocBlockFromMethod($trait->getName(), $ucFirstProperty, $type, $reflector->isTrait() ? $originalClass : $reflector->getName());
433439
}
434440
}
435441

436442
try {
437-
return [$this->docBlockFactory->create($method, $this->createFromReflector($reflector)), $prefix];
443+
$declaringClass = $reflector->isTrait() ? $originalClass : $reflector->getName();
444+
445+
return [$this->docBlockFactory->create($method, $this->createFromReflector($reflector)), $prefix, $declaringClass];
438446
} catch (\InvalidArgumentException|\RuntimeException) {
439447
return null;
440448
}
Collapse file

‎src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php‎

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
+75Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@
2323
use Symfony\Component\PropertyInfo\Tests\Fixtures\DockBlockFallback;
2424
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
2525
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyCollection;
26+
use Symfony\Component\PropertyInfo\Tests\Fixtures\Extractor\ChildOfParentUsingTrait;
27+
use Symfony\Component\PropertyInfo\Tests\Fixtures\Extractor\ChildOfParentWithPromotedSelfDocBlock;
28+
use Symfony\Component\PropertyInfo\Tests\Fixtures\Extractor\ChildWithSelfDocBlock;
29+
use Symfony\Component\PropertyInfo\Tests\Fixtures\Extractor\ClassUsingNestedTrait;
30+
use Symfony\Component\PropertyInfo\Tests\Fixtures\Extractor\ClassUsingTraitWithSelfDocBlock;
31+
use Symfony\Component\PropertyInfo\Tests\Fixtures\Extractor\ParentUsingTraitWithSelfDocBlock;
32+
use Symfony\Component\PropertyInfo\Tests\Fixtures\Extractor\ParentWithPromotedSelfDocBlock;
33+
use Symfony\Component\PropertyInfo\Tests\Fixtures\Extractor\ParentWithSelfDocBlock;
2634
use Symfony\Component\PropertyInfo\Tests\Fixtures\InvalidDummy;
2735
use Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy;
2836
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php80Dummy;
@@ -819,6 +827,39 @@ public function testPropertiesParentType(string $class, string $property, ?Type
819827
$this->assertEquals($type, $this->extractor->getType($class, $property));
820828
}
821829

830+
/**
831+
* @param class-string $class
832+
* @param class-string $expectedResolvedClass
833+
*
834+
* @dataProvider selfDocBlockResolutionProvider
835+
*/
836+
public function testSelfDocBlockResolvesToDeclaringClass(string $class, string $property, string $expectedResolvedClass)
837+
{
838+
$this->assertEquals(Type::object($expectedResolvedClass), $this->extractor->getType($class, $property));
839+
}
840+
841+
/**
842+
* @return iterable<string, array{0: class-string, 1: string, 2: class-string}>
843+
*/
844+
public static function selfDocBlockResolutionProvider(): iterable
845+
{
846+
yield 'parent property' => [ParentWithSelfDocBlock::class, 'selfProp', ParentWithSelfDocBlock::class];
847+
yield 'parent property from child' => [ChildWithSelfDocBlock::class, 'selfProp', ParentWithSelfDocBlock::class];
848+
yield 'parent accessor' => [ParentWithSelfDocBlock::class, 'selfAccessor', ParentWithSelfDocBlock::class];
849+
yield 'parent accessor from child' => [ChildWithSelfDocBlock::class, 'selfAccessor', ParentWithSelfDocBlock::class];
850+
yield 'parent mutator' => [ParentWithSelfDocBlock::class, 'selfMutator', ParentWithSelfDocBlock::class];
851+
yield 'parent mutator from child' => [ChildWithSelfDocBlock::class, 'selfMutator', ParentWithSelfDocBlock::class];
852+
yield 'trait property' => [ClassUsingTraitWithSelfDocBlock::class, 'selfTraitProp', ClassUsingTraitWithSelfDocBlock::class];
853+
yield 'trait accessor' => [ClassUsingTraitWithSelfDocBlock::class, 'selfTraitAccessor', ClassUsingTraitWithSelfDocBlock::class];
854+
yield 'trait mutator' => [ClassUsingTraitWithSelfDocBlock::class, 'selfTraitMutator', ClassUsingTraitWithSelfDocBlock::class];
855+
yield 'trait property from child' => [ChildOfParentUsingTrait::class, 'selfTraitProp', ParentUsingTraitWithSelfDocBlock::class];
856+
yield 'trait accessor from child' => [ChildOfParentUsingTrait::class, 'selfTraitAccessor', ParentUsingTraitWithSelfDocBlock::class];
857+
yield 'trait mutator from child' => [ChildOfParentUsingTrait::class, 'selfTraitMutator', ParentUsingTraitWithSelfDocBlock::class];
858+
yield 'nested trait property' => [ClassUsingNestedTrait::class, 'innerSelfProp', ClassUsingNestedTrait::class];
859+
yield 'promoted property' => [ParentWithPromotedSelfDocBlock::class, 'promotedSelfProp', ParentWithPromotedSelfDocBlock::class];
860+
yield 'promoted property from child' => [ChildOfParentWithPromotedSelfDocBlock::class, 'promotedSelfProp', ParentWithPromotedSelfDocBlock::class];
861+
}
862+
822863
/**
823864
* @return iterable<array{0: class-string, 1: string, 2: ?Type}>
824865
*/
@@ -932,6 +973,40 @@ public function testSkipVoidNeverReturnTypeAccessorsLegacy()
932973
$this->assertCount(1, $types);
933974
$this->assertEquals(LegacyType::BUILTIN_TYPE_STRING, $types[0]->getBuiltinType());
934975
}
976+
977+
/**
978+
* @param class-string $class
979+
* @param class-string $expectedResolvedClass
980+
*/
981+
#[IgnoreDeprecations]
982+
#[Group('legacy')]
983+
#[DataProvider('selfDocBlockResolutionProviderLegacy')]
984+
public function testSelfDocBlockResolvesToDeclaringClassLegacy(string $class, string $property, string $expectedResolvedClass)
985+
{
986+
$this->assertEquals([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, $expectedResolvedClass)], $this->extractor->getTypes($class, $property));
987+
}
988+
989+
/**
990+
* @return iterable<string, array{0: class-string, 1: string, 2: class-string}>
991+
*/
992+
public static function selfDocBlockResolutionProviderLegacy(): iterable
993+
{
994+
yield 'parent property' => [ParentWithSelfDocBlock::class, 'selfProp', ParentWithSelfDocBlock::class];
995+
yield 'parent property from child' => [ChildWithSelfDocBlock::class, 'selfProp', ParentWithSelfDocBlock::class];
996+
yield 'parent accessor' => [ParentWithSelfDocBlock::class, 'selfAccessor', ParentWithSelfDocBlock::class];
997+
yield 'parent accessor from child' => [ChildWithSelfDocBlock::class, 'selfAccessor', ParentWithSelfDocBlock::class];
998+
yield 'parent mutator' => [ParentWithSelfDocBlock::class, 'selfMutator', ParentWithSelfDocBlock::class];
999+
yield 'parent mutator from child' => [ChildWithSelfDocBlock::class, 'selfMutator', ParentWithSelfDocBlock::class];
1000+
yield 'trait property' => [ClassUsingTraitWithSelfDocBlock::class, 'selfTraitProp', ClassUsingTraitWithSelfDocBlock::class];
1001+
yield 'trait accessor' => [ClassUsingTraitWithSelfDocBlock::class, 'selfTraitAccessor', ClassUsingTraitWithSelfDocBlock::class];
1002+
yield 'trait mutator' => [ClassUsingTraitWithSelfDocBlock::class, 'selfTraitMutator', ClassUsingTraitWithSelfDocBlock::class];
1003+
yield 'trait property from child' => [ChildOfParentUsingTrait::class, 'selfTraitProp', ParentUsingTraitWithSelfDocBlock::class];
1004+
yield 'trait accessor from child' => [ChildOfParentUsingTrait::class, 'selfTraitAccessor', ParentUsingTraitWithSelfDocBlock::class];
1005+
yield 'trait mutator from child' => [ChildOfParentUsingTrait::class, 'selfTraitMutator', ParentUsingTraitWithSelfDocBlock::class];
1006+
yield 'nested trait property' => [ClassUsingNestedTrait::class, 'innerSelfProp', ClassUsingNestedTrait::class];
1007+
yield 'promoted property' => [ParentWithPromotedSelfDocBlock::class, 'promotedSelfProp', ParentWithPromotedSelfDocBlock::class];
1008+
yield 'promoted property from child' => [ChildOfParentWithPromotedSelfDocBlock::class, 'promotedSelfProp', ParentWithPromotedSelfDocBlock::class];
1009+
}
9351010
}
9361011

9371012
class EmptyDocBlock

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.