ReflectionEnum / ReflectionEnumCase / ReflectionFiber
概述
PHP 8.1 引入了枚举(Enum)和纤程(Fiber),反射 API 也随之扩展。ReflectionEnum 用于检查枚举类型的结构,ReflectionEnumCase 用于检查枚举的各个 case,ReflectionFiber 用于检查纤程的状态。本章将详细讲解这三个反射类的用法。
PHP 版本
这些反射类均从 PHP 8.1 开始可用。
基础概念
ReflectionEnum
检查枚举类型(Enum)的信息,包括枚举类型(纯枚举/回退枚举)、case 列表、枚举值的类型等。
ReflectionEnumCase
检查枚举的各个 case,包括名称、值、是否 backed、所属枚举类型等。
ReflectionFiber
检查纤程的状态信息,包括执行状态、调用栈等。
语法与代码
ReflectionEnum 基本用法
php
<?php
declare(strict_types=1);
enum Status: string
{
case Active = 'active';
case Inactive = 'inactive';
case Pending = 'pending';
}
$refEnum = new \ReflectionEnum(Status::class);
echo "枚举名: " . $refEnum->getName() . "\n";
echo "是否为枚举: " . ($refEnum->isEnum() ? 'yes' : 'no') . "\n";
echo "是否为 backed: " . ($refEnum->isBacked() ? 'yes' : 'no') . "\n";
if ($refEnum->isBacked()) {
echo "枚举值类型: " . $refEnum->getBackingType()?->getName() . "\n";
}
echo "\n所有 case:\n";
foreach ($refEnum->getCases() as $case) {
echo " " . $case->getName();
if ($case->isBacked()) {
echo " = " . var_export($case->getValue(), true);
}
echo "\n";
}ReflectionEnumCase 基本用法
php
<?php
declare(strict_types=1);
$case = new \ReflectionEnumCase(Status::class, 'Active');
echo "Case 名称: " . $case->getName() . "\n";
echo "Case 值: " . $case->getValue()->value . "\n";
echo "所属枚举: " . $case->getEnum()->getName() . "\n";
echo "是否 backed: " . ($case->isBacked() ? 'yes' : 'no') . "\n";
// 获取枚举实例
$active = $case->getValue();
echo $active instanceof Status; // true纯枚举 vs Backed 枚举
php
<?php
declare(strict_types=1);
// 纯枚举
enum Color
{
case Red;
case Green;
case Blue;
}
// Backed 枚举
enum Role: int
{
case Admin = 1;
case Editor = 2;
case Viewer = 3;
}
function inspectEnum(string $enumClass): void
{
$ref = new \ReflectionEnum($enumClass);
echo "枚举: " . $ref->getShortName() . "\n";
echo "Backed: " . ($ref->isBacked() ? 'yes' : 'no') . "\n";
if ($ref->isBacked()) {
echo "值类型: " . $ref->getBackingType()->getName() . "\n";
}
echo "Cases:\n";
foreach ($ref->getCases() as $case) {
echo " " . $case->getName();
if ($case->isBacked()) {
echo " = " . var_export($case->getValue()->value, true);
}
echo "\n";
}
echo "\n";
}
inspectEnum(Color::class);
inspectEnum(Role::class);枚举上的注解反射
php
<?php
declare(strict_types=1);
use Attribute;
#[Attribute(Attribute::TARGET_ENUM_CASE)]
class Description
{
public function __construct(public readonly string $text) {}
}
#[Attribute(Attribute::TARGET_CLASS)]
class Label
{
public function __construct(public readonly string $name) {}
}
#[Label(name: 'HTTP 方法')]
enum HttpMethod: string
{
#[Description(text: '获取资源')]
case Get = 'GET';
#[Description(text: '创建资源')]
case Post = 'POST';
#[Description(text: '更新资源')]
case Put = 'PUT';
}
// 读取枚举类上的注解
$refEnum = new \ReflectionEnum(HttpMethod::class);
$labelAttrs = $refEnum->getAttributes(Label::class);
foreach ($labelAttrs as $attr) {
$label = $attr->newInstance();
echo "枚举标签: {$label->name}\n";
}
// 读取枚举 case 上的注解
foreach ($refEnum->getCases() as $case) {
$descAttrs = $case->getAttributes(Description::class);
foreach ($descAttrs as $descAttr) {
$desc = $descAttr->newInstance();
echo "{$case->getName()}: {$desc->text}\n";
}
}ReflectionFiber 基本用法
php
<?php
declare(strict_types=1);
$fiber = new \Fiber(function (): void {
Fiber::suspend('step 1');
Fiber::suspend('step 2');
return 'done';
});
$fiber->start();
$refFiber = new \ReflectionFiber($fiber);
echo "状态: " . $refFiber->status() . "\n"; // SUSPENDED (3)
$fiber->resume();
$refFiber = new \ReflectionFiber($fiber);
echo "状态: " . $refFiber->status() . "\n"; // SUSPENDED (3)
$fiber->resume();
$refFiber = new \ReflectionFiber($fiber);
echo "状态: " . $refFiber->status() . "\n"; // FINISHED (4)详细说明
ReflectionEnum 常用方法
| 方法 | 说明 |
|---|---|
getName() | 枚举完整类名 |
isEnum() | 是否为枚举 |
isBacked() | 是否为 backed 枚举 |
getBackingType() | 获取值类型(仅 backed 枚举) |
getCases() | 获取所有 case |
hasCase(string $name) | 是否有指定 case |
getCase(string $name) | 获取指定 case |
ReflectionEnumCase 常用方法
| 方法 | 说明 |
|---|---|
getName() | case 名称 |
getValue() | 枚举实例 |
getEnum() | 所属枚举类型 |
isBacked() | 是否有值 |
getAttributes() | 获取注解 |
ReflectionFiber 方法
| 方法 | 说明 |
|---|---|
status() | 纤程状态 |
getFiber() | 获取 Fiber 实例 |
实战示例
实战:枚举信息导出器
php
<?php
declare(strict_types=1);
class EnumExporter
{
public function export(string $enumClass): array
{
$ref = new \ReflectionEnum($enumClass);
$result = [
'name' => $ref->getShortName(),
'backed' => $ref->isBacked(),
'type' => $ref->isBacked() ? $ref->getBackingType()->getName() : null,
'cases' => [],
];
foreach ($ref->getCases() as $case) {
$caseInfo = [
'name' => $case->getName(),
'value' => $case->isBacked() ? $case->getValue()->value : null,
];
// 读取 case 上的注解
$attrs = [];
foreach ($case->getAttributes() as $attr) {
$attrs[$attr->getName()] = $attr->getArguments();
}
$caseInfo['attributes'] = $attrs;
$result['cases'][] = $caseInfo;
}
return $result;
}
}
$exporter = new EnumExporter();
print_r($exporter->export(HttpMethod::class));注意事项
ReflectionEnum 只能用于枚举
对非枚举类使用 ReflectionEnum 会抛出 ReflectionException。
ReflectionFiber 的局限性
ReflectionFiber 提供的信息有限,主要用于调试。
最佳实践
- 利用枚举反射构建表单:根据枚举的 case 和注解自动生成表单选项。
- 缓存枚举反射结果:枚举结构在运行时不变,可以安全缓存。
- 配合注解使用:在枚举 case 上使用注解添加元数据。
php
<?php
declare(strict_types=1);
function getEnumOptions(string $enumClass): array
{
$ref = new \ReflectionEnum($enumClass);
return array_map(
fn(\ReflectionEnumCase $case) => [
'name' => $case->getName(),
'value' => $case->getValue(),
],
$ref->getCases()
);
}ReflectionEnum 详细用法
枚举类型完整分析
php
<?php
declare(strict_types=1);
enum Status: string
{
case Active = 'active';
case Inactive = 'inactive';
case Pending = 'pending';
case Archived = 'archived';
}
enum Role
{
case Admin;
case Editor;
case Viewer;
}
function analyzeEnum(string $enumClass): array
{
$refEnum = new \ReflectionEnum($enumClass);
return [
'name' => $refEnum->getName(),
'isBacked' => $refEnum->isBacked(),
'backingType' => $refEnum->getBackingType()?->getName(),
'cases' => array_map(
fn(\ReflectionEnumUnitCase|\ReflectionEnumBackedCase $case) => [
'name' => $case->getName(),
'value' => $case instanceof \ReflectionEnumBackedCase
? $case->getEnum()->getValue() : null,
],
$refEnum->getCases()
),
'hasCases' => $refEnum->hasCase('Active'),
'caseCount' => count($refEnum->getCases()),
];
}
print_r(analyzeEnum(Status::class));枚举 case 的反射
php
<?php
declare(strict_types=1);
enum HttpMethod: string
{
case Get = 'GET';
case Post = 'POST';
case Put = 'PUT';
case Delete = 'DELETE';
case Patch = 'PATCH';
}
function enumCaseDetails(\ReflectionEnumBackedCase $case): array
{
return [
'name' => $case->getName(),
'value' => $case->getValue(),
'enum' => $case->getEnum()->getName(),
'declaringClass' => $case->getDeclaringClass()->getName(),
];
}
$refEnum = new \ReflectionEnum(HttpMethod::class);
foreach ($refEnum->getCases() as $case) {
if ($case instanceof \ReflectionEnumBackedCase) {
print_r(enumCaseDetails($case));
}
}枚举方法反射
php
<?php
declare(strict_types=1);
enum Color: string
{
case Red = '#FF0000';
case Green = '#00FF00';
case Blue = '#0000FF';
public function isWarm(): bool
{
return in_array($this, [self::Red, self::Green], true);
}
public function toRgb(): array
{
return sscanf($this->value, '#%02x%02x%02x');
}
public static function fromName(string $name): self
{
return self::from($name);
}
}
$refEnum = new \ReflectionEnum(Color::class);
echo "枚举方法:\n";
foreach ($refEnum->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
if (!$method->isStatic()) {
$returnType = $method->getReturnType()?->getName() ?? 'void';
echo " {$method->getName()}(): {$returnType}\n";
}
}ReflectionFiber 详解
获取 Fiber 的状态信息
php
<?php
declare(strict_types=1);
$fiber = new \Fiber(function (): void {
echo "Fiber 开始\n";
\Fiber::suspend('first pause');
echo "Fiber 恢复\n";
\Fiber::suspend('second pause');
echo "Fiber 结束\n");
});
$fiber->start();
// 通过反射获取 Fiber 状态
$refFiber = new \ReflectionFiber($fiber);
echo "Fiber 状态: ";
echo match ($refFiber->getFiberStatus()) {
\Fiber::STATUS_INIT => 'init (未启动)',
\Fiber::STATUS_SUSPENDED => 'suspended (挂起)',
\Fiber::STATUS_RUNNING => 'running (运行中)',
\Fiber::STATUS_FINISHED => 'finished (已完成)',
\Fiber::STATUS_DEAD => 'dead (异常终止)',
default => 'unknown',
} . "\n";
// 获取 Fiber 函数信息
$executingFunction = $refFiber->getExecutingFunction();
echo "执行函数: " . $executingFunction->getName() . "\n";
$fiber->resume();
$refFiber = new \ReflectionFiber($fiber);
echo "恢复后状态: ";
echo match ($refFiber->getFiberStatus()) {
\Fiber::STATUS_SUSPENDED => 'suspended',
\Fiber::STATUS_FINISHED => 'finished',
default => 'unknown',
} . "\n";常见误区与 FAQ
ReflectionEnum 只适用于 enum 吗?
是的。ReflectionEnum 只能反射枚举类型。对普通类使用会抛出异常。
如何区分 backed enum 和 unit enum?
使用 ReflectionEnum::isBacked() 方法。Backed enum 有 getBackingType() 返回的类型信息。
ReflectionFiber 的 getFiberStatus 与 Fiber 方法的关系?
ReflectionFiber::getFiberStatus() 返回 Fiber 的当前状态常量,与 Fiber 类的状态检查方法(isRunning()、isSuspended() 等)返回布尔值不同。反射方式返回精确的状态码。