160 lines
4.6 KiB
PHP
160 lines
4.6 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace Aether\Traits;
|
||
|
||
use App\Notice\Enum\NoticeStatusEnum;
|
||
use App\Notice\Model\NoticeStatsModel;
|
||
use InvalidArgumentException;
|
||
use ReflectionClass;
|
||
|
||
trait AetherEnum
|
||
{
|
||
/**
|
||
* 获取所有枚举值数组(严格保持定义顺序).
|
||
* @return array<int|string> 枚举值集合
|
||
*/
|
||
public static function values(): array
|
||
{
|
||
self::validateEnumStructure();
|
||
|
||
$values = [];
|
||
foreach (self::cases() as $case) {
|
||
$values[] = $case->value;
|
||
}
|
||
return $values;
|
||
}
|
||
|
||
/**
|
||
* 获取所有枚举描述数组(与values()顺序一一对应).
|
||
* @return array<string> 描述文本集合
|
||
*/
|
||
public static function descriptions(): array
|
||
{
|
||
self::validateEnumStructure();
|
||
|
||
$descriptions = [];
|
||
foreach (self::cases() as $case) {
|
||
$descriptions[] = $case->description();
|
||
}
|
||
return $descriptions;
|
||
}
|
||
|
||
/**
|
||
* 获取值-描述映射数组(用于下拉选择等场景).
|
||
* @return array<int|string, string> 键为枚举值,值为描述文本
|
||
*/
|
||
public static function valueMap(): array
|
||
{
|
||
self::validateEnumStructure();
|
||
|
||
$map = [];
|
||
foreach (self::cases() as $case) {
|
||
$map[$case->value] = $case->description();
|
||
}
|
||
return $map;
|
||
}
|
||
|
||
/**
|
||
* 根据值获取枚举实例(严格模式).
|
||
* @param int|string $value 枚举值
|
||
* @return AetherEnum|NoticeStatsModel|NoticeStatusEnum 枚举实例
|
||
*/
|
||
public static function fromValue(int|string $value): self
|
||
{
|
||
self::validateEnumStructure();
|
||
|
||
// 检查值类型是否与枚举类型匹配(基于第一个case的类型)
|
||
$firstCase = self::cases()[0] ?? null;
|
||
if ($firstCase) {
|
||
$expectedType = gettype($firstCase->value);
|
||
$actualType = gettype($value);
|
||
if ($expectedType !== $actualType) {
|
||
throw new InvalidArgumentException(sprintf(
|
||
'枚举值类型不匹配,%s期望%s类型,实际为%s',
|
||
self::class,
|
||
$expectedType,
|
||
$actualType
|
||
));
|
||
}
|
||
}
|
||
|
||
$enum = self::tryFrom($value);
|
||
if (! $enum) {
|
||
throw new InvalidArgumentException(sprintf(
|
||
'无效的%s值: %s,允许值: %s',
|
||
self::class,
|
||
$value,
|
||
implode(', ', self::values())
|
||
));
|
||
}
|
||
return $enum;
|
||
}
|
||
|
||
/**
|
||
* 根据描述获取枚举实例(精确匹配).
|
||
* @param string $description 描述文本
|
||
* @return null|AetherEnum|NoticeStatsModel|NoticeStatusEnum 匹配的枚举实例,无匹配时返回null
|
||
*/
|
||
public static function fromDescription(string $description): ?self
|
||
{
|
||
self::validateEnumStructure();
|
||
|
||
foreach (self::cases() as $case) {
|
||
if ($case->description() === $description) {
|
||
return $case;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 检查值是否为有效的枚举值(严格类型检查).
|
||
* @param int|string $value 待检查的值
|
||
* @return bool 是否有效
|
||
*/
|
||
public static function isValidValue(int|string $value): bool
|
||
{
|
||
self::validateEnumStructure();
|
||
|
||
foreach (self::cases() as $case) {
|
||
if ($case->value === $value) { // 严格相等,避免类型松散匹配
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 校验枚举结构合法性(替代__init,私有静态方法).
|
||
*/
|
||
private static function validateEnumStructure(): void
|
||
{
|
||
// 检查当前类是否为枚举
|
||
if (! (new ReflectionClass(self::class))->isEnum()) {
|
||
throw new InvalidArgumentException(sprintf(
|
||
'AetherEnum trait仅允许枚举类使用,%s不是枚举',
|
||
self::class
|
||
));
|
||
}
|
||
|
||
// 检查枚举是否实现了description()方法
|
||
if (! method_exists(self::class, 'description')) {
|
||
throw new InvalidArgumentException(sprintf(
|
||
'枚举类%s必须实现description()方法(返回字符串描述)',
|
||
self::class
|
||
));
|
||
}
|
||
|
||
// 检查description()方法返回值是否为字符串
|
||
$sampleCase = self::cases()[0] ?? null;
|
||
if ($sampleCase && ! is_string($sampleCase->description())) {
|
||
throw new InvalidArgumentException(sprintf(
|
||
'枚举类%s的description()方法必须返回字符串',
|
||
self::class
|
||
));
|
||
}
|
||
}
|
||
}
|