PHP枚举类型中的`__toString`魔术方法

24

我想知道为什么不能为枚举提供一个__toString实现。

IDE显示“Enum may not include '__toString'”。但这是我创建枚举后首先考虑的事情。以前我在代码中使用封装字符串的值对象,当需要时使用字符串强制转换。现在我想将它们迁移到枚举,但那些对象拒绝了。

#[Immutable]
enum SaveKlinesFromApiQueue: string
{
    case DEFAULT = 'save_klines_from_api_queue';
    case PRIORITY = 'save_klines_from_api_priority_queue';

    public function __toString(): string
    {
        return $this->value;
    }
}

$this->value里面有没有东西还是空的? - benzo
9
枚举类型的RFC明确禁止了几乎所有魔术方法,其表述的担忧是关于状态和/或动态性。我认为总体思想是,您要么使用字符串,要么使用更高级别的东西,而__toString()会让人们(在调试时)感到困惑,因为它允许将枚举传递给接受字符串类型参数的方法,就像类一样。 - Chris Haas
每个案例都是 SaveKlinesFromApiQueue 的一个对象实例。当您执行 echo SaveKlinesFromApiQueue; 时,$this 应该指的是什么? - Rain
2
@ChrisHaas 如果使用严格类型,就不会让枚举类型传递到接受字符串类型参数的方法中而导致混淆。如果他们不使用严格类型,那么他们就应该感到困惑。所述问题是关于状态和/或动态性的,但根据定义,枚举类型不应具有任何状态。在PHP中,所有对象都可以有一个__toString方法,这在显示网格等方面非常有用,但是对于枚举类型,我们必须返回类型检查,这不是一个好主意。 - vitoriodachef
1
@vitoriodachef,强制类型是可选的,这是一个有争议的问题,“值得”的看法很主观,我个人认为有点苛刻。无论如何,我并没有参与RFC过程,只是在这里留下了评论。更长的RFC讨论在这里。此外,RFC有一个专门的“自动标量转换”部分,可能会引起兴趣。如果您认为这是一个足够重要的功能,请联系列表或RFC作者并说明您的立场。 - Chris Haas
有人看到关于后端枚举的Stringable接口的隐式实现的RFC吗? - rela589n
4个回答

9

如果您想在常规类中像const一样使用Enum,我认为您不应该使用Enum。相反,考虑创建一个抽象类,就像这样:

abstract class SaveKlinesFromApiQueue
{
    public const DEFAULT = 'save_klines_from_api_queue';
    public const PRIORITY = 'save_klines_from_api_priority_queue';
}

除了正确使用Enum之外,在你的情况下,你可以使用:

echo SaveKlinesFromApiQueue::DEFAULT->name;

结果:"默认"

或者

echo SaveKlinesFromApiQueue::DEFAULT->value;

结果: "save_klines_from_api_queue"


2

正如Chris所提到的,魔术方法是不允许的。

对于最常见的两种用例:

要获取单个值的字符串值,只需使用->value

如果您想要所有值的字符串值,请在方法中添加循环:

    public static function strings(): array
    {
        $strings = [];
        foreach(self::cases() as $case) {
            $strings[] = $case->value;
        }
        return $strings;
    }

3
这并不是这样的。当项目有像枚举一样行事的值对象,并且这些对象定义了魔术方法时 - 你(也就是我)就会遇到麻烦。在项目的某个地方可能没有启用严格类型,并且一些方法参数使用字符串进行类型提示(当没有值对象时执行此操作)。值对象实现了__toString方法,它可以正常工作。现在,当枚举存在时,我想将这些值对象转换为枚举,但显然我无法这样做而不出问题。 - rela589n

0

对于简单的枚举值,请使用"const"而不是"case":

enum SaveKlinesFromApiQueue: string {
    const DEFAULT = 'save_klines_from_api_queue';
    const PRIORITY = 'save_klines_from_api_priority_queue';
}

echo SaveKlinesFromApiQueue::DEFAULT;

1
这使得使用枚举的整个意义都失去了。 - Okneloper

-3

如果您的用例涉及将枚举属性转换为json,则实现JsonSerializable是可行的。

enum ParameterTypeEnum implements \JsonSerializable
{
    case QUERY;
    case COOKIE;
    case HEADER;
    case PATH;

    public function getType(): string
    {
        return match($this) {
            ParameterTypeEnum::QUERY => 'query',
            ParameterTypeEnum::COOKIE => 'cookie',
            ParameterTypeEnum::HEADER => 'header',
            ParameterTypeEnum::PATH => 'path',
        };
    }

    public function jsonSerialize(): mixed
    {
        return $this->getType();
    }
}

这种方法的一致性较低。 - Payedimaunt
请分享更多细节。在任何其他情况下,PHP本身会调用__toString(),比如一个强制转换,这将如何工作? - Nico Haase
1
@NicoHaase 不会的。我刚刚测试了(string)的转换,它会抛出一个异常。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接