From 0356ea590c5a90d9082d90b43b84f40e2612c691 Mon Sep 17 00:00:00 2001 From: Alex Pott <alex.a.pott@googlemail.com> Date: Wed, 13 Mar 2024 11:18:35 +0000 Subject: [PATCH] Issue #3427388 by godotislate, Berdir: Update Drupal\Component\Annotation\Doctrine\StaticReflectionParser::hasClassAttribute() to allow attribute subclasses --- .../Annotation/Doctrine/StaticReflectionParser.php | 10 ++++++++-- .../Doctrine/Fixtures/Attribute/Nonexistent.php | 9 +++++++++ .../Fixtures/ExtraAttributes/ExampleAttribute.php | 2 +- .../ExtraAttributes/ExampleParentAttribute.php | 8 ++++++++ .../Doctrine/StaticReflectionParserTest.php | 13 ++++++++----- 5 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 core/tests/Drupal/Tests/Component/Annotation/Doctrine/Fixtures/Attribute/Nonexistent.php create mode 100644 core/tests/Drupal/Tests/Component/Annotation/Doctrine/Fixtures/ExtraAttributes/ExampleParentAttribute.php diff --git a/core/lib/Drupal/Component/Annotation/Doctrine/StaticReflectionParser.php b/core/lib/Drupal/Component/Annotation/Doctrine/StaticReflectionParser.php index 10b9441c1a28..ce1aa0c083e1 100644 --- a/core/lib/Drupal/Component/Annotation/Doctrine/StaticReflectionParser.php +++ b/core/lib/Drupal/Component/Annotation/Doctrine/StaticReflectionParser.php @@ -205,7 +205,7 @@ protected function parse() break; case T_CLASS: // Convert the attributes to fully qualified names. - $this->classAttributes = array_map(fn($name) => strtolower($this->fullySpecifyName($name)), $attributeNames); + $this->classAttributes = array_map(fn($name) => $this->fullySpecifyName($name), $attributeNames); if ($last_token !== T_PAAMAYIM_NEKUDOTAYIM && $last_token !== T_NEW) { $this->docComment['class'] = $docComment; $docComment = ''; @@ -346,7 +346,13 @@ public function getStaticReflectionParserForDeclaringClass($type, $name) public function hasClassAttribute(string $attribute): bool { $this->parse(); - return in_array('\\' . ltrim(strtolower($attribute), '\\'), $this->classAttributes, TRUE); + foreach ($this->classAttributes as $classAttribute) { + if (is_a($classAttribute, $attribute, TRUE)) { + return TRUE; + } + } + + return FALSE; } /** diff --git a/core/tests/Drupal/Tests/Component/Annotation/Doctrine/Fixtures/Attribute/Nonexistent.php b/core/tests/Drupal/Tests/Component/Annotation/Doctrine/Fixtures/Attribute/Nonexistent.php new file mode 100644 index 000000000000..45ed519aa40d --- /dev/null +++ b/core/tests/Drupal/Tests/Component/Annotation/Doctrine/Fixtures/Attribute/Nonexistent.php @@ -0,0 +1,9 @@ +<?php + +namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Attribute; + +// @phpstan-ignore-next-line +#[NonexistentAttribute] +final class Nonexistent +{ +} diff --git a/core/tests/Drupal/Tests/Component/Annotation/Doctrine/Fixtures/ExtraAttributes/ExampleAttribute.php b/core/tests/Drupal/Tests/Component/Annotation/Doctrine/Fixtures/ExtraAttributes/ExampleAttribute.php index 0275fabb56f8..abcd69fa1291 100644 --- a/core/tests/Drupal/Tests/Component/Annotation/Doctrine/Fixtures/ExtraAttributes/ExampleAttribute.php +++ b/core/tests/Drupal/Tests/Component/Annotation/Doctrine/Fixtures/ExtraAttributes/ExampleAttribute.php @@ -3,6 +3,6 @@ namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\ExtraAttributes; #[\Attribute] -final class ExampleAttribute +final class ExampleAttribute extends ExampleParentAttribute { } diff --git a/core/tests/Drupal/Tests/Component/Annotation/Doctrine/Fixtures/ExtraAttributes/ExampleParentAttribute.php b/core/tests/Drupal/Tests/Component/Annotation/Doctrine/Fixtures/ExtraAttributes/ExampleParentAttribute.php new file mode 100644 index 000000000000..ea1eaaa0f89a --- /dev/null +++ b/core/tests/Drupal/Tests/Component/Annotation/Doctrine/Fixtures/ExtraAttributes/ExampleParentAttribute.php @@ -0,0 +1,8 @@ +<?php + +namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\ExtraAttributes; + +#[\Attribute] +class ExampleParentAttribute { + +} diff --git a/core/tests/Drupal/Tests/Component/Annotation/Doctrine/StaticReflectionParserTest.php b/core/tests/Drupal/Tests/Component/Annotation/Doctrine/StaticReflectionParserTest.php index d929e48f6120..85a99f3c353b 100644 --- a/core/tests/Drupal/Tests/Component/Annotation/Doctrine/StaticReflectionParserTest.php +++ b/core/tests/Drupal/Tests/Component/Annotation/Doctrine/StaticReflectionParserTest.php @@ -15,8 +15,10 @@ class StaticReflectionParserTest extends TestCase { /** * @testWith ["AttributeClass", "\\Attribute", true] + * ["AttributeClass", "attribute", true] * ["AttributeClass", "Attribute", true] * ["AttributeClass", "\\DoesNotExist", false] + * ["Nonexistent", "NonexistentAttribute", false] * ["MultipleAttributes", "Attribute", true] * ["MultipleAttributes", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\Attribute\\AttributeClass", true] * ["MultipleAttributes", "DoesNotExist", false] @@ -26,15 +28,16 @@ class StaticReflectionParserTest extends TestCase { * ["UsedAsQualified", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleAttribute", true] * ["Qualified", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleAttribute", true] * ["Relative", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\Attribute\\SubDir\\SubDirAttribute", true] + * ["FullyQualified", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleParentAttribute", true] + * ["Used", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleParentAttribute", true] + * ["UsedAs", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleParentAttribute", true] + * ["UsedAsQualified", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleParentAttribute", true] + * ["Qualified", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleParentAttribute", true] */ public function testAttribute(string $class, string $attribute_class, bool $expected) { $finder = MockFileFinder::create(__DIR__ . '/Fixtures/Attribute/' . $class . '.php'); $parser = new StaticReflectionParser('\\Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\Attribute\\' . $class, $finder); - $this->assertSame($expected, $parser->hasClassAttribute($attribute_class), "'$class' has '$attribute_class'"); - // Attribute names and namespaces are case-insensitive in PHP. Practically - // Composer autoloading makes this untrue but builtins like \Attribute are - // case-insensitive so we should support that. - $this->assertSame($expected, $parser->hasClassAttribute(strtoupper($attribute_class)), "'$class' has '$attribute_class'"); + $this->assertSame($expected, $parser->hasClassAttribute($attribute_class), "'$class' has attribute that is a '$attribute_class'"); } } -- GitLab