1
0
Fork 0
mirror of https://github.com/archtechx/enums.git synced 2025-12-15 04:44:05 +00:00
This commit is contained in:
Samuel Štancl 2022-03-29 19:50:02 +02:00
parent cc5bba1912
commit 7b0871db00
7 changed files with 389 additions and 46 deletions

22
src/Meta/Meta.php Normal file
View file

@ -0,0 +1,22 @@
<?php
namespace ArchTech\Enums\Meta;
use Attribute;
#[Attribute(Attribute::TARGET_CLASS)]
class Meta
{
/** @var MetaProperty[] */
public array $metaProperties;
public function __construct(array|string ...$metaProperties) {
// When an array is passed, it'll be wrapped in an outer array due to the ...variadic parameter
if (isset($metaProperties[0]) && is_array($metaProperties[0])) {
// Extract the inner array
$metaProperties = $metaProperties[0];
}
$this->metaProperties = $metaProperties;
}
}

36
src/Meta/MetaProperty.php Normal file
View file

@ -0,0 +1,36 @@
<?php
namespace ArchTech\Enums\Meta;
abstract class MetaProperty
{
public function __construct(
public mixed $value,
) {
$this->value = $this->transform($value);
}
public static function make(mixed $value): static
{
return new static($value);
}
protected function transform(mixed $value): mixed
{
// Feel free to override this to transform the value during instantiation
return $value;
}
/** Get the name of the accessor method */
public static function method(): string
{
if (property_exists(static::class, 'method')) {
return static::${'method'};
}
$parts = explode('\\', static::class);
return lcfirst(end($parts));
}
}

63
src/Meta/Reflection.php Normal file
View file

@ -0,0 +1,63 @@
<?php
namespace ArchTech\Enums\Meta;
use ReflectionAttribute;
use ReflectionEnumUnitCase;
use ReflectionObject;
class Reflection
{
/**
* Get the meta properties enabled on an Enum.
*
* @param Enum&\ArchTech\Enums\Metadata $enum
* @return string[]|array<\class-string<MetaProperty>>
*/
public static function metaProperties(mixed $enum): array
{
$reflection = new ReflectionObject($enum);
// Attributes of the `Meta` type
$attributes = array_values(array_filter(
$reflection->getAttributes(),
fn (ReflectionAttribute $attr) => $attr->getName() === Meta::class,
));
if ($attributes) {
return $attributes[0]->newInstance()->metaProperties;
}
return [];
}
/**
* Get the value of a meta property on the provided enum.
*
* @param \class-string<MetaProperty> $property
* @param Enum $enum
* @return mixed
*/
public static function metaValue(string $metaProperty, mixed $enum): mixed
{
// Find the case used by $enum
$reflection = new ReflectionEnumUnitCase($enum::class, $enum->name);
$attributes = $reflection->getAttributes();
// Instantiate each ReflectionAttribute
/** @var MetaProperty[] $properties */
$properties = array_map(fn (ReflectionAttribute $attr) => $attr->newInstance(), $attributes);
// Find the property that matches the $metaProperty class
$properties = array_filter($properties, fn (MetaProperty $property) => $property::class === $metaProperty);
// Reset array index
$properties = array_values($properties);
if ($properties) {
return $properties[0]->value;
}
return null;
}
}

43
src/Metadata.php Normal file
View file

@ -0,0 +1,43 @@
<?php
namespace ArchTech\Enums;
use ArchTech\Enums\Meta\MetaProperty;
use ValueError;
trait Metadata
{
/** Try to get the first case with this meta property value. */
public static function tryFromMeta(MetaProperty $metaProperty): static|null
{
foreach (static::cases() as $case) {
if (Meta\Reflection::metaValue($metaProperty::class, $case) === $metaProperty->value) {
return $case;
}
}
return null;
}
/** Get the first case with this meta property value. */
public static function fromMeta(MetaProperty $metaProperty): static
{
return static::tryFromMeta($metaProperty) ?? throw new ValueError(
'Enum ' . static::class . ' does not have a case with a meta property "' .
$metaProperty::class . '" of value "' . $metaProperty->value . '"'
);
}
public function __call(string $property, $arguments): mixed
{
$metaProperties = Meta\Reflection::metaProperties($this);
foreach ($metaProperties as $metaProperty) {
if ($metaProperty::method() === $property) {
return Meta\Reflection::metaValue($metaProperty, $this);
}
}
return null;
}
}