From e3ec55fb65503cf11208dbbe8af1503480e3438f Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Fri, 5 Jan 2024 10:18:02 +0100 Subject: [PATCH] [PropertyInfo] restrict access to PhpStanExtractor based on visibility --- .../Component/PropertyInfo/CHANGELOG.md | 1 + .../Extractor/PhpStanExtractor.php | 18 +++++++++++--- .../Tests/Extractor/PhpStanExtractorTest.php | 21 ++++++++++++++++ ...mmyPropertyAndGetterWithDifferentTypes.php | 24 +++++++++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyPropertyAndGetterWithDifferentTypes.php 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; + } +}