From f752637140a3ccb05760ef4cc76d103053eeb8f0 Mon Sep 17 00:00:00 2001 From: Sergey Danilchenko Date: Sat, 26 Apr 2025 20:58:06 +0200 Subject: [PATCH 1/5] [12.x] Introduce helper functions for accessing class attributes --- src/Illuminate/Support/helpers.php | 44 ++++++++++++++ .../Fixtures/ClassesWithAttributes.php | 29 ++++++++++ tests/Support/SupportHelpersTest.php | 57 +++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 tests/Support/Fixtures/ClassesWithAttributes.php diff --git a/src/Illuminate/Support/helpers.php b/src/Illuminate/Support/helpers.php index 8ecb1eb3cffe..fc08a28c65fa 100644 --- a/src/Illuminate/Support/helpers.php +++ b/src/Illuminate/Support/helpers.php @@ -4,6 +4,7 @@ use Illuminate\Contracts\Support\Htmlable; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; +use Illuminate\Support\Collection; use Illuminate\Support\Env; use Illuminate\Support\Fluent; use Illuminate\Support\HigherOrderTapProxy; @@ -78,6 +79,49 @@ function blank($value) } } +if (! function_exists('class_attributes')) { + /** + * Get specified class attribute(s), optionally following an inheritance chain. + * + * @template TTarget of object + * @template TAttribute of object + * + * @param TTarget|class-string $objectOrClass + * @param class-string $attribute + * @return ($ascend is true ? Collection, Collection> : Collection) + */ + function class_attributes($objectOrClass, $attribute, $ascend = false) + { + $refClass = new ReflectionClass($objectOrClass); + $attributes = []; + + do { + $attributes[$refClass->name] = new Collection(array_map( + fn (ReflectionAttribute $refAttr) => $refAttr->newInstance(), + $refClass->getAttributes($attribute) + )); + } while ($ascend && false !== $refClass = $refClass->getParentClass()); + + return $ascend ? new Collection($attributes) : reset($attributes); + } +} + +if (! function_exists('class_attribute')) { + /** + * Get a specified class attribute, optionally following an inheritance chain. + * + * @template TAttribute of object + * + * @param object|class-string $objectOrClass + * @param class-string $attribute + * @return TAttribute|null + */ + function class_attribute($objectOrClass, $attribute, $ascend = false) + { + return class_attributes($objectOrClass, $attribute, $ascend)->flatten()->first(); + } +} + if (! function_exists('class_basename')) { /** * Get the class "basename" of the given object / class. diff --git a/tests/Support/Fixtures/ClassesWithAttributes.php b/tests/Support/Fixtures/ClassesWithAttributes.php new file mode 100644 index 000000000000..6f178527cbf5 --- /dev/null +++ b/tests/Support/Fixtures/ClassesWithAttributes.php @@ -0,0 +1,29 @@ +assertFalse(blank($model)); } + public function testClassAttributes() + { + require __DIR__.'/Fixtures/ClassesWithAttributes.php'; + + $this->assertSame([], class_attributes(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class)->toArray()); + + $this->assertSame( + [Fixtures\ChildClass::class => [], Fixtures\ParentClass::class => []], + class_attributes(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class, true)->toArray() + ); + + $this->assertSame( + ['quick', 'brown', 'fox'], + class_attributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->map->string->all() + ); + + $this->assertSame( + ['quick', 'brown', 'fox', 'lazy', 'dog'], + class_attributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->flatten()->map->string->all() + ); + + $this->assertSame(7, class_attributes(Fixtures\ChildClass::class, Fixtures\NumAttr::class)->sum->number); + $this->assertSame(12, class_attributes(Fixtures\ChildClass::class, Fixtures\NumAttr::class, true)->flatten()->sum->number); + $this->assertSame(5, class_attributes(Fixtures\ParentClass::class, Fixtures\NumAttr::class)->sum->number); + $this->assertSame(5, class_attributes(Fixtures\ParentClass::class, Fixtures\NumAttr::class, true)->flatten()->sum->number); + + $this->assertSame( + [Fixtures\ChildClass::class, Fixtures\ParentClass::class], + class_attributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->keys()->all() + ); + + $this->assertContainsOnlyInstancesOf( + Fixtures\StrAttr::class, + class_attributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->all() + ); + + $this->assertContainsOnlyInstancesOf( + Fixtures\StrAttr::class, + class_attributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->flatten()->all() + ); + } + + public function testClassAttribute() + { + require __DIR__.'/Fixtures/ClassesWithAttributes.php'; + + $this->assertNull(class_attribute(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class)); + $this->assertNull(class_attribute(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class, true)); + $this->assertNull(class_attribute(Fixtures\ChildClass::class, Fixtures\ParentOnlyAttr::class)); + $this->assertInstanceOf(Fixtures\ParentOnlyAttr::class, class_attribute(Fixtures\ChildClass::class, Fixtures\ParentOnlyAttr::class, true)); + $this->assertInstanceOf(Fixtures\StrAttr::class, class_attribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class)); + $this->assertInstanceOf(Fixtures\StrAttr::class, class_attribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)); + $this->assertSame('quick', class_attribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->string); + $this->assertSame('quick', class_attribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->string); + $this->assertSame('lazy', class_attribute(Fixtures\ParentClass::class, Fixtures\StrAttr::class)->string); + } + public function testClassBasename() { $this->assertSame('Baz', class_basename('Foo\Bar\Baz')); From 7ecd40fabc4bb9097ec7d1b58d2a2bf6618e2371 Mon Sep 17 00:00:00 2001 From: Sergey Danilchenko Date: Sat, 26 Apr 2025 21:48:28 +0200 Subject: [PATCH 2/5] fix tests --- .../Fixtures/ClassesWithAttributes.php | 24 ++++++++++++++----- tests/Support/SupportHelpersTest.php | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/tests/Support/Fixtures/ClassesWithAttributes.php b/tests/Support/Fixtures/ClassesWithAttributes.php index 6f178527cbf5..baed29f97fd3 100644 --- a/tests/Support/Fixtures/ClassesWithAttributes.php +++ b/tests/Support/Fixtures/ClassesWithAttributes.php @@ -5,25 +5,37 @@ use Attribute; #[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] -class UnusedAttr {} +class UnusedAttr +{ +} #[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] -class ParentOnlyAttr {} +class ParentOnlyAttr +{ +} #[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] class StrAttr { - public function __construct(public string $string) {} + public function __construct(public string $string) + { + } } #[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] class NumAttr { - public function __construct(public int $number) {} + public function __construct(public int $number) + { + } } #[StrAttr('lazy'), StrAttr('dog'), NumAttr(2), NumAttr(3), ParentOnlyAttr] -class ParentClass {} +class ParentClass +{ +} #[StrAttr('quick'), StrAttr('brown'), StrAttr('fox'), NumAttr(7)] -class ChildClass extends ParentClass {} +class ChildClass extends ParentClass +{ +} diff --git a/tests/Support/SupportHelpersTest.php b/tests/Support/SupportHelpersTest.php index fcf909e692d2..6224018b0354 100644 --- a/tests/Support/SupportHelpersTest.php +++ b/tests/Support/SupportHelpersTest.php @@ -91,7 +91,7 @@ public function jsonSerialize(): mixed public function testClassAttributes() { - require __DIR__.'/Fixtures/ClassesWithAttributes.php'; + require_once __DIR__.'/Fixtures/ClassesWithAttributes.php'; $this->assertSame([], class_attributes(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class)->toArray()); @@ -133,7 +133,7 @@ class_attributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->fla public function testClassAttribute() { - require __DIR__.'/Fixtures/ClassesWithAttributes.php'; + require_once __DIR__.'/Fixtures/ClassesWithAttributes.php'; $this->assertNull(class_attribute(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class)); $this->assertNull(class_attribute(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class, true)); From 1d06fb2abc12c79d2a725dda79569eaa4b228b36 Mon Sep 17 00:00:00 2001 From: Sergey Danilchenko Date: Sat, 26 Apr 2025 23:26:39 +0200 Subject: [PATCH 3/5] move functions to Reflector class --- src/Illuminate/Support/Reflector.php | 40 ++++++++++++++++++ src/Illuminate/Support/helpers.php | 44 -------------------- tests/Support/SupportHelpersTest.php | 57 -------------------------- tests/Support/SupportReflectorTest.php | 57 ++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 101 deletions(-) diff --git a/src/Illuminate/Support/Reflector.php b/src/Illuminate/Support/Reflector.php index a767d5ea7073..217515940504 100644 --- a/src/Illuminate/Support/Reflector.php +++ b/src/Illuminate/Support/Reflector.php @@ -2,6 +2,7 @@ namespace Illuminate\Support; +use ReflectionAttribute; use ReflectionClass; use ReflectionEnum; use ReflectionMethod; @@ -124,6 +125,45 @@ protected static function getTypeName($parameter, $type) return $name; } + /** + * Get specified class attribute(s), optionally following an inheritance chain. + * + * @template TTarget of object + * @template TAttribute of object + * + * @param TTarget|class-string $objectOrClass + * @param class-string $attribute + * @return ($ascend is true ? Collection, Collection> : Collection) + */ + public static function getAttributes($objectOrClass, $attribute, $ascend = false) + { + $refClass = new ReflectionClass($objectOrClass); + $attributes = []; + + do { + $attributes[$refClass->name] = new Collection(array_map( + fn (ReflectionAttribute $refAttr) => $refAttr->newInstance(), + $refClass->getAttributes($attribute) + )); + } while ($ascend && false !== $refClass = $refClass->getParentClass()); + + return $ascend ? new Collection($attributes) : reset($attributes); + } + + /** + * Get a specified class attribute, optionally following an inheritance chain. + * + * @template TAttribute of object + * + * @param object|class-string $objectOrClass + * @param class-string $attribute + * @return TAttribute|null + */ + public static function getAttribute($objectOrClass, $attribute, $ascend = false) + { + return static::getAttributes($objectOrClass, $attribute, $ascend)->flatten()->first(); + } + /** * Determine if the parameter's type is a subclass of the given type. * diff --git a/src/Illuminate/Support/helpers.php b/src/Illuminate/Support/helpers.php index fc08a28c65fa..8ecb1eb3cffe 100644 --- a/src/Illuminate/Support/helpers.php +++ b/src/Illuminate/Support/helpers.php @@ -4,7 +4,6 @@ use Illuminate\Contracts\Support\Htmlable; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; -use Illuminate\Support\Collection; use Illuminate\Support\Env; use Illuminate\Support\Fluent; use Illuminate\Support\HigherOrderTapProxy; @@ -79,49 +78,6 @@ function blank($value) } } -if (! function_exists('class_attributes')) { - /** - * Get specified class attribute(s), optionally following an inheritance chain. - * - * @template TTarget of object - * @template TAttribute of object - * - * @param TTarget|class-string $objectOrClass - * @param class-string $attribute - * @return ($ascend is true ? Collection, Collection> : Collection) - */ - function class_attributes($objectOrClass, $attribute, $ascend = false) - { - $refClass = new ReflectionClass($objectOrClass); - $attributes = []; - - do { - $attributes[$refClass->name] = new Collection(array_map( - fn (ReflectionAttribute $refAttr) => $refAttr->newInstance(), - $refClass->getAttributes($attribute) - )); - } while ($ascend && false !== $refClass = $refClass->getParentClass()); - - return $ascend ? new Collection($attributes) : reset($attributes); - } -} - -if (! function_exists('class_attribute')) { - /** - * Get a specified class attribute, optionally following an inheritance chain. - * - * @template TAttribute of object - * - * @param object|class-string $objectOrClass - * @param class-string $attribute - * @return TAttribute|null - */ - function class_attribute($objectOrClass, $attribute, $ascend = false) - { - return class_attributes($objectOrClass, $attribute, $ascend)->flatten()->first(); - } -} - if (! function_exists('class_basename')) { /** * Get the class "basename" of the given object / class. diff --git a/tests/Support/SupportHelpersTest.php b/tests/Support/SupportHelpersTest.php index 6224018b0354..ee67f818a3cd 100644 --- a/tests/Support/SupportHelpersTest.php +++ b/tests/Support/SupportHelpersTest.php @@ -89,63 +89,6 @@ public function jsonSerialize(): mixed $this->assertFalse(blank($model)); } - public function testClassAttributes() - { - require_once __DIR__.'/Fixtures/ClassesWithAttributes.php'; - - $this->assertSame([], class_attributes(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class)->toArray()); - - $this->assertSame( - [Fixtures\ChildClass::class => [], Fixtures\ParentClass::class => []], - class_attributes(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class, true)->toArray() - ); - - $this->assertSame( - ['quick', 'brown', 'fox'], - class_attributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->map->string->all() - ); - - $this->assertSame( - ['quick', 'brown', 'fox', 'lazy', 'dog'], - class_attributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->flatten()->map->string->all() - ); - - $this->assertSame(7, class_attributes(Fixtures\ChildClass::class, Fixtures\NumAttr::class)->sum->number); - $this->assertSame(12, class_attributes(Fixtures\ChildClass::class, Fixtures\NumAttr::class, true)->flatten()->sum->number); - $this->assertSame(5, class_attributes(Fixtures\ParentClass::class, Fixtures\NumAttr::class)->sum->number); - $this->assertSame(5, class_attributes(Fixtures\ParentClass::class, Fixtures\NumAttr::class, true)->flatten()->sum->number); - - $this->assertSame( - [Fixtures\ChildClass::class, Fixtures\ParentClass::class], - class_attributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->keys()->all() - ); - - $this->assertContainsOnlyInstancesOf( - Fixtures\StrAttr::class, - class_attributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->all() - ); - - $this->assertContainsOnlyInstancesOf( - Fixtures\StrAttr::class, - class_attributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->flatten()->all() - ); - } - - public function testClassAttribute() - { - require_once __DIR__.'/Fixtures/ClassesWithAttributes.php'; - - $this->assertNull(class_attribute(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class)); - $this->assertNull(class_attribute(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class, true)); - $this->assertNull(class_attribute(Fixtures\ChildClass::class, Fixtures\ParentOnlyAttr::class)); - $this->assertInstanceOf(Fixtures\ParentOnlyAttr::class, class_attribute(Fixtures\ChildClass::class, Fixtures\ParentOnlyAttr::class, true)); - $this->assertInstanceOf(Fixtures\StrAttr::class, class_attribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class)); - $this->assertInstanceOf(Fixtures\StrAttr::class, class_attribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)); - $this->assertSame('quick', class_attribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->string); - $this->assertSame('quick', class_attribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->string); - $this->assertSame('lazy', class_attribute(Fixtures\ParentClass::class, Fixtures\StrAttr::class)->string); - } - public function testClassBasename() { $this->assertSame('Baz', class_basename('Foo\Bar\Baz')); diff --git a/tests/Support/SupportReflectorTest.php b/tests/Support/SupportReflectorTest.php index 2933ff090795..ba8873baf200 100644 --- a/tests/Support/SupportReflectorTest.php +++ b/tests/Support/SupportReflectorTest.php @@ -75,6 +75,63 @@ public function testIsCallable() $this->assertFalse(Reflector::isCallable(['TotallyMissingClass', 'foo'])); $this->assertTrue(Reflector::isCallable(['TotallyMissingClass', 'foo'], true)); } + + public function testGetAttributes() + { + require_once __DIR__.'/Fixtures/ClassesWithAttributes.php'; + + $this->assertSame([], Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class)->toArray()); + + $this->assertSame( + [Fixtures\ChildClass::class => [], Fixtures\ParentClass::class => []], + Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class, true)->toArray() + ); + + $this->assertSame( + ['quick', 'brown', 'fox'], + Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->map->string->all() + ); + + $this->assertSame( + ['quick', 'brown', 'fox', 'lazy', 'dog'], + Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->flatten()->map->string->all() + ); + + $this->assertSame(7, Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\NumAttr::class)->sum->number); + $this->assertSame(12, Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\NumAttr::class, true)->flatten()->sum->number); + $this->assertSame(5, Reflector::getAttributes(Fixtures\ParentClass::class, Fixtures\NumAttr::class)->sum->number); + $this->assertSame(5, Reflector::getAttributes(Fixtures\ParentClass::class, Fixtures\NumAttr::class, true)->flatten()->sum->number); + + $this->assertSame( + [Fixtures\ChildClass::class, Fixtures\ParentClass::class], + Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->keys()->all() + ); + + $this->assertContainsOnlyInstancesOf( + Fixtures\StrAttr::class, + Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->all() + ); + + $this->assertContainsOnlyInstancesOf( + Fixtures\StrAttr::class, + Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->flatten()->all() + ); + } + + public function testGetAttribute() + { + require_once __DIR__.'/Fixtures/ClassesWithAttributes.php'; + + $this->assertNull(Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class)); + $this->assertNull(Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class, true)); + $this->assertNull(Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\ParentOnlyAttr::class)); + $this->assertInstanceOf(Fixtures\ParentOnlyAttr::class, Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\ParentOnlyAttr::class, true)); + $this->assertInstanceOf(Fixtures\StrAttr::class, Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class)); + $this->assertInstanceOf(Fixtures\StrAttr::class, Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)); + $this->assertSame('quick', Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->string); + $this->assertSame('quick', Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->string); + $this->assertSame('lazy', Reflector::getAttribute(Fixtures\ParentClass::class, Fixtures\StrAttr::class)->string); + } } class A From 1d30a7e1c04e12c695c601be6cb0b6b01e3a2007 Mon Sep 17 00:00:00 2001 From: Sergey Danilchenko Date: Sun, 27 Apr 2025 12:53:11 +0200 Subject: [PATCH 4/5] rename to getClassAttribute(s) --- src/Illuminate/Support/Reflector.php | 6 ++-- tests/Support/SupportReflectorTest.php | 44 +++++++++++++------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Illuminate/Support/Reflector.php b/src/Illuminate/Support/Reflector.php index 217515940504..87c095a4f63a 100644 --- a/src/Illuminate/Support/Reflector.php +++ b/src/Illuminate/Support/Reflector.php @@ -135,7 +135,7 @@ protected static function getTypeName($parameter, $type) * @param class-string $attribute * @return ($ascend is true ? Collection, Collection> : Collection) */ - public static function getAttributes($objectOrClass, $attribute, $ascend = false) + public static function getClassAttributes($objectOrClass, $attribute, $ascend = false) { $refClass = new ReflectionClass($objectOrClass); $attributes = []; @@ -159,9 +159,9 @@ public static function getAttributes($objectOrClass, $attribute, $ascend = false * @param class-string $attribute * @return TAttribute|null */ - public static function getAttribute($objectOrClass, $attribute, $ascend = false) + public static function getClassAttribute($objectOrClass, $attribute, $ascend = false) { - return static::getAttributes($objectOrClass, $attribute, $ascend)->flatten()->first(); + return static::getClassAttributes($objectOrClass, $attribute, $ascend)->flatten()->first(); } /** diff --git a/tests/Support/SupportReflectorTest.php b/tests/Support/SupportReflectorTest.php index ba8873baf200..67e438361d8f 100644 --- a/tests/Support/SupportReflectorTest.php +++ b/tests/Support/SupportReflectorTest.php @@ -76,61 +76,61 @@ public function testIsCallable() $this->assertTrue(Reflector::isCallable(['TotallyMissingClass', 'foo'], true)); } - public function testGetAttributes() + public function testGetClassAttributes() { require_once __DIR__.'/Fixtures/ClassesWithAttributes.php'; - $this->assertSame([], Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class)->toArray()); + $this->assertSame([], Reflector::getClassAttributes(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class)->toArray()); $this->assertSame( [Fixtures\ChildClass::class => [], Fixtures\ParentClass::class => []], - Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class, true)->toArray() + Reflector::getClassAttributes(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class, true)->toArray() ); $this->assertSame( ['quick', 'brown', 'fox'], - Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->map->string->all() + Reflector::getClassAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->map->string->all() ); $this->assertSame( ['quick', 'brown', 'fox', 'lazy', 'dog'], - Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->flatten()->map->string->all() + Reflector::getClassAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->flatten()->map->string->all() ); - $this->assertSame(7, Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\NumAttr::class)->sum->number); - $this->assertSame(12, Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\NumAttr::class, true)->flatten()->sum->number); - $this->assertSame(5, Reflector::getAttributes(Fixtures\ParentClass::class, Fixtures\NumAttr::class)->sum->number); - $this->assertSame(5, Reflector::getAttributes(Fixtures\ParentClass::class, Fixtures\NumAttr::class, true)->flatten()->sum->number); + $this->assertSame(7, Reflector::getClassAttributes(Fixtures\ChildClass::class, Fixtures\NumAttr::class)->sum->number); + $this->assertSame(12, Reflector::getClassAttributes(Fixtures\ChildClass::class, Fixtures\NumAttr::class, true)->flatten()->sum->number); + $this->assertSame(5, Reflector::getClassAttributes(Fixtures\ParentClass::class, Fixtures\NumAttr::class)->sum->number); + $this->assertSame(5, Reflector::getClassAttributes(Fixtures\ParentClass::class, Fixtures\NumAttr::class, true)->flatten()->sum->number); $this->assertSame( [Fixtures\ChildClass::class, Fixtures\ParentClass::class], - Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->keys()->all() + Reflector::getClassAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->keys()->all() ); $this->assertContainsOnlyInstancesOf( Fixtures\StrAttr::class, - Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->all() + Reflector::getClassAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->all() ); $this->assertContainsOnlyInstancesOf( Fixtures\StrAttr::class, - Reflector::getAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->flatten()->all() + Reflector::getClassAttributes(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->flatten()->all() ); } - public function testGetAttribute() + public function testGetClassAttribute() { require_once __DIR__.'/Fixtures/ClassesWithAttributes.php'; - $this->assertNull(Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class)); - $this->assertNull(Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class, true)); - $this->assertNull(Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\ParentOnlyAttr::class)); - $this->assertInstanceOf(Fixtures\ParentOnlyAttr::class, Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\ParentOnlyAttr::class, true)); - $this->assertInstanceOf(Fixtures\StrAttr::class, Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class)); - $this->assertInstanceOf(Fixtures\StrAttr::class, Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)); - $this->assertSame('quick', Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->string); - $this->assertSame('quick', Reflector::getAttribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->string); - $this->assertSame('lazy', Reflector::getAttribute(Fixtures\ParentClass::class, Fixtures\StrAttr::class)->string); + $this->assertNull(Reflector::getClassAttribute(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class)); + $this->assertNull(Reflector::getClassAttribute(Fixtures\ChildClass::class, Fixtures\UnusedAttr::class, true)); + $this->assertNull(Reflector::getClassAttribute(Fixtures\ChildClass::class, Fixtures\ParentOnlyAttr::class)); + $this->assertInstanceOf(Fixtures\ParentOnlyAttr::class, Reflector::getClassAttribute(Fixtures\ChildClass::class, Fixtures\ParentOnlyAttr::class, true)); + $this->assertInstanceOf(Fixtures\StrAttr::class, Reflector::getClassAttribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class)); + $this->assertInstanceOf(Fixtures\StrAttr::class, Reflector::getClassAttribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)); + $this->assertSame('quick', Reflector::getClassAttribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class)->string); + $this->assertSame('quick', Reflector::getClassAttribute(Fixtures\ChildClass::class, Fixtures\StrAttr::class, true)->string); + $this->assertSame('lazy', Reflector::getClassAttribute(Fixtures\ParentClass::class, Fixtures\StrAttr::class)->string); } } From 45248efc4fe94d8d696a5970a24192dd74b021c2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 29 Apr 2025 15:10:14 -0500 Subject: [PATCH 5/5] formatting --- src/Illuminate/Support/Reflector.php | 79 ++++++++++++++-------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/src/Illuminate/Support/Reflector.php b/src/Illuminate/Support/Reflector.php index 87c095a4f63a..f5eb72f0fcdd 100644 --- a/src/Illuminate/Support/Reflector.php +++ b/src/Illuminate/Support/Reflector.php @@ -57,6 +57,46 @@ public static function isCallable($var, $syntaxOnly = false) return false; } + /** + * Get the specified class attribute, optionally following an inheritance chain. + * + * @template TAttribute of object + * + * @param object|class-string $objectOrClass + * @param class-string $attribute + * @return TAttribute|null + */ + public static function getClassAttribute($objectOrClass, $attribute, $ascend = false) + { + return static::getClassAttributes($objectOrClass, $attribute, $ascend)->flatten()->first(); + } + + /** + * Get the specified class attribute(s), optionally following an inheritance chain. + * + * @template TTarget of object + * @template TAttribute of object + * + * @param TTarget|class-string $objectOrClass + * @param class-string $attribute + * @return ($includeParents is true ? Collection, Collection> : Collection) + */ + public static function getClassAttributes($objectOrClass, $attribute, $includeParents = false) + { + $reflectionClass = new ReflectionClass($objectOrClass); + + $attributes = []; + + do { + $attributes[$reflectionClass->name] = new Collection(array_map( + fn (ReflectionAttribute $reflectionAttribute) => $reflectionAttribute->newInstance(), + $reflectionClass->getAttributes($attribute) + )); + } while ($includeParents && false !== $reflectionClass = $reflectionClass->getParentClass()); + + return $includeParents ? new Collection($attributes) : reset($attributes); + } + /** * Get the class name of the given parameter's type, if possible. * @@ -125,45 +165,6 @@ protected static function getTypeName($parameter, $type) return $name; } - /** - * Get specified class attribute(s), optionally following an inheritance chain. - * - * @template TTarget of object - * @template TAttribute of object - * - * @param TTarget|class-string $objectOrClass - * @param class-string $attribute - * @return ($ascend is true ? Collection, Collection> : Collection) - */ - public static function getClassAttributes($objectOrClass, $attribute, $ascend = false) - { - $refClass = new ReflectionClass($objectOrClass); - $attributes = []; - - do { - $attributes[$refClass->name] = new Collection(array_map( - fn (ReflectionAttribute $refAttr) => $refAttr->newInstance(), - $refClass->getAttributes($attribute) - )); - } while ($ascend && false !== $refClass = $refClass->getParentClass()); - - return $ascend ? new Collection($attributes) : reset($attributes); - } - - /** - * Get a specified class attribute, optionally following an inheritance chain. - * - * @template TAttribute of object - * - * @param object|class-string $objectOrClass - * @param class-string $attribute - * @return TAttribute|null - */ - public static function getClassAttribute($objectOrClass, $attribute, $ascend = false) - { - return static::getClassAttributes($objectOrClass, $attribute, $ascend)->flatten()->first(); - } - /** * Determine if the parameter's type is a subclass of the given type. *