Skip to content

Commit 829d9cc

Browse files
committed
Allow class inheritance determination for enums
### Motivation In the scope of [a downstream project issue](Crell/Serde#10), It was noticed that in cases where an enum implemented an interface, the interface attributes could not be read. This change allows `ReflectionEnum` to retrieve such information if it exists
1 parent 8c52fcf commit 829d9cc

4 files changed

Lines changed: 71 additions & 3 deletions

File tree

README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,54 @@ In this case, a class may be marked with either `Screen` or `Audio`, but not bot
818818

819819
In this example, `$displayInfoA->type` will be an instance of `Screen`, `$displayInfoB->type` will be an instance of `Audio`, and `$displayInfoC->type` will be `null`.
820820

821+
### Interface using attributes
822+
823+
Interfaces may also implement attributes. This is useful for cases in which you want to define a type map at the interface level.
824+
825+
Note: in this case, make sure the class implements the `Inheritable` interface, to return the expected values.
826+
827+
```php
828+
use Crell\AttributeUtils\Inheritable;
829+
830+
#[Attribute(Attribute::TARGET_CLASS)]
831+
final class Application implements Inheritable
832+
{
833+
public function __construct(
834+
public readonly string $name,
835+
) {
836+
}
837+
}
838+
839+
#[Application(name: 'app')]
840+
interface Something
841+
{
842+
}
843+
844+
enum MyEnum: string implements Something
845+
{
846+
case A = 'a';
847+
case B = 'b';
848+
}
849+
850+
#[Application(name: 'other-app')]
851+
enum AnotherEnum: string implements Something
852+
{
853+
case A = 'a';
854+
case B = 'b';
855+
}
856+
857+
858+
$analyzer = new Crell\AttributeUtils\Analyzer();
859+
860+
/** @var Application $anotherEnumAttributes */
861+
$enumAttributes = $analyzer->analyze(MyEnum::class, Application::class);
862+
print $enumAttributes->name . PHP_EOL; // Prints 'app'
863+
864+
/** @var Application $classAttributes */
865+
$anotherEnumAttributes = $analyzer->analyze(AnotherEnum::class, Application::class);
866+
print $anotherEnumAttributes->name . PHP_EOL; // Prints 'other-app'
867+
```
868+
821869
## Change log
822870

823871
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

src/AttributeParser.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,7 @@ protected function attributeInheritanceTree(\Reflector $subject, string $attribu
224224
\ReflectionMethod::class => $this->classElementInheritanceTree($subject),
225225
\ReflectionClassConstant::class => $this->classElementInheritanceTree($subject),
226226
\ReflectionParameter::class => $this->parameterInheritanceTree($subject),
227-
// If it's an enum, there's nothing to inherit so just stub that out.
228-
\ReflectionEnum::class => [],
227+
\ReflectionEnum::class => $this->classInheritanceTree($subject),
229228
};
230229
}
231230
}
@@ -235,7 +234,7 @@ protected function attributeInheritanceTree(\Reflector $subject, string $attribu
235234
*
236235
* This includes both classes and interfaces.
237236
*
238-
* @param \ReflectionClass<object> $subject
237+
* @param \ReflectionClass<object>|\ReflectionEnum<\UnitEnum> $subject
239238
* The reflection of the class for which we want the ancestors.
240239
* @return iterable<\ReflectionClass<object>>
241240
* @throws \ReflectionException

tests/ClassAnalyzerTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
use Crell\AttributeUtils\Records\ClassWithScopesMulti;
5050
use Crell\AttributeUtils\Records\ClassWithScopesNotDefault;
5151
use Crell\AttributeUtils\Records\ClassWithSubAttributes;
52+
use Crell\AttributeUtils\Records\EnumWithInterface;
5253
use Crell\AttributeUtils\Records\LabeledApp;
5354
use Crell\AttributeUtils\Records\MissingPropertyAttributeArguments;
5455
use Crell\AttributeUtils\Records\MultiuseClass;
@@ -416,6 +417,15 @@ public static function attributeTestProvider(): \Generator
416417
},
417418
];
418419

420+
yield 'Enum implementing interface' => [
421+
'subject' => EnumWithInterface::class,
422+
'attribute' => BasicClass::class,
423+
'test' => static function(mixed $classDef) {
424+
self::assertEquals(5, $classDef->a);
425+
self::assertEquals(10, $classDef->b);
426+
},
427+
];
428+
419429
yield 'Field takes defaults from class' => [
420430
'subject' => PropertyThatTakesClassDefault::class,
421431
'attribute' => PropertyTakesClassDefaultClass::class,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Crell\AttributeUtils\Records;
6+
7+
enum EnumWithInterface: int implements AnInterface
8+
{
9+
case A = 1;
10+
case B = 2;
11+
}

0 commit comments

Comments
 (0)