ldc-crm/app/Traits/Enums/BaseEnum.php
2024-10-15 14:10:04 +02:00

310 lines
8.2 KiB
PHP

<?php
namespace App\Traits\Enums;
use Doctrine\Inflector\InflectorFactory;
trait BaseEnum
{
// * GENERIC METHODS
/**
* Get enum value.
*
* @return string|integer
*/
public function value(): string|int
{
return self::toValues()[$this->name];
}
/**
* Get enum label.
*
* @return string
*/
public function label(): string
{
return self::toLabels()[$this->name];
}
/**
* Get enum name.
*
* @return string
*/
public function name(): string
{
return $this->name;
}
// * STATIC FETCH METHODS
/**
* Get enum labels.
*
* @return array<string>
*/
public static function toLabels(): array
{
if (empty(self::getCache('labels'))) {
$labels = self::getLabelsConstant();
$array = [];
\array_map(function ($enum) use (&$array, $labels) {
if (!\is_null($labels) and !isset($labels[$enum->name])) {
throw new \Error('Missing definition label ' . self::class . " for {$enum->name}");
}
$array = array_merge($array, [$enum->name => __(
!\is_null($labels) ? $labels[$enum->name] : ucfirst($enum->name)
)]);
}, self::cases());
self::setCache('labels', $array);
}
return self::getCache('labels');
}
/**
* Get enum values.
*
* @return array<integer|string>
*/
public static function toValues(): array
{
if (empty(self::getCache('values'))) {
$array = [];
\array_map(function ($enum) use (&$array) {
$array = array_merge($array, [$enum->name => $enum->value]);
}, self::cases());
self::setCache('values', $array);
}
return self::getCache('values');
}
/**
* Get enums.
*
* @return array
*/
public static function all(): array
{
return self::cases();
}
/**
* Get enums.
*
* @return array
*/
public static function toArray(): array
{
if (empty(self::getCache('enums'))) {
$definitions = self::getCustomDefinitions();
$array = [];
$inflector = InflectorFactory::create()->build();
\array_map(function ($enum) use (&$array, $definitions, $inflector) {
$baseArray = [
'name' => $enum->name,
'label' => $enum->label(),
'value' => $enum->value,
];
foreach ($definitions as $index => $elements) {
$index = $inflector->singularize(strtolower($index));
if (!isset($elements[$enum->name])) {
throw new \Error("Missing definition {$index} " . self::class . " for {$enum->name}");
}
$baseArray[$index] = $elements[$enum->name];
}
$array = array_merge($array, [$enum->name => (object)$baseArray]);
}, self::cases());
self::setCache('enums', $array);
} //end if
return self::getCache('enums');
}
/**
* Fetch enum using value.
*
* @param \App\Traits\Enums\BaseEnum|integer|string $value
* @param string $field
* @return \App\Traits\Enums\BaseEnum|null
*/
public static function make(self|int|string $value, string $field = 'value'): self|null
{
return !\is_object($value) ? self::cases()[array_search($value, array_column(self::cases(), $field))] : $value;
}
/**
* Test if 2 enum are equals.
*
* @param object ...$others
* @return boolean
* @throws \Error If provided element are not enums taht extends BaseEnum.
*/
public function equals(object ...$others): bool
{
foreach ($others as $other) {
if (!\in_array(BaseEnum::class, $this->classUsesRecursive($other))) {
throw new \Error(__METHOD__ .
' requires $enum to use ' . BaseEnum::class);
}
if (
get_class($this) === get_class($other)
&& $this->value === $other->value
) {
return true;
}
}
return false;
}
/**
* Serialize to json.
*
* @return mixed
*/
public function jsonSerialize(): mixed
{
return $this->value;
}
// * CACHE
/**
* Get cache array.
*
* @param string $index
* @return array
*/
private static function getCache(string $index): array
{
self::initCache();
if (!isset($GLOBALS[self::class]['props'][$index])) {
$GLOBALS[self::class]['props'][$index] = [];
}
return $GLOBALS[self::class]['props'][$index];
}
/**
* Set cache array.
*
* @param string $index
* @param array $items
* @return void
*/
private static function setCache(string $index, array $items): void
{
self::initCache();
if (!isset($GLOBALS[self::class]['props'][$index])) {
$GLOBALS[self::class]['props'][$index] = [];
}
$GLOBALS[self::class]['props'][$index] = $items;
}
/**
* Init cache if needed.
*
* @return void
*/
private static function initCache(): void
{
if (!isset($GLOBALS[self::class])) {
$GLOBALS[self::class] = [
'props' => [
'enums' => [],
'values' => [],
'labels' => []
],
'customProps' => null
];
}
}
// * PRIVATE
/**
* Fetch the LABELS constant if exists.
*
* @return array|null
*/
private static function getLabelsConstant(): array|null
{
$class_name = self::class;
try {
$constant_reflex = new \ReflectionClassConstant($class_name, 'LABELS');
return $constant_reflex->getValue();
} catch (\ReflectionException $e) {
return null;
}
}
/**
* Get custom constants.
*
* @return array
*/
private static function getCustomDefinitions(): array
{
// ? Has custom props in cache ?
if (!isset($GLOBALS[self::class]) or \is_null($GLOBALS[self::class]['customProps'])) {
$names = [];
\array_map(function (self $enum) use (&$names) {
$names = array_merge($names, [$enum->name => $enum]);
}, self::cases());
$GLOBALS[self::class]['customProps'] = \array_filter(
(new \ReflectionClass(self::class))->getConstants(),
function (mixed $constant, string $key) use ($names) {
if ($key === 'LABELS' or !\is_array($constant)) {
return false;
}
return count(\array_filter(
array_flip($constant),
function (string $name) use ($names) {
return array_key_exists($name, $names);
}
));
},
ARRAY_FILTER_USE_BOTH
);
} //end if
return $GLOBALS[self::class]['customProps'];
}
/**
* Returns all traits used by a class, its parent classes and trait of their traits.
*
* @param object|string $class
* @return array
*/
private function classUsesRecursive($class)
{
if (is_object($class)) {
$class = get_class($class);
}
$results = [];
foreach (array_reverse(class_parents($class) ?: []) + [$class => $class] as $class) {
$results += $this->traitUsesRecursive($class);
}
return array_unique($results);
}
/**
* Returns all traits used by a trait and its traits.
*
* @param object|string $trait
* @return array
*/
private function traitUsesRecursive($trait)
{
$traits = class_uses($trait) ?: [];
foreach ($traits as $trait) {
$traits += $this->traitUsesRecursive($trait);
}
return $traits;
}
}