如何在 PHP 8.1 中使用枚举

在这里插入图片描述
PHP 8.1 最终将添加对enums 的语言支持。枚举是枚举的缩写,是只能用特定值实例化的类型。它们常见于其他面向对象的语言中,但以前需要用户态变通方法才能在 PHP 中实现。

基本语法

这是一个简单的枚举的样子:

enum PostStatus {
    case Published;
    case InReview;
    case Draft;
}

的case关键字,先前的部分switch陈述,是用来划定枚举接受的特定值。值的引用方式与类常量相同:

$published = PostStatus::Published;

枚举的行为类似于类和接口。它们与类型系统完全兼容,因此您可以键入提示函数仅接受枚举中定义的值:

class BlogPost {
 
    public function __construct(
        public string $Headline,
        public string $Content,
        public PostStatus $Status=PostStatus::Draft) {}
 
}

这是使用BlogPost该类的示例:

// OK
$post = new BlogPost(
    "Example Post",
    "An example",
    PostStatus::Draft
);
 
// TypeError: Argument #3 ($Status) must be of type PostStatus
$post = new BlogPost(
    "Broken Example",
    "A broken example",
    "Submitted"
);

第一个实例有效,因为它 S t a t u s 是 来 自 P o s t S t a t u s 枚 举 的 有 效 值 。 在 第 二 种 情 况 下 , 纯 字 符 串 作 为 传 递 Status是来自PostStatus枚举的有效值。在第二种情况下,纯字符串作为 传递 StatusPostStatusStatus,这是禁止的,因为值必须在 中定义PostStatus。

枚举案例表示为枚举对象上的常量。这意味着您可以将它们用作静态值和常量表达式的一部分。该BlogPost构造示出了一个枚举的情况下被用作默认参数值,其中$Status自动设置时,没有值由呼叫者提供给草案。

您可以使用它的cases方法访问枚举中的所有可用值:

PostStatus::cases();
// [PostStatus::Published, PostStatus::InReview, PostStatus::Draft]

纯枚举与支持枚举

PostStatus上面的枚举是一个纯枚举。它只包含case语句,没有额外的数据。PHP 还允许您将值附加到枚举案例,创建一个支持的枚举。

enum PostStatus : string {
    case Published = "S1";
    case InReview = "S2";
    case Draft = "S3";
}

此处PostStatus枚举已被修改以创建支持的枚举。枚举定义中的 typehint 规定每个 case 都有一个分配给它的字符串值。在这个例子中,我们假设每个命名的帖子状态都有一个相关的短标识符。当帖子被持久化时,可能是这个标识符被保存到数据库中。

您可以通过value案例实例上的属性访问支持的值:

class BlogPostRepository {
 
    public function save(BlogPost $Post) : void {
        $this -> insert(
            "blog_posts",
            [
                "headline" => $Post -> Headline,
                "content" => $Post -> Content,
                "status" => $Post -> Status -> value
            ]
        );
    }
 
}
 
$post = new BlogPost("Example", "Demo", PostStatus::Published);
(new BlogPostRepository()) -> save($post);

此示例将根据上面显示的枚举的支持版本将持久status字段的值设置为。S1PostStatus

支持的枚举只接受字符串和整数作为值。也不可能使用联合类型 string|int。此外,每个案例都需要一个唯一的值——下面的例子是不允许的:

enum PostStatus : string {
 
    case Published = "S1";
    case Draft = "S1";
 
}

PHP 在枚举上提供了一个实用方法来从支持的值创建实例:

// fetch the blog post from earlier from the database
// the "status" field = S1
$status = PostStatus::from($record["status"]);

该from()方法将从价值案例中水合实例。在此示例中,S1被映射回Published案例,并且您的代码接收PostStatus::Published.

from()ValueError如果输入值无效,则抛出一个;在您知道该值可能不可用的情况下,可以使用替代tryFrom()方法。null当没有匹配时返回,而不是抛出错误。

向枚举添加方法

由于枚举是基于类的,您还可以向它们添加方法!

enum PostStatus {
 
    case Published;
    case Draft;
 
    public function isPubliclyAccessible() : bool {
        return ($this instanceof self::Published);
    }
 
}

这使您可以在枚举中保留特定于案例的行为,而不是在代码库中复制它。

枚举也可以实现接口:

enum PostStatus implements PublicAccessGatable {
 
    case Published;
    case Draft;
 
    public function isPubliclyAccessible() : bool {
        return ($this instanceof self::Published);
    }
 
}

现在您可以将PostStatus实例传递给任何接受 a 的对象PublicAccessGatable:

class UserAuthenticator {
 
    function shouldAllowAccess(PublicAccessGatable $Resource) : bool {
        return ($this -> User -> isAdmin() || $Resource -> isPubliclyAccessible());
    }
 
}
 
$auth = new UserAuthenticator();
 
// get a blog post from the database
if (!$auth -> shouldAllowAccess($post -> Status)) {
    http_response_code(403);
}

对枚举方法可以做什么没有任何限制——毕竟它们是常规的 PHP 方法——但通常你希望它们与实例的情况进行某种比较,然后返回一个静态值。枚举可以使用特征,因此您也可以引入以这种方式抽象的现有方法。

您可以在枚举中使用public,protected和private方法,尽管protected和private具有相同的效果。枚举不能相互扩展,因此private实际上是多余的。您也不能添加构造函数或析构函数。静态方法受支持,可以在枚举类或其案例实例上调用。

常数

枚举也可以有自己的常量,可以是常规的文字值,也可以是对枚举案例的引用:

enum PostStatus {
 
    case Published;
    case Draft;
 
    public const Live = self::Published;
    public const PlainConstant = "foobar";
 
}

这可能会造成混淆,因为使用相同的语法来访问案例(枚举实例)和常量:

$published = PostStatus::Published;
$plain = PostStatus::PlainConstant;

p u b l i s h e d 会 满 足 类 型 P o s t S t a t u s 提 示 , 因 为 它 published会满足类型PostStatus提示,因为它 publishedPostStatusplain指的是简单的标量值。

何时使用枚举?

枚举适用于在变量可以取值方面需要灵活性的情况,但仅限于预先确定的一组可能情况。

贯穿这篇文章的博客文章类就是一个经典的例子。帖子只能处于一组已知状态中的一个,但 PHP 以前没有直接的方法来实现这一点。

在旧版本中,您可能使用过这种方法:

class PostStatus {
    const Published = 0;
    const Draft = 1;
}
 
class BlogPost {
    public function __construct(
        public string $Headline,
        public int $Status
    ) {}
}
 
$post = new BlogPost("My Headline", PostStatus::Published);

这里的问题是它$Status实际上接受任何整数,所以下面的调用将是完全有效的:

$post = new BlogPost("My Headline", 9000);

此外,BlogPost并且PostStatus是完全超然的——阅读的人BlogPost无法了解$Status 实际接受的价值观的范围。虽然可以通过使用适当的 docblock 类型提示或第三方“假枚举”包​​来缓解这些问题,但它们都围绕其他编程语言简化的概念添加了额外的层。

将本机枚举添加到 PHP 是有助于完善语言类型系统的一个步骤。您最终可以以使每个人都在同一页面上的方式输入允许的值。如果您传递无效值,您将收到运行时错误。您的 IDE 可以更好地帮助您提供正确的值,因为它会知道$Status只接受三个选项,而不是“任何整数”。

结论

枚举解决了在 PHP 中工作时一些常见的开发人员痛点。它们使得输入提示参数、返回值和属性必须是一组预定选项中的一个成为可能。

枚举是灵活的代码库实体,您可以以纯形式保持简单,或使用支持的值、接口实现和自定义方法进行扩展。枚举的行为类似于常规的对象在大多数情况下和支持类功能,例如__call(),__invoke和::class。

您可以使用新enum_exists()函数和ReflectionEnum反射类自省枚举。此外,枚举实现了两个新接口,UnitEnum(在纯枚举的情况下)和BackedEnum(对于具有支持值的枚举)。这些可用于适用于任何枚举的通用框架代码。这些接口不能由用户态代码手动实现。

枚举将作为 2021 年 11 月 8.1 版本的一部分登陆 PHP。它们已经在最新的 beta 版本中可用。PHP 8.1 还将提供其他一些便利特性,包括只读属性和交集类型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mikes zhang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值