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枚举的有效值。在第二种情况下,纯字符串作为 传递 Status是来自PostStatus枚举的有效值。在第二种情况下,纯字符串作为传递Status,这是禁止的,因为值必须在 中定义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提示,因为它 published会满足类型PostStatus提示,因为它plain指的是简单的标量值。
何时使用枚举?
枚举适用于在变量可以取值方面需要灵活性的情况,但仅限于预先确定的一组可能情况。
贯穿这篇文章的博客文章类就是一个经典的例子。帖子只能处于一组已知状态中的一个,但 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 还将提供其他一些便利特性,包括只读属性和交集类型。