Skip to content

Commit

Permalink
attribute support
Browse files Browse the repository at this point in the history
  • Loading branch information
henzeb committed May 12, 2023
1 parent de06d40 commit aad11d0
Show file tree
Hide file tree
Showing 10 changed files with 438 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All notable changes to `Enumhancer` will be documented in this file

## 2.1.0 - 2023-05-12

- Support for [Attributes](docs/attributes.md)

## 2.0.0 - 2023-02-28

- Now supports Laravel 10
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ implemented the methods of `Getters`, `Extractor` and `Reporters`.

### Features

- [Attributes](docs/attributes.md)
- [Bitmasks](docs/bitmasks.md)
- [Constructor](docs/constructor.md)
- [Comparison](docs/comparison.md)
Expand Down
145 changes: 145 additions & 0 deletions docs/attributes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Attributes

Enumhancer supports [Attributes](https://www.php.net/manual/en/language.attributes.overview.php).
You simply declare your attribute and Enumhancer will allow you to access them.

In most situations, you want to define a method to get the value nicely,
so the `getAttribute` method is `protected` by default.

## Fetch a single case attribute

````php
#[Attribute(Attribute::TARGET_CLASS_CONSTANT)]
class Description {
public function __construct(public string $value) {}
}
````

````php
enum Suit {
use Henzeb\Enumhancer\Concerns\Attributes;

#[Description('Suit of Hearts')]
case Hearts;

#[Description('Suit of Clubs')]
case Clubs;

#[Description('Suit of Spades')]
case Spades;

case Diamonds;

public function getDescription(): ?string
{
return $this->getAttribute(Description::class)?->value;
}
}
````

````php
Suit::Hearts->getDescription(); // returns 'Card of Hearts'
Suit::Clubs->getDescription(); // returns 'Card of Clubs'
Suit::Diamonds->getDescription(); // returns null
````

## Fetch multiple case attributes

Sometimes, you might want to work with repeatable attributes.

````php
#[Attribute(Attribute::TARGET_CLASS_CONSTANT)]
class Description {
public function __construct(public string $value) {}
}

#[Attribute(Attribute::TARGET_CLASS_CONSTANT)]
class Color {
public function __construct(public string $value) {}
}
````

````php
enum Suit {
use Henzeb\Enumhancer\Concerns\Attributes;

#[Description('Card of Hearts'), Color('red')]
case Hearts;

#[Description('Card of Clubs'), Color('black')]
case Clubs;

#[Description('Card of Spades')]
case Spades;

case Diamonds;

public function getAllDescriptions(): []
{
return $this->getAttributes(Description::class);
}

public function getAllAttributes(): []
{
return $this->getAttributes();
}
}
````

````php
Suit::Hearts->getAllDescriptions(); // returns [new Description('Card of Hearts')]
Suit::Diamonds->getAllDescriptions(); // returns []

Suit::Hearts->getAllAttributes(); // returns [new Description('Card of Hearts'), new Color('red')]
Suit::Spades->getAllAttributes(); // returns [new Description('Card of Hearts')]
````

## Fetching class attributes

````php
#[Attribute(Attribute::TARGET_CLASS)]
class Description {
public function __construct(public string $value) {}
}

#[Attribute(Attribute::TARGET_CLASS)]
class CardCount {
public function __construct(public int $value) {}
}
````

````php
#[Description('deck of cards'), CardCount(52)]
enum Suit {
use Henzeb\Enumhancer\Concerns\Attributes;

public static function getSuitDescription(): string
{
return self::getEnumAttribute(Description::class)->value
}

public static function getCardCount(): int
{
return self::getEnumAttribute(CardCount::class)->value
}

public static function getSuitDescriptions(): array
{
return self::getAttributes(Description::class);
}

public static function getSuitAttributes(): array
{
return self::getAttributes();
}
}
````

````php
Suit::getSuitDescription(); // returns deck of cards
Suit::getCardcount(); // returns 52

Suit::getSuitDescriptions(); //returns [new Description('deck of cards')]
Suit::getSuitAttributes(); //returns [new Description('deck of cards'), new CardCount(52)]
````

28 changes: 28 additions & 0 deletions src/Concerns/Attributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Henzeb\Enumhancer\Concerns;

use Henzeb\Enumhancer\Helpers\EnumAttributes;

trait Attributes
{
protected function getAttribute(string $attributeClass): mixed
{
return EnumAttributes::fromCase(self::class, $this, $attributeClass);
}

protected function getAttributes(string $attributeClass = null): array
{
return EnumAttributes::fromCaseArray(self::class, $this, $attributeClass);
}

protected static function getEnumAttribute(string $attributeClass): mixed
{
return EnumAttributes::fromEnum(self::class, $attributeClass);
}

protected static function getEnumAttributes(string $attributeClass = null): array
{
return EnumAttributes::fromEnumArray(self::class, $attributeClass);
}
}
65 changes: 65 additions & 0 deletions src/Helpers/EnumAttributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Henzeb\Enumhancer\Helpers;

use ReflectionAttribute;
use ReflectionClass;
use ReflectionClassConstant;
use UnitEnum;

final class EnumAttributes
{
public static function fromCase(string $enumClass, UnitEnum $case, string $attributeClass): mixed
{
EnumCheck::check($case, $enumClass);

$enumAttributes = (new ReflectionClassConstant($enumClass, $case->name))
->getAttributes($attributeClass, ReflectionAttribute::IS_INSTANCEOF);

if (count($enumAttributes) > 0) {
return $enumAttributes[0]->newInstance();
}

return null;
}

public static function fromCaseArray(string $enumClass, UnitEnum $case, string $attributeClass = null): array
{
EnumCheck::check($case, $enumClass);

$enumAttributes = (new ReflectionClassConstant($enumClass, $case->name))
->getAttributes($attributeClass, ReflectionAttribute::IS_INSTANCEOF);

return array_map(
fn($enumAttribute) => $enumAttribute->newInstance(),
$enumAttributes
);
}

public static function fromEnum(string $enumClass, string $attributeClass): mixed
{
EnumCheck::check($enumClass);

$enumAttributes = (new ReflectionClass($enumClass))
->getAttributes($attributeClass, ReflectionAttribute::IS_INSTANCEOF);

if (count($enumAttributes) > 0) {
return $enumAttributes[0]->newInstance();
}

return null;
}

public static function fromEnumArray(string $enumClass, string $attributeClass = null): array
{
EnumCheck::check($enumClass);

$enumAttributes = (new ReflectionClass($enumClass))
->getAttributes($attributeClass, ReflectionAttribute::IS_INSTANCEOF);

return array_map(
fn($enumAttribute) => $enumAttribute->newInstance(),
$enumAttributes
);
}
}
11 changes: 11 additions & 0 deletions tests/Fixtures/UnitEnums/Attributes/AnotherAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Henzeb\Enumhancer\Tests\Fixtures\UnitEnums\Attributes;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS_CONSTANT | Attribute::TARGET_CLASS)]
class AnotherAttribute
{

}
26 changes: 26 additions & 0 deletions tests/Fixtures/UnitEnums/Attributes/AttributesEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Henzeb\Enumhancer\Tests\Fixtures\UnitEnums\Attributes;

use Henzeb\Enumhancer\Concerns\Attributes;

enum AttributesEnum: string
{
use Attributes {
getAttribute as public;
getAttributes as public;
getEnumAttribute as public;
getEnumAttributes as public;
}

#[Description('has description')]
case WithAttribute = 'with';

case WithoutAttribute = 'without';

#[Description('has description'), Description('and another one')]
case WithMultipleAttributes = 'multiple';

#[Description('has description'), AnotherAttribute]
case WithMixedAttributes = 'mixed';
}
16 changes: 16 additions & 0 deletions tests/Fixtures/UnitEnums/Attributes/ClassAttributesEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Henzeb\Enumhancer\Tests\Fixtures\UnitEnums\Attributes;

use Henzeb\Enumhancer\Concerns\Attributes;

#[Description('test'), AnotherAttribute]
enum ClassAttributesEnum: string
{
use Attributes {
getEnumAttribute as public;
getEnumAttributes as public;
}

case Attribute = 'attribute';
}
13 changes: 13 additions & 0 deletions tests/Fixtures/UnitEnums/Attributes/Description.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Henzeb\Enumhancer\Tests\Fixtures\UnitEnums\Attributes;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS_CONSTANT | Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
class Description
{
public function __construct(public string $value)
{
}
}
Loading

0 comments on commit aad11d0

Please sign in to comment.