From 5e667cf141c7225dec0ab045e4bb55a501f5f824 Mon Sep 17 00:00:00 2001 From: Andrew Alyamovsky Date: Mon, 24 Jun 2024 12:50:14 +0200 Subject: [PATCH 1/8] [PropertyInfo] Add attributes extractor --- .../Compiler/UnusedTagsPass.php | 1 + .../Resources/config/property_info.php | 1 + .../DependencyInjection/PropertyInfoPass.php | 3 +++ .../Extractor/ReflectionExtractor.php | 26 +++++++++++++++++- .../PropertyAttributesExtractorInterface.php | 27 +++++++++++++++++++ .../PropertyInfoCacheExtractor.php | 7 ++++- .../PropertyInfo/PropertyInfoExtractor.php | 7 +++++ .../PropertyInfoPassTest.php | 6 +++-- .../Tests/Fixtures/DummyExtractor.php | 16 ++++++++++- .../Tests/Fixtures/NullExtractor.php | 11 +++++++- 10 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Component/PropertyInfo/PropertyAttributesExtractorInterface.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php index ae2523e515d0c..f0bb7e285acd4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php @@ -73,6 +73,7 @@ class UnusedTagsPass implements CompilerPassInterface 'property_info.initializable_extractor', 'property_info.list_extractor', 'property_info.type_extractor', + 'property_info.attributes_extractor', 'proxy', 'remote_event.consumer', 'routing.condition_service', diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.php index 90587839d54c4..b2181749f53d2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.php @@ -45,6 +45,7 @@ ->tag('property_info.type_extractor', ['priority' => -1002]) ->tag('property_info.access_extractor', ['priority' => -1000]) ->tag('property_info.initializable_extractor', ['priority' => -1000]) + ->tag('property_info.attributes_extractor', ['priority' => -1000]) ->alias(PropertyReadInfoExtractorInterface::class, 'property_info.reflection_extractor') ->alias(PropertyWriteInfoExtractorInterface::class, 'property_info.reflection_extractor') diff --git a/src/Symfony/Component/PropertyInfo/DependencyInjection/PropertyInfoPass.php b/src/Symfony/Component/PropertyInfo/DependencyInjection/PropertyInfoPass.php index de2a374ec564d..0cc463ad01830 100644 --- a/src/Symfony/Component/PropertyInfo/DependencyInjection/PropertyInfoPass.php +++ b/src/Symfony/Component/PropertyInfo/DependencyInjection/PropertyInfoPass.php @@ -47,5 +47,8 @@ public function process(ContainerBuilder $container): void $initializableExtractors = $this->findAndSortTaggedServices('property_info.initializable_extractor', $container); $definition->setArgument(4, new IteratorArgument($initializableExtractors)); + + $attributesExtractor = $this->findAndSortTaggedServices('property_info.attributes_extractor', $container); + $definition->replaceArgument(5, new IteratorArgument($attributesExtractor)); } } diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index 953e33f04f27c..9dc1db0aa0e7a 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -12,6 +12,7 @@ namespace Symfony\Component\PropertyInfo\Extractor; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyAttributesExtractorInterface; use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface; use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; use Symfony\Component\PropertyInfo\PropertyReadInfo; @@ -36,7 +37,7 @@ * * @final */ -class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface, PropertyInitializableExtractorInterface, PropertyReadInfoExtractorInterface, PropertyWriteInfoExtractorInterface, ConstructorArgumentTypeExtractorInterface +class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface, PropertyInitializableExtractorInterface, PropertyReadInfoExtractorInterface, PropertyWriteInfoExtractorInterface, ConstructorArgumentTypeExtractorInterface, PropertyAttributesExtractorInterface { /** * @internal @@ -507,6 +508,29 @@ public function getWriteInfo(string $class, string $property, array $context = [ return $noneProperty; } + public function getAttributes(string $class, string $property, array $context = []): ?array + { + try { + $reflClass = new \ReflectionClass($class); + } catch (\ReflectionException) { + return null; + } + + if (!$reflClass->hasProperty($property)) { + return null; + } + + $attributes = []; + foreach ($reflClass->getProperty($property)->getAttributes() as $attribute) { + $attributes[] = [ + 'name' => $attribute->getName(), + 'arguments' => $attribute->getArguments(), + ]; + } + + return $attributes; + } + /** * @return LegacyType[]|null */ diff --git a/src/Symfony/Component/PropertyInfo/PropertyAttributesExtractorInterface.php b/src/Symfony/Component/PropertyInfo/PropertyAttributesExtractorInterface.php new file mode 100644 index 0000000000000..80e5b43f6ee04 --- /dev/null +++ b/src/Symfony/Component/PropertyInfo/PropertyAttributesExtractorInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyInfo; + +interface PropertyAttributesExtractorInterface +{ + /** + * Gets the attributes of the property. + * + * Returns an array of attributes, each attribute is an associative array with the following keys: + * - name: The fully-qualified class name of the attribute + * - arguments: An associative array of attribute arguments + * + * Example: + * [['name' => 'FQCN', 'arguments' => ['key' => 'value']], ...] + */ + public function getAttributes(string $class, string $property, array $context = []): ?array; +} diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php index 38b9c68a2e29e..85e8a07a8f2ab 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoCacheExtractor.php @@ -21,7 +21,7 @@ * * @final */ -class PropertyInfoCacheExtractor implements PropertyInfoExtractorInterface, PropertyInitializableExtractorInterface +class PropertyInfoCacheExtractor implements PropertyInfoExtractorInterface, PropertyInitializableExtractorInterface, PropertyAttributesExtractorInterface { private array $arrayCache = []; @@ -69,6 +69,11 @@ public function getTypes(string $class, string $property, array $context = []): return $this->extract('getTypes', [$class, $property, $context]); } + public function getAttributes(string $class, string $property, array $context = []): ?array + { + return $this->extract('getAttributes', [$class, $property, $context]); + } + public function isInitializable(string $class, string $property, array $context = []): ?bool { return $this->extract('isInitializable', [$class, $property, $context]); diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php index 8e8952c7f4e23..f413af15f3c5c 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php @@ -28,6 +28,7 @@ class PropertyInfoExtractor implements PropertyInfoExtractorInterface, PropertyI * @param iterable $descriptionExtractors * @param iterable $accessExtractors * @param iterable $initializableExtractors + * @param iterable $attributesExtractors */ public function __construct( private readonly iterable $listExtractors = [], @@ -35,6 +36,7 @@ public function __construct( private readonly iterable $descriptionExtractors = [], private readonly iterable $accessExtractors = [], private readonly iterable $initializableExtractors = [], + private readonly iterable $attributesExtractors = [], ) { } @@ -66,6 +68,11 @@ public function getTypes(string $class, string $property, array $context = []): return $this->extract($this->typeExtractors, 'getTypes', [$class, $property, $context]); } + public function getAttributes(string $class, string $property, array $context = []): ?array + { + return $this->extract($this->attributesExtractors, 'getAttributes', [$class, $property, $context]); + } + public function isReadable(string $class, string $property, array $context = []): ?bool { return $this->extract($this->accessExtractors, 'isReadable', [$class, $property, $context]); diff --git a/src/Symfony/Component/PropertyInfo/Tests/DependencyInjection/PropertyInfoPassTest.php b/src/Symfony/Component/PropertyInfo/Tests/DependencyInjection/PropertyInfoPassTest.php index a1db4822e045c..120f0eefdb255 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/DependencyInjection/PropertyInfoPassTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/DependencyInjection/PropertyInfoPassTest.php @@ -26,7 +26,7 @@ public function testServicesAreOrderedAccordingToPriority($index, $tag) { $container = new ContainerBuilder(); - $definition = $container->register('property_info')->setArguments([null, null, null, null, null]); + $definition = $container->register('property_info')->setArguments([null, null, null, null, null, null]); $container->register('n2')->addTag($tag, ['priority' => 100]); $container->register('n1')->addTag($tag, ['priority' => 200]); $container->register('n3')->addTag($tag); @@ -50,6 +50,7 @@ public static function provideTags() [2, 'property_info.description_extractor'], [3, 'property_info.access_extractor'], [4, 'property_info.initializable_extractor'], + [5, 'property_info.attributes_extractor'], ]; } @@ -57,7 +58,7 @@ public function testReturningEmptyArrayWhenNoService() { $container = new ContainerBuilder(); $propertyInfoExtractorDefinition = $container->register('property_info') - ->setArguments([[], [], [], [], []]); + ->setArguments([[], [], [], [], [], []]); $propertyInfoPass = new PropertyInfoPass(); $propertyInfoPass->process($container); @@ -67,5 +68,6 @@ public function testReturningEmptyArrayWhenNoService() $this->assertEquals(new IteratorArgument([]), $propertyInfoExtractorDefinition->getArgument(2)); $this->assertEquals(new IteratorArgument([]), $propertyInfoExtractorDefinition->getArgument(3)); $this->assertEquals(new IteratorArgument([]), $propertyInfoExtractorDefinition->getArgument(4)); + $this->assertEquals(new IteratorArgument([]), $propertyInfoExtractorDefinition->getArgument(5)); } } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyExtractor.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyExtractor.php index cfffd45e0c05f..52a6899ca72e0 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyExtractor.php @@ -13,6 +13,7 @@ use Symfony\Component\PropertyInfo\Extractor\ConstructorArgumentTypeExtractorInterface; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyAttributesExtractorInterface; use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface; use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface; use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; @@ -23,7 +24,7 @@ /** * @author Kévin Dunglas */ -class DummyExtractor implements PropertyListExtractorInterface, PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface, PropertyInitializableExtractorInterface, ConstructorArgumentTypeExtractorInterface +class DummyExtractor implements PropertyListExtractorInterface, PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface, PropertyInitializableExtractorInterface, ConstructorArgumentTypeExtractorInterface, PropertyAttributesExtractorInterface { public function getShortDescription($class, $property, array $context = []): ?string { @@ -45,6 +46,19 @@ public function getType($class, $property, array $context = []): ?Type return Type::int(); } + public function getAttributes($class, $property, array $context = []): ?array + { + return [ + [ + 'name' => \stdClass::class, + 'arguments' => [ + 'foo' => 'bar', + 'baz' => 'qux', + ], + ], + ]; + } + public function getTypesFromConstructor(string $class, string $property): ?array { return [new LegacyType(LegacyType::BUILTIN_TYPE_STRING)]; diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/NullExtractor.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/NullExtractor.php index 1d924044531ff..a506ee9fb38f8 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/NullExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/NullExtractor.php @@ -12,6 +12,7 @@ namespace Symfony\Component\PropertyInfo\Tests\Fixtures; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyAttributesExtractorInterface; use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface; use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface; use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; @@ -23,7 +24,7 @@ * * @author Kévin Dunglas */ -class NullExtractor implements PropertyListExtractorInterface, PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface, PropertyInitializableExtractorInterface +class NullExtractor implements PropertyListExtractorInterface, PropertyDescriptionExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface, PropertyInitializableExtractorInterface, PropertyAttributesExtractorInterface { public function getShortDescription($class, $property, array $context = []): ?string { @@ -57,6 +58,14 @@ public function getType($class, $property, array $context = []): ?Type return null; } + public function getAttributes($class, $property, array $context = []): ?array + { + $this->assertIsString($class); + $this->assertIsString($property); + + return null; + } + public function isReadable($class, $property, array $context = []): ?bool { $this->assertIsString($class); From aaa35acfb54d6104e4fb10efcbee4722fcb83478 Mon Sep 17 00:00:00 2001 From: Andrew Alyamovsky Date: Tue, 25 Jun 2024 01:33:56 +0200 Subject: [PATCH 2/8] [PropertyInfo] Add interface implementation --- src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php index f413af15f3c5c..6ab19dba8238a 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php +++ b/src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php @@ -20,7 +20,7 @@ * * @final */ -class PropertyInfoExtractor implements PropertyInfoExtractorInterface, PropertyInitializableExtractorInterface +class PropertyInfoExtractor implements PropertyInfoExtractorInterface, PropertyInitializableExtractorInterface, PropertyAttributesExtractorInterface { /** * @param iterable $listExtractors From 8c72472c1c6ec92c29201350951569d8caa7cfc4 Mon Sep 17 00:00:00 2001 From: Andrew Alyamovsky Date: Wed, 26 Jun 2024 22:38:59 +0200 Subject: [PATCH 3/8] [PropertyInfo] Fix DI --- .../DependencyInjection/FrameworkExtension.php | 3 +++ .../Bundle/FrameworkBundle/Resources/config/property_info.php | 4 +++- .../PropertyInfo/DependencyInjection/PropertyInfoPass.php | 2 +- .../Component/PropertyInfo/Extractor/ReflectionExtractor.php | 3 ++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index ade8c70a9ef77..386ab4ea63437 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -130,6 +130,7 @@ use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\PhpStanExtractor; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyAttributesExtractorInterface; use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface; use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface; use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface; @@ -642,6 +643,8 @@ public function load(array $configs, ContainerBuilder $container): void ->addTag('property_info.access_extractor'); $container->registerForAutoconfiguration(PropertyInitializableExtractorInterface::class) ->addTag('property_info.initializable_extractor'); + $container->registerForAutoconfiguration(PropertyAttributesExtractorInterface::class) + ->addTag('property_info.attributes_extractor'); $container->registerForAutoconfiguration(EncoderInterface::class) ->addTag('serializer.encoder'); $container->registerForAutoconfiguration(DecoderInterface::class) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.php index b2181749f53d2..b491546bd51c0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.php @@ -13,6 +13,7 @@ use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyAttributesExtractorInterface; use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface; use Symfony\Component\PropertyInfo\PropertyInfoCacheExtractor; use Symfony\Component\PropertyInfo\PropertyInfoExtractor; @@ -26,7 +27,7 @@ return static function (ContainerConfigurator $container) { $container->services() ->set('property_info', PropertyInfoExtractor::class) - ->args([[], [], [], [], []]) + ->args([[], [], [], [], [], []]) ->alias(PropertyAccessExtractorInterface::class, 'property_info') ->alias(PropertyDescriptionExtractorInterface::class, 'property_info') @@ -34,6 +35,7 @@ ->alias(PropertyTypeExtractorInterface::class, 'property_info') ->alias(PropertyListExtractorInterface::class, 'property_info') ->alias(PropertyInitializableExtractorInterface::class, 'property_info') + ->alias(PropertyAttributesExtractorInterface::class, 'property_info') ->set('property_info.cache', PropertyInfoCacheExtractor::class) ->decorate('property_info') diff --git a/src/Symfony/Component/PropertyInfo/DependencyInjection/PropertyInfoPass.php b/src/Symfony/Component/PropertyInfo/DependencyInjection/PropertyInfoPass.php index 0cc463ad01830..37cac5d8a32ef 100644 --- a/src/Symfony/Component/PropertyInfo/DependencyInjection/PropertyInfoPass.php +++ b/src/Symfony/Component/PropertyInfo/DependencyInjection/PropertyInfoPass.php @@ -49,6 +49,6 @@ public function process(ContainerBuilder $container): void $definition->setArgument(4, new IteratorArgument($initializableExtractors)); $attributesExtractor = $this->findAndSortTaggedServices('property_info.attributes_extractor', $container); - $definition->replaceArgument(5, new IteratorArgument($attributesExtractor)); + $definition->setArgument(5, new IteratorArgument($attributesExtractor)); } } diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index 9dc1db0aa0e7a..a5af834c6dc34 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -521,7 +521,8 @@ public function getAttributes(string $class, string $property, array $context = } $attributes = []; - foreach ($reflClass->getProperty($property)->getAttributes() as $attribute) { + $reflProperty = $reflClass->getProperty($property); + foreach ($reflProperty->getAttributes() as $attribute) { $attributes[] = [ 'name' => $attribute->getName(), 'arguments' => $attribute->getArguments(), From 3928042855ce143b6651c696fa6fde5b5c033b01 Mon Sep 17 00:00:00 2001 From: Andrew Alyamovsky Date: Wed, 26 Jun 2024 22:39:10 +0200 Subject: [PATCH 4/8] [PropertyInfo] Add tests --- .../Extractor/ReflectionExtractorTest.php | 104 ++++++++++++++++++ .../Tests/Fixtures/DummyAttribute.php | 15 +++ .../Tests/Fixtures/DummyWithAttributes.php | 24 ++++ 3 files changed, 143 insertions(+) create mode 100644 src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyAttribute.php create mode 100644 src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyWithAttributes.php diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php index d5e5c676727ed..e2d161fc3fecc 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php @@ -19,6 +19,8 @@ use Symfony\Component\PropertyInfo\Tests\Fixtures\ConstructorDummy; use Symfony\Component\PropertyInfo\Tests\Fixtures\DefaultValue; use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy; +use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyAttribute; +use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyWithAttributes; use Symfony\Component\PropertyInfo\Tests\Fixtures\NotInstantiable; use Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy; use Symfony\Component\PropertyInfo\Tests\Fixtures\Php71Dummy; @@ -498,6 +500,108 @@ public static function getInitializableProperties(): array ]; } + /** + * @dataProvider getAttributes + */ + public function testGetAttributes(string $class, string $property, ?array $expected) + { + $this->assertSame($expected, $this->extractor->getAttributes($class, $property)); + } + + public static function getAttributes(): array + { + return [ + [ + DummyWithAttributes::class, + 'a', + [ + [ + 'name' => DummyAttribute::class, + 'arguments' => [ + 'type' => 'foo', + 'name' => 'nameA', + 'version' => 1, + ], + ], + ], + ], + [ + DummyWithAttributes::class, + 'b', + [ + [ + 'name' => DummyAttribute::class, + 'arguments' => [ + 'type' => 'bar', + 'name' => 'nameB', + 'version' => 2, + ], + ], + ], + ], + [ + DummyWithAttributes::class, + 'c', + [ + [ + 'name' => DummyAttribute::class, + 'arguments' => [ + 'type' => 'baz', + 'name' => 'nameC', + 'version' => 3, + ], + ], + ], + ], + [ + DummyWithAttributes::class, + 'd', + [ + [ + 'name' => DummyAttribute::class, + 'arguments' => [ + 0 => 'foo', + 1 => 'nameD', + 2 => 4, + ], + ], + ], + ], + [ + DummyWithAttributes::class, + 'e', + [ + [ + 'name' => DummyAttribute::class, + 'arguments' => [ + 'type' => 'foo', + 'name' => 'nameE1', + 'version' => 5, + ], + ], + [ + 'name' => DummyAttribute::class, + 'arguments' => [ + 'type' => 'foo', + 'name' => 'nameE2', + 'version' => 10, + ], + ], + ], + ], + [ + DummyWithAttributes::class, + 'f', + [], + ], + [ + DummyWithAttributes::class, + 'nonExistentProperty', + null, + ], + ]; + } + /** * @group legacy * diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyAttribute.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyAttribute.php new file mode 100644 index 0000000000000..20da0fb2d90c2 --- /dev/null +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyAttribute.php @@ -0,0 +1,15 @@ + Date: Wed, 26 Jun 2024 22:40:56 +0200 Subject: [PATCH 5/8] [PropertyInfo] Update changelog --- src/Symfony/Component/PropertyInfo/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Component/PropertyInfo/CHANGELOG.md b/src/Symfony/Component/PropertyInfo/CHANGELOG.md index 490dab43b4754..2851965e08911 100644 --- a/src/Symfony/Component/PropertyInfo/CHANGELOG.md +++ b/src/Symfony/Component/PropertyInfo/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.2 +--- + + * Add `PropertyAttributesExtractorInterface` to extract property attributes + 7.1 --- From 80e402bc27193d9c45b4aedbd3256315901539b9 Mon Sep 17 00:00:00 2001 From: Andrew Alyamovsky Date: Wed, 26 Jun 2024 22:41:16 +0200 Subject: [PATCH 6/8] [PropertyInfo] Update changelog --- src/Symfony/Component/PropertyInfo/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/PropertyInfo/CHANGELOG.md b/src/Symfony/Component/PropertyInfo/CHANGELOG.md index 2851965e08911..77492aa27d2b4 100644 --- a/src/Symfony/Component/PropertyInfo/CHANGELOG.md +++ b/src/Symfony/Component/PropertyInfo/CHANGELOG.md @@ -4,7 +4,7 @@ CHANGELOG 7.2 --- - * Add `PropertyAttributesExtractorInterface` to extract property attributes + * Add `PropertyAttributesExtractorInterface` to extract property attributes (implemented by `ReflectionExtractor`) 7.1 --- From 450cabb98911f4830398d8aa89861c4b0f9af436 Mon Sep 17 00:00:00 2001 From: Andrew Alyamovsky Date: Wed, 26 Jun 2024 22:48:53 +0200 Subject: [PATCH 7/8] [PropertyInfo] Update phpdoc --- .../PropertyInfo/PropertyAttributesExtractorInterface.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/PropertyInfo/PropertyAttributesExtractorInterface.php b/src/Symfony/Component/PropertyInfo/PropertyAttributesExtractorInterface.php index 80e5b43f6ee04..04e79557cbd3c 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyAttributesExtractorInterface.php +++ b/src/Symfony/Component/PropertyInfo/PropertyAttributesExtractorInterface.php @@ -11,6 +11,9 @@ namespace Symfony\Component\PropertyInfo; +/** + * @author Andrew Alyamovsky + */ interface PropertyAttributesExtractorInterface { /** @@ -19,9 +22,6 @@ interface PropertyAttributesExtractorInterface * Returns an array of attributes, each attribute is an associative array with the following keys: * - name: The fully-qualified class name of the attribute * - arguments: An associative array of attribute arguments - * - * Example: - * [['name' => 'FQCN', 'arguments' => ['key' => 'value']], ...] */ public function getAttributes(string $class, string $property, array $context = []): ?array; } From ceafe5d710cd4637dd6d930e7555ac6f06813a9f Mon Sep 17 00:00:00 2001 From: Andrew Alyamovsky Date: Mon, 1 Jul 2024 22:16:39 +0200 Subject: [PATCH 8/8] [PropertyInfo] Fix code review --- .../PropertyInfo/PropertyAttributesExtractorInterface.php | 4 +++- .../PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php | 4 ++-- .../Component/PropertyInfo/Tests/Fixtures/DummyAttribute.php | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/PropertyInfo/PropertyAttributesExtractorInterface.php b/src/Symfony/Component/PropertyInfo/PropertyAttributesExtractorInterface.php index 04e79557cbd3c..b0d3600c7396e 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyAttributesExtractorInterface.php +++ b/src/Symfony/Component/PropertyInfo/PropertyAttributesExtractorInterface.php @@ -21,7 +21,9 @@ interface PropertyAttributesExtractorInterface * * Returns an array of attributes, each attribute is an associative array with the following keys: * - name: The fully-qualified class name of the attribute - * - arguments: An associative array of attribute arguments + * - arguments: An associative array of attribute arguments if present + * + * @return array}>|null */ public function getAttributes(string $class, string $property, array $context = []): ?array; } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php index e2d161fc3fecc..a222193332038 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php @@ -501,14 +501,14 @@ public static function getInitializableProperties(): array } /** - * @dataProvider getAttributes + * @dataProvider attributesProvider */ public function testGetAttributes(string $class, string $property, ?array $expected) { $this->assertSame($expected, $this->extractor->getAttributes($class, $property)); } - public static function getAttributes(): array + public static function attributesProvider(): array { return [ [ diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyAttribute.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyAttribute.php index 20da0fb2d90c2..1c6bac8cf1e89 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyAttribute.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyAttribute.php @@ -9,7 +9,6 @@ public function __construct( public string $type, public string $name, public int $version, - ) - { + ) { } }