1

根据RFC on Enumerations,可以通过使用将属性添加到案例中Attribute::TARGET_CLASS_CONSTANT。(实际上,RFC 说TARGET_CLASS_CONST,但这要么是错字,要么是后来的更改。)但是,我在尝试使用反射访问它们时遇到了麻烦。

鉴于此设置:

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

enum TestNum
{
    #[TestAttribute('alpha value')]
    case ALPHA;

    #[TestAttribute('beta value')]
    case BETA;
}

我希望下面的代码给我一个具有单个属性的数组,但是它返回一个空数组。

$obj = TestNum::ALPHA;
$reflection = new ReflectionClass($obj);
$classAttributes = $reflection->getAttributes(TestAttribute::class);
var_dump($classAttributes);

此处演示:https ://3v4l.org/uLDVQ#v8.1.2

我在PHP src中找到了一个测试用例,但是用法不是我所期望的。我需要分解它,而不是使用实例:

var_dump((new \ReflectionClassConstant(TestNum::class, 'ALPHA'))->getAttributes(TestAttribute::class)[0]->newInstance());

此处演示:https ://3v4l.org/BsA9r#v8.1.2

我可以使用这种格式,但感觉真的很hacky,因为我几乎在反射中使用反射:

var_dump((new \ReflectionClassConstant($obj::class, $obj->name))->getAttributes(TestAttribute::class)[0]->newInstance());

此处演示:https ://3v4l.org/YY6Oa#v8.1.2

具体来说,该new \ReflectionClassConstant($obj::class, $obj->name)模式似乎奇怪的样板。

是否有另一种方法可以访问我缺少的单个枚举案例属性?

4

3 回答 3

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

enum TestNum {
  #[TestAttribute( 'alpha value' )]
  case ALPHA;

  #[TestAttribute( 'beta value' )]
  case BETA;
}

$obj = TestNum::ALPHA;
$ref = new ReflectionEnumUnitCase($obj::class, $obj->name);
$argument = $ref->getAttributes('TestAttribute')[0]->getArguments()[0];

print_r($argument);   // Prints: 'alpha value'
于 2022-01-25T23:06:15.683 回答
2
#[Attribute( Attribute::TARGET_CLASS_CONSTANT )]
class TestAttribute {
  public function __construct(public string $value) {
  }
}

enum TestNum {
  #[TestAttribute( 'alpha value' )]
  case ALPHA;

  #[TestAttribute( 'beta value' )]
  case BETA;
}


$obj = TestNum::ALPHA;
$ref = (new ReflectionClass($obj))->getReflectionConstant('ALPHA');

var_dump($ref->getAttributes()[0]->getArguments());   // alpha value
var_dump($ref->getAttributes()[0]->getName());        // TestAttribute
var_dump($ref->getName());                            // ALPHA
var_dump($ref->getValue());                           // enum(TestNum::ALPHA)
于 2022-01-25T22:30:23.797 回答
0

感谢 lukas.j 和 Clément Baconnier 的评论和回答。

我认为我的问题最终是我对枚举的心理模型。在我看来,枚举是抽象类和最终子类的语法糖:

abstract class TestNum
{
    // Not valid, just representative
    const ALPHA = new Alpha();
}
final class Alpha extends TestNum{};

但实际上,枚举更接近:

final class TestNum
{
    private $map;

    // Not valid, just representative
    const ALPHA = self::createInstance('ALPHA');

    private function createInstance($key) : self
    {
        $map[$key] = new self();
        return $map[$key];
    }
}

如果您查看我的(不正确的)心智模型,枚举案例上的属性用例与类上的属性的用例相同。

所以我的问题的答案是否定的。您直接或通过实例进入反射的事实TestNum::ALPHA并不意味着您正在反射该实例,您只是一直在反射TestNum。这意味着这三个之间的差异为零:

  • new ReflectionClass(TestNum::class)
  • new ReflectionClass(TestNum::ALPHA)
  • new ReflectionClass(TestNum::ALPHA::class)

的,您可以ALPHA通过命名字符串(作为文字或通过name枚举上的属性)显式请求它。但是反射并不“知道”那TestNum::ALPHA是. 换句话说,没有可反映的内部状态可以将其与.ALPHATestNumTestNum::ALPHATestNum::BETA

于 2022-01-26T15:48:15.597 回答