diff --git a/src/Symfony/Component/PropertyInfo/CHANGELOG.md b/src/Symfony/Component/PropertyInfo/CHANGELOG.md index 6e0a2ff449dec..298c64357cfab 100644 --- a/src/Symfony/Component/PropertyInfo/CHANGELOG.md +++ b/src/Symfony/Component/PropertyInfo/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Introduce `PropertyDocBlockExtractorInterface` to extract a property's doc block + * Restrict access to `PhpStanExtractor` based on visibility 6.4 --- diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpStanExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpStanExtractor.php index 5762e58e32971..119ee08cd1d1d 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/PhpStanExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpStanExtractor.php @@ -53,7 +53,7 @@ final class PhpStanExtractor implements PropertyTypeExtractorInterface, Construc * @param list|null $accessorPrefixes * @param list|null $arrayMutatorPrefixes */ - public function __construct(?array $mutatorPrefixes = null, ?array $accessorPrefixes = null, ?array $arrayMutatorPrefixes = null) + public function __construct(?array $mutatorPrefixes = null, ?array $accessorPrefixes = null, ?array $arrayMutatorPrefixes = null, private bool $allowPrivateAccess = true) { if (!class_exists(ContextFactory::class)) { throw new \LogicException(sprintf('Unable to use the "%s" class as the "phpdocumentor/type-resolver" package is not installed. Try running composer require "phpdocumentor/type-resolver".', __CLASS__)); @@ -232,6 +232,10 @@ private function getDocBlockFromProperty(string $class, string $property): ?arra return null; } + if (!$this->canAccessMemberBasedOnItsVisibility($reflectionProperty)) { + return null; + } + // Type can be inside property docblock as `@var` $rawDocNode = $reflectionProperty->getDocComment(); $phpDocNode = $rawDocNode ? $this->getPhpDocNode($rawDocNode) : null; @@ -274,8 +278,11 @@ private function getDocBlockFromMethod(string $class, string $ucFirstProperty, i } if ( - (self::ACCESSOR === $type && 0 === $reflectionMethod->getNumberOfRequiredParameters()) - || (self::MUTATOR === $type && $reflectionMethod->getNumberOfParameters() >= 1) + ( + (self::ACCESSOR === $type && 0 === $reflectionMethod->getNumberOfRequiredParameters()) + || (self::MUTATOR === $type && $reflectionMethod->getNumberOfParameters() >= 1) + ) + && $this->canAccessMemberBasedOnItsVisibility($reflectionMethod) ) { break; } @@ -305,4 +312,9 @@ private function getPhpDocNode(string $rawDocNode): PhpDocNode return $phpDocNode; } + + private function canAccessMemberBasedOnItsVisibility(\ReflectionProperty|\ReflectionMethod $member): bool + { + return $this->allowPrivateAccess || $member->isPublic(); + } } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php index 06fdd9104e60c..f3754a3d9296b 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php @@ -17,6 +17,7 @@ use Symfony\Component\PropertyInfo\Tests\Fixtures\ConstructorDummyWithoutDocBlock; use Symfony\Component\PropertyInfo\Tests\Fixtures\DefaultValue; use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy; +use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyPropertyAndGetterWithDifferentTypes; use Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy; use Symfony\Component\PropertyInfo\Tests\Fixtures\Php80Dummy; use Symfony\Component\PropertyInfo\Tests\Fixtures\Php80PromotedDummy; @@ -475,6 +476,26 @@ public static function php80TypesProvider() [Php80PromotedDummy::class, 'promoted', null], ]; } + + public static function allowPrivateAccessProvider(): array + { + return [ + [true, [new Type(Type::BUILTIN_TYPE_STRING)]], + [false, [new Type(Type::BUILTIN_TYPE_ARRAY, collection: true, collectionKeyType: new Type('int'), collectionValueType: new Type('string'))]], + ]; + } + + /** + * @dataProvider allowPrivateAccessProvider + */ + public function testAllowPrivateAccess(bool $allowPrivateAccess, array $expectedTypes) + { + $extractor = new PhpStanExtractor(allowPrivateAccess: $allowPrivateAccess); + $this->assertEquals( + $expectedTypes, + $extractor->getTypes(DummyPropertyAndGetterWithDifferentTypes::class, 'foo') + ); + } } class PhpStanOmittedParamTagTypeDocBlock diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyPropertyAndGetterWithDifferentTypes.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyPropertyAndGetterWithDifferentTypes.php new file mode 100644 index 0000000000000..0e08a16dd5591 --- /dev/null +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyPropertyAndGetterWithDifferentTypes.php @@ -0,0 +1,24 @@ + + */ + public function getFoo(): array + { + return (array)$this->foo; + } +}