From 7b57d5883105def39da257ffe73f873d3d9e0990 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 24 Jan 2023 15:02:24 +0100 Subject: [PATCH 1/8] Update license years (last time) --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 63af57a..6e3afce 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-2023 Fabien Potencier +Copyright (c) 2015-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 31e33a6a8d1ccb12d9846866e2515970bd7f52e1 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 29 Jan 2023 18:04:53 +0100 Subject: [PATCH 2/8] fail with a meaningful error when a needed package is missing --- Extractor/PhpDocExtractor.php | 2 +- Extractor/PhpStanExtractor.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Extractor/PhpDocExtractor.php b/Extractor/PhpDocExtractor.php index ef1967b..2cecfcf 100644 --- a/Extractor/PhpDocExtractor.php +++ b/Extractor/PhpDocExtractor.php @@ -60,7 +60,7 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property public function __construct(DocBlockFactoryInterface $docBlockFactory = null, array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null) { if (!class_exists(DocBlockFactory::class)) { - throw new \LogicException(sprintf('Unable to use the "%s" class as the "phpdocumentor/reflection-docblock" package is not installed.', __CLASS__)); + 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__)); } $this->docBlockFactory = $docBlockFactory ?: DocBlockFactory::createInstance(); diff --git a/Extractor/PhpStanExtractor.php b/Extractor/PhpStanExtractor.php index f833731..66c5882 100644 --- a/Extractor/PhpStanExtractor.php +++ b/Extractor/PhpStanExtractor.php @@ -11,6 +11,7 @@ namespace Symfony\Component\PropertyInfo\Extractor; +use phpDocumentor\Reflection\Types\ContextFactory; use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode; @@ -59,6 +60,10 @@ final class PhpStanExtractor implements PropertyTypeExtractorInterface, Construc */ public function __construct(array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null) { + 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__)); + } + $this->phpStanTypeHelper = new PhpStanTypeHelper(); $this->mutatorPrefixes = $mutatorPrefixes ?? ReflectionExtractor::$defaultMutatorPrefixes; $this->accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes; From 9a44c261710209a3fc215c5e3cce6059f829893b Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 2 Feb 2023 21:06:30 +0100 Subject: [PATCH 3/8] [PropertyInfo] Add meaningful message when `phpstan/phpdoc-parser` is not installed when using `PhpStanExtractor` --- Extractor/PhpStanExtractor.php | 4 ++++ Tests/Extractor/PhpDocExtractorTest.php | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Extractor/PhpStanExtractor.php b/Extractor/PhpStanExtractor.php index 66c5882..52a43d1 100644 --- a/Extractor/PhpStanExtractor.php +++ b/Extractor/PhpStanExtractor.php @@ -64,6 +64,10 @@ public function __construct(array $mutatorPrefixes = null, array $accessorPrefix 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__)); } + if (!class_exists(PhpDocParser::class)) { + 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__)); + } + $this->phpStanTypeHelper = new PhpStanTypeHelper(); $this->mutatorPrefixes = $mutatorPrefixes ?? ReflectionExtractor::$defaultMutatorPrefixes; $this->accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes; diff --git a/Tests/Extractor/PhpDocExtractorTest.php b/Tests/Extractor/PhpDocExtractorTest.php index f053832..c6a02b5 100644 --- a/Tests/Extractor/PhpDocExtractorTest.php +++ b/Tests/Extractor/PhpDocExtractorTest.php @@ -53,7 +53,7 @@ public function testParamTagTypeIsOmitted() $this->assertNull($this->extractor->getTypes(OmittedParamTagTypeDocBlock::class, 'omittedType')); } - public function invalidTypesProvider() + public static function invalidTypesProvider() { return [ 'pub' => ['pub', null, null], @@ -83,7 +83,7 @@ public function testExtractTypesWithNoPrefixes($property, array $type = null) $this->assertEquals($type, $noPrefixExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); } - public function typesProvider() + public static function typesProvider() { return [ ['foo', null, 'Short description.', 'Long description.'], @@ -166,7 +166,7 @@ public function testExtractCollection($property, array $type = null, $shortDescr $this->testExtract($property, $type, $shortDescription, $longDescription); } - public function provideCollectionTypes() + public static function provideCollectionTypes() { return [ ['iteratorCollection', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Iterator', true, null, new Type(Type::BUILTIN_TYPE_STRING))], null, null], @@ -230,7 +230,7 @@ public function testExtractTypesWithCustomPrefixes($property, array $type = null $this->assertEquals($type, $customExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); } - public function typesWithCustomPrefixesProvider() + public static function typesWithCustomPrefixesProvider() { return [ ['foo', null, 'Short description.', 'Long description.'], @@ -271,7 +271,7 @@ public function typesWithCustomPrefixesProvider() ]; } - public function typesWithNoPrefixesProvider() + public static function typesWithNoPrefixesProvider() { return [ ['foo', null, 'Short description.', 'Long description.'], @@ -317,7 +317,7 @@ public function testReturnNullOnEmptyDocBlock() $this->assertNull($this->extractor->getShortDescription(EmptyDocBlock::class, 'foo')); } - public function dockBlockFallbackTypesProvider() + public static function dockBlockFallbackTypesProvider() { return [ 'pub' => [ @@ -348,7 +348,7 @@ public function testPropertiesDefinedByTraits(string $property, Type $type) $this->assertEquals([$type], $this->extractor->getTypes(DummyUsingTrait::class, $property)); } - public function propertiesDefinedByTraitsProvider(): array + public static function propertiesDefinedByTraitsProvider(): array { return [ ['propertyInTraitPrimitiveType', new Type(Type::BUILTIN_TYPE_STRING)], @@ -368,7 +368,7 @@ public function testMethodsDefinedByTraits(string $property, Type $type) $this->assertEquals([$type], $this->extractor->getTypes(DummyUsingTrait::class, $property)); } - public function methodsDefinedByTraitsProvider(): array + public static function methodsDefinedByTraitsProvider(): array { return [ ['methodInTraitPrimitiveType', new Type(Type::BUILTIN_TYPE_STRING)], @@ -388,7 +388,7 @@ public function testPropertiesStaticType(string $class, string $property, Type $ $this->assertEquals([$type], $this->extractor->getTypes($class, $property)); } - public function propertiesStaticTypeProvider(): array + public static function propertiesStaticTypeProvider(): array { return [ [ParentDummy::class, 'propertyTypeStatic', new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)], @@ -404,7 +404,7 @@ public function testPropertiesParentType(string $class, string $property, ?array $this->assertEquals($types, $this->extractor->getTypes($class, $property)); } - public function propertiesParentTypeProvider(): array + public static function propertiesParentTypeProvider(): array { return [ [ParentDummy::class, 'parentAnnotationNoParent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'parent')]], @@ -435,7 +435,7 @@ public function testExtractConstructorTypes($property, array $type = null) $this->assertEquals($type, $this->extractor->getTypesFromConstructor('Symfony\Component\PropertyInfo\Tests\Fixtures\ConstructorDummy', $property)); } - public function constructorTypesProvider() + public static function constructorTypesProvider() { return [ ['date', [new Type(Type::BUILTIN_TYPE_INT)]], From 38ceb9e1c54de5071db520ee976ca8d12debfb5a Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 14 Dec 2022 15:42:16 +0100 Subject: [PATCH 4/8] Migrate to `static` data providers using `rector/rector` --- .../PropertyInfoPassTest.php | 2 +- Tests/Extractor/PhpStanExtractorTest.php | 24 ++++++++-------- Tests/Extractor/ReflectionExtractorTest.php | 28 +++++++++---------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Tests/DependencyInjection/PropertyInfoPassTest.php b/Tests/DependencyInjection/PropertyInfoPassTest.php index aea826e..a1db482 100644 --- a/Tests/DependencyInjection/PropertyInfoPassTest.php +++ b/Tests/DependencyInjection/PropertyInfoPassTest.php @@ -42,7 +42,7 @@ public function testServicesAreOrderedAccordingToPriority($index, $tag) $this->assertEquals($expected, $definition->getArgument($index)); } - public function provideTags() + public static function provideTags() { return [ [0, 'property_info.list_extractor'], diff --git a/Tests/Extractor/PhpStanExtractorTest.php b/Tests/Extractor/PhpStanExtractorTest.php index 467c076..a517e7a 100644 --- a/Tests/Extractor/PhpStanExtractorTest.php +++ b/Tests/Extractor/PhpStanExtractorTest.php @@ -58,7 +58,7 @@ public function testParamTagTypeIsOmitted() $this->assertNull($this->extractor->getTypes(PhpStanOmittedParamTagTypeDocBlock::class, 'omittedType')); } - public function invalidTypesProvider() + public static function invalidTypesProvider() { return [ 'pub' => ['pub'], @@ -86,7 +86,7 @@ public function testExtractTypesWithNoPrefixes($property, array $type = null) $this->assertEquals($type, $noPrefixExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); } - public function typesProvider() + public static function typesProvider() { return [ ['foo', null], @@ -139,7 +139,7 @@ public function testExtractCollection($property, array $type = null) $this->testExtract($property, $type); } - public function provideCollectionTypes() + public static function provideCollectionTypes() { return [ ['iteratorCollection', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Iterator', true, null, new Type(Type::BUILTIN_TYPE_STRING))]], @@ -197,7 +197,7 @@ public function testExtractTypesWithCustomPrefixes($property, array $type = null $this->assertEquals($type, $customExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property)); } - public function typesWithCustomPrefixesProvider() + public static function typesWithCustomPrefixesProvider() { return [ ['foo', null], @@ -235,7 +235,7 @@ public function typesWithCustomPrefixesProvider() ]; } - public function typesWithNoPrefixesProvider() + public static function typesWithNoPrefixesProvider() { return [ ['foo', null], @@ -273,7 +273,7 @@ public function typesWithNoPrefixesProvider() ]; } - public function dockBlockFallbackTypesProvider() + public static function dockBlockFallbackTypesProvider() { return [ 'pub' => [ @@ -304,7 +304,7 @@ public function testPropertiesDefinedByTraits(string $property, Type $type) $this->assertEquals([$type], $this->extractor->getTypes(DummyUsingTrait::class, $property)); } - public function propertiesDefinedByTraitsProvider(): array + public static function propertiesDefinedByTraitsProvider(): array { return [ ['propertyInTraitPrimitiveType', new Type(Type::BUILTIN_TYPE_STRING)], @@ -321,7 +321,7 @@ public function testPropertiesStaticType(string $class, string $property, Type $ $this->assertEquals([$type], $this->extractor->getTypes($class, $property)); } - public function propertiesStaticTypeProvider(): array + public static function propertiesStaticTypeProvider(): array { return [ [ParentDummy::class, 'propertyTypeStatic', new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)], @@ -337,7 +337,7 @@ public function testPropertiesParentType(string $class, string $property, ?array $this->assertEquals($types, $this->extractor->getTypes($class, $property)); } - public function propertiesParentTypeProvider(): array + public static function propertiesParentTypeProvider(): array { return [ [ParentDummy::class, 'parentAnnotationNoParent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'parent')]], @@ -353,7 +353,7 @@ public function testExtractConstructorTypes($property, array $type = null) $this->assertEquals($type, $this->extractor->getTypesFromConstructor('Symfony\Component\PropertyInfo\Tests\Fixtures\ConstructorDummy', $property)); } - public function constructorTypesProvider() + public static function constructorTypesProvider() { return [ ['date', [new Type(Type::BUILTIN_TYPE_INT)]], @@ -372,7 +372,7 @@ public function testExtractorUnionTypes(string $property, ?array $types) $this->assertEquals($types, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\DummyUnionType', $property)); } - public function unionTypesProvider(): array + public static function unionTypesProvider(): array { return [ ['a', [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)]], @@ -410,7 +410,7 @@ public function testExtractorIntRangeType(string $property, ?array $types) $this->assertEquals($types, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\IntRangeDummy', $property)); } - public function intRangeTypeProvider(): array + public static function intRangeTypeProvider(): array { return [ ['a', [new Type(Type::BUILTIN_TYPE_INT)]], diff --git a/Tests/Extractor/ReflectionExtractorTest.php b/Tests/Extractor/ReflectionExtractorTest.php index e17e203..b795558 100644 --- a/Tests/Extractor/ReflectionExtractorTest.php +++ b/Tests/Extractor/ReflectionExtractorTest.php @@ -207,7 +207,7 @@ public function testExtractors($property, array $type = null) $this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property, [])); } - public function typesProvider() + public static function typesProvider() { return [ ['a', null], @@ -234,7 +234,7 @@ public function testExtractPhp7Type(string $class, string $property, array $type $this->assertEquals($type, $this->extractor->getTypes($class, $property, [])); } - public function php7TypesProvider() + public static function php7TypesProvider() { return [ [Php7Dummy::class, 'foo', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)]], @@ -255,7 +255,7 @@ public function testExtractPhp71Type($property, array $type = null) $this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php71Dummy', $property, [])); } - public function php71TypesProvider() + public static function php71TypesProvider() { return [ ['foo', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)]], @@ -276,7 +276,7 @@ public function testExtractPhp80Type($property, array $type = null) $this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php80Dummy', $property, [])); } - public function php80TypesProvider() + public static function php80TypesProvider() { return [ ['foo', [new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)]], @@ -300,7 +300,7 @@ public function testExtractPhp81Type($property, array $type = null) $this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php81Dummy', $property, [])); } - public function php81TypesProvider() + public static function php81TypesProvider() { return [ ['nothing', null], @@ -326,7 +326,7 @@ public function testExtractPhp82Type($property, array $type = null) $this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Php82Dummy', $property, [])); } - public function php82TypesProvider(): iterable + public static function php82TypesProvider(): iterable { yield ['nil', null]; yield ['false', [new Type(Type::BUILTIN_TYPE_FALSE)]]; @@ -344,7 +344,7 @@ public function testExtractWithDefaultValue($property, $type) $this->assertEquals($type, $this->extractor->getTypes(DefaultValue::class, $property, [])); } - public function defaultValueProvider() + public static function defaultValueProvider() { return [ ['defaultInt', [new Type(Type::BUILTIN_TYPE_INT, false)]], @@ -366,7 +366,7 @@ public function testIsReadable($property, $expected) ); } - public function getReadableProperties() + public static function getReadableProperties() { return [ ['bar', false], @@ -397,7 +397,7 @@ public function testIsWritable($property, $expected) ); } - public function getWritableProperties() + public static function getWritableProperties() { return [ ['bar', false], @@ -451,7 +451,7 @@ public function testIsInitializable(string $class, string $property, bool $expec $this->assertSame($expected, $this->extractor->isInitializable($class, $property)); } - public function getInitializableProperties(): array + public static function getInitializableProperties(): array { return [ [Php71Dummy::class, 'string', true], @@ -475,7 +475,7 @@ public function testExtractTypeConstructor(string $class, string $property, arra $this->assertNull($this->extractor->getTypes($class, $property, ['enable_constructor_extraction' => false])); } - public function constructorTypesProvider(): array + public static function constructorTypesProvider(): array { return [ // php71 dummy has following constructor: __construct(string $string, int $intPrivate) @@ -534,7 +534,7 @@ public function testGetReadAccessor($class, $property, $found, $type, $name, $vi $this->assertSame($static, $readAcessor->isStatic()); } - public function readAccessorProvider(): array + public static function readAccessorProvider(): array { return [ [Dummy::class, 'bar', true, PropertyReadInfo::TYPE_PROPERTY, 'bar', PropertyReadInfo::VISIBILITY_PRIVATE, false], @@ -592,7 +592,7 @@ public function testGetWriteMutator($class, $property, $allowConstruct, $found, } } - public function writeMutatorProvider(): array + public static function writeMutatorProvider(): array { return [ [Dummy::class, 'bar', false, true, PropertyWriteInfo::TYPE_PROPERTY, 'bar', null, null, PropertyWriteInfo::VISIBILITY_PRIVATE, false], @@ -650,7 +650,7 @@ public function testExtractConstructorTypes(string $property, array $type = null $this->assertEquals($type, $this->extractor->getTypesFromConstructor('Symfony\Component\PropertyInfo\Tests\Fixtures\ConstructorDummy', $property)); } - public function extractConstructorTypesProvider(): array + public static function extractConstructorTypesProvider(): array { return [ ['timezone', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeZone')]], From 5cf906918ea0f74032ffc5c0b85def246ce409df Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 14 Feb 2023 09:53:37 +0100 Subject: [PATCH 5/8] Fix merge --- Tests/Extractor/PhpDocExtractorTest.php | 4 ++-- Tests/Extractor/PhpStanExtractorTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/Extractor/PhpDocExtractorTest.php b/Tests/Extractor/PhpDocExtractorTest.php index 14ed89a..6985e2d 100644 --- a/Tests/Extractor/PhpDocExtractorTest.php +++ b/Tests/Extractor/PhpDocExtractorTest.php @@ -418,7 +418,7 @@ public function testPseudoTypes($property, array $type) $this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\PseudoTypesDummy', $property)); } - public function pseudoTypesProvider(): array + public static function pseudoTypesProvider(): array { return [ ['classString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]], @@ -441,7 +441,7 @@ public function testExtractPromotedProperty(string $property, ?array $types) $this->assertEquals($types, $this->extractor->getTypes(Php80Dummy::class, $property)); } - public function promotedPropertyProvider(): array + public static function promotedPropertyProvider(): array { return [ ['promoted', null], diff --git a/Tests/Extractor/PhpStanExtractorTest.php b/Tests/Extractor/PhpStanExtractorTest.php index 62644fe..99a5e3d 100644 --- a/Tests/Extractor/PhpStanExtractorTest.php +++ b/Tests/Extractor/PhpStanExtractorTest.php @@ -395,7 +395,7 @@ public function testPseudoTypes($property, array $type) $this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\PhpStanPseudoTypesDummy', $property)); } - public function pseudoTypesProvider(): array + public static function pseudoTypesProvider(): array { return [ ['classString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]], @@ -462,7 +462,7 @@ public function testExtractPhp80Type(string $class, $property, array $type = nul $this->assertEquals($type, $this->extractor->getTypes($class, $property, [])); } - public function php80TypesProvider() + public static function php80TypesProvider() { return [ [Php80Dummy::class, 'promotedAndMutated', [new Type(Type::BUILTIN_TYPE_STRING)]], From 722737086d76b4edabfc2d50a48cebd4b8cd5546 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 16 Feb 2023 10:33:00 +0100 Subject: [PATCH 6/8] CS fix --- Util/PhpDocTypeHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Util/PhpDocTypeHelper.php b/Util/PhpDocTypeHelper.php index c4a2edb..2c858c3 100644 --- a/Util/PhpDocTypeHelper.php +++ b/Util/PhpDocTypeHelper.php @@ -173,7 +173,7 @@ private function normalizeType(string $docType): string case 'boolean': return 'bool'; - // real is not part of the PHPDoc standard, so we ignore it + // real is not part of the PHPDoc standard, so we ignore it case 'double': return 'float'; From 8454a441d117449abd0d61124e68f93f927ad5ba Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 14 Mar 2023 15:59:20 +0100 Subject: [PATCH 7/8] Fix some Composer keywords --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 30d77f6..79af9e8 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "keywords": [ "property", "type", - "PHPDoc", + "phpdoc", "symfony", "validator", "doctrine" From ff45ebbfd781eab2571d9d4412d82a9a733fc2b3 Mon Sep 17 00:00:00 2001 From: Cas van Dongen Date: Fri, 4 Nov 2022 10:42:22 +0100 Subject: [PATCH 8/8] [PropertyAccess] Readonly properties must have no PropertyWriteInfo --- Extractor/ReflectionExtractor.php | 6 +++++- Tests/Extractor/ReflectionExtractorTest.php | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Extractor/ReflectionExtractor.php b/Extractor/ReflectionExtractor.php index bdb2172..4da5912 100644 --- a/Extractor/ReflectionExtractor.php +++ b/Extractor/ReflectionExtractor.php @@ -397,8 +397,12 @@ public function getWriteInfo(string $class, string $property, array $context = [ if ($reflClass->hasProperty($property) && ($reflClass->getProperty($property)->getModifiers() & $this->propertyReflectionFlags)) { $reflProperty = $reflClass->getProperty($property); + if (\PHP_VERSION_ID < 80100 || !$reflProperty->isReadOnly()) { + return new PropertyWriteInfo(PropertyWriteInfo::TYPE_PROPERTY, $property, $this->getWriteVisiblityForProperty($reflProperty), $reflProperty->isStatic()); + } - return new PropertyWriteInfo(PropertyWriteInfo::TYPE_PROPERTY, $property, $this->getWriteVisiblityForProperty($reflProperty), $reflProperty->isStatic()); + $errors[] = [sprintf('The property "%s" in class "%s" is a promoted readonly property.', $property, $reflClass->getName())]; + $allowMagicSet = $allowMagicCall = false; } if ($allowMagicSet) { diff --git a/Tests/Extractor/ReflectionExtractorTest.php b/Tests/Extractor/ReflectionExtractorTest.php index b795558..f33ad70 100644 --- a/Tests/Extractor/ReflectionExtractorTest.php +++ b/Tests/Extractor/ReflectionExtractorTest.php @@ -642,6 +642,18 @@ public function testGetWriteInfoDeprecatedEnableMagicCallExtractionInContext() ]); } + /** + * @requires PHP 8.1 + */ + public function testGetWriteInfoReadonlyProperties() + { + $writeMutatorConstructor = $this->extractor->getWriteInfo(Php81Dummy::class, 'foo', ['enable_constructor_extraction' => true]); + $writeMutatorWithoutConstructor = $this->extractor->getWriteInfo(Php81Dummy::class, 'foo', ['enable_constructor_extraction' => false]); + + $this->assertSame(PropertyWriteInfo::TYPE_CONSTRUCTOR, $writeMutatorConstructor->getType()); + $this->assertSame(PropertyWriteInfo::TYPE_NONE, $writeMutatorWithoutConstructor->getType()); + } + /** * @dataProvider extractConstructorTypesProvider */