PHP 8.1 新特性
PHP 8.1 带来了枚举(Enums)、只读属性(Readonly Properties)、纤程(Fibers)、never 返回类型、交叉类型(Intersection Types)和 first-class callable 语法等重量级特性。这些特性进一步完善了 PHP 的类型系统和并发处理能力。
前置知识
阅读本节前,建议先了解:PHP 8.0 新特性
枚举(Enums)
基本枚举
php
<?php
declare(strict_types=1);
// 基本枚举(无底类型)
enum Status
{
case Active;
case Inactive;
case Pending;
}
// 使用
$userStatus = Status::Active;
echo $userStatus->name; // "Active"
// match 配合枚举
function getStatusLabel(Status $status): string
{
return match ($status) {
Status::Active => '活跃',
Status::Inactive => '未激活',
Status::Pending => '待处理',
};
}Backed Enum(有底类型的枚举)
php
<?php
declare(strict_types=1);
// 字符串底类型
enum OrderStatus: string
{
case Pending = 'pending';
case Processing = 'processing';
case Shipped = 'shipped';
case Delivered = 'delivered';
case Cancelled = 'cancelled';
// 枚举方法
public function canTransitionTo(self $newStatus): bool
{
$allowed = match ($this) {
self::Pending => [self::Processing, self::Cancelled],
self::Processing => [self::Shipped, self::Cancelled],
self::Shipped => [self::Delivered],
self::Delivered, self::Cancelled => [],
};
return in_array($newStatus, $allowed, true);
}
public function label(): string
{
return match ($this) {
self::Pending => '待处理',
self::Processing => '处理中',
self::Shipped => '已发货',
self::Delivered => '已送达',
self::Cancelled => '已取消',
};
}
public function isFinalState(): bool
{
return match ($this) {
self::Delivered, self::Cancelled => true,
default => false,
};
}
}
// 整数底类型
enum UserRole: int
{
case Guest = 0;
case Member = 1;
case Editor = 2;
case Admin = 3;
public function hasPermission(string $permission): bool
{
$permissions = match ($this) {
self::Guest => ['view'],
self::Member => ['view', 'create', 'edit_own'],
self::Editor => ['view', 'create', 'edit_own', 'edit_all'],
self::Admin => ['*'],
};
return in_array('*', $permissions, true)
|| in_array($permission, $permissions, true);
}
}
// 枚举值的使用
$status = OrderStatus::Processing;
echo $status->value; // "processing"
echo $status->label(); // "处理中"
// 从值创建枚举
$status = OrderStatus::from('pending'); // OrderStatus::Pending
$status = OrderStatus::tryFrom('unknown'); // null(不存在时返回 null)
// 遍历所有枚举值
foreach (OrderStatus::cases() as $case) {
echo "{$case->value}: {$case->label()}\n";
}枚举接口与 Trait
php
<?php
declare(strict_types=1);
interface Colorful
{
public function color(): string;
}
trait HasDescription
{
public function description(): string
{
return "This is a " . $this->name . " entity";
}
}
enum Priority: int implements Colorful
{
use HasDescription;
case Low = 1;
case Medium = 2;
case High = 3;
public function color(): string
{
return match ($this) {
self::Low => 'green',
self::Medium => 'yellow',
self::High => 'red',
};
}
}
echo Priority::High->color(); // "red"
echo Priority::Medium->description(); // "This is a Medium entity"只读属性(Readonly Properties)
php
<?php
declare(strict_types=1);
class User
{
public function __construct(
public readonly string $name,
public readonly string $email,
public readonly int $age,
) {}
// ❌ 错误:不能修改 readonly 属性
// public function setName(string $name): void {
// $this->name = $name; // Error: Cannot modify readonly property
// }
// ✅ 正确:readonly 属性只能在构造函数中初始化
public static function fromArray(array $data): self
{
return new self(
name: $data['name'],
email: $data['email'],
age: $data['age'],
);
}
}
// 只读属性可以用于普通属性(非构造器提升)
class Config
{
public readonly string $appVersion;
public function __construct()
{
$this->appVersion = '1.0.0';
}
}Readonly 限制
readonly只能在类型声明的属性上使用(必须有类型)readonly只能在构造函数中或声明时赋值一次static属性不能是readonly
Fibers(纤程)
基本概念
php
<?php
declare(strict_types=1);
// Fiber 是 PHP 8.1 引入的底层并发原语
// 它允许在函数调用栈中的任意位置暂停和恢复执行
$fiber = new Fiber(function (): void {
$value = Fiber::suspend('first');
echo "Received: {$value}\n"; // "Received: second"
$value = Fiber::suspend('third');
echo "Received: {$value}\n"; // "Received: fourth"
});
// 启动 Fiber
$result = $fiber->start(); // 返回 'first'
echo "Fiber returned: {$result}\n"; // "Fiber returned: first"
// 恢复 Fiber
$result = $fiber->resume('second'); // 打印 "Received: second",返回 'third'
$result = $fiber->resume('fourth'); // 打印 "Received: fourth"实际应用:模拟异步 I/O
php
<?php
declare(strict_types=1);
class AsyncTask
{
private Fiber $fiber;
private mixed $result = null;
private bool $completed = false;
public function __construct(callable $callback)
{
$this->fiber = new Fiber(function () use ($callback): void {
$this->result = $callback();
$this->completed = true;
});
}
public function run(): mixed
{
if ($this->completed) {
return $this->result;
}
$this->fiber->start();
return Fiber::suspend();
}
public function complete(): void
{
if (!$this->completed && $this->fiber->isStarted()) {
$this->fiber->resume();
}
}
public function isCompleted(): bool
{
return $this->completed;
}
}never 返回类型
php
<?php
declare(strict_types=1);
// never 表示函数永远不会正常返回
// 意味着函数要么抛出异常,要么调用 exit(),要么进入无限循环
function throwError(string $message): never
{
throw new RuntimeException($message);
}
function redirect(string $url): never
{
header("Location: {$url}");
exit();
}
function handleCommand(string $command): never
{
match ($command) {
'quit' => exit(0),
'help' => exit($this->showHelp()),
default => throw new InvalidArgumentException("Unknown command: {$command}"),
};
}
// 与 void 的区别
// void: 函数正常返回,但不返回值
// never: 函数永远不会返回
function logMessage(string $msg): void
{
echo $msg . "\n";
// 函数正常返回,没有返回值
}
function abort(string $msg): never
{
throw new RuntimeException($msg);
// 函数不会到达这里
}交叉类型(Intersection Types)
php
<?php
declare(strict_types=1);
// 交叉类型:一个值必须同时满足多个类型约束
// 主要用于接口组合
function processEntity(Countable&Iterator $collection): int
{
$count = count($collection); // Countable 的方法
$collection->rewind(); // Iterator 的方法
return $count;
}
// 示例:同时实现多个接口
interface HasId
{
public function getId(): string;
}
interface HasTimestamps
{
public function getCreatedAt(): DateTimeImmutable;
public function getUpdatedAt(): DateTimeImmutable;
}
// 函数参数要求同时实现两个接口
function saveEntity(HasId&HasTimestamps $entity): void
{
$id = $entity->getId();
$createdAt = $entity->getCreatedAt();
// 保存到数据库...
}First-class Callable 语法
php
<?php
declare(strict_types=1);
// PHP 8.0 及之前
$fn = strlen(...);
$fn = Closure::fromCallable('strlen');
$fn = [$this, 'method'](...);
// PHP 8.1+ First-class callable 语法
$fn = strlen(...); // Closure
$fn = $array->push(...); // 闭包
$fn = $obj->method(...); // 闭包
$fn = User::create(...); // 闭包
// 实际使用
class OrderService
{
public function processOrders(array $orders, callable $processor): array
{
return array_map($processor, $orders);
}
}
$service = new OrderService();
// 使用 first-class callable
$result = $service->processOrders($orders, $this->validateOrder(...));
$result = $service->processOrders($orders, fn ($o) => $this->validateOrder($o)); // PHP 7.4其他改进
显式八进制数字格式
php
<?php
declare(strict_types=1);
// PHP 8.0 之前
$octal = 017;
// PHP 8.1+ 显式八进制
$octal = 0o17; // 等同于 15(十进制)
$hex = 0x1F; // 十六进制
$binary = 0b1111; // 二进制array_is_list()
php
<?php
declare(strict_types=1);
// 检查数组是否是列表(从 0 开始的连续整数键)
array_is_list([]); // true
array_is_list([1, 2, 3]); // true
array_is_list(['a', 'b', 'c']); // true
array_is_list([0 => 'a', 1 => 'b']); // true
array_is_list([1 => 'a', 2 => 'b']); // false(不从 0 开始)
array_is_list(['key' => 'value']); // false(关联数组)
array_is_list([0 => 'a', 2 => 'b']); // false(键不连续)最佳实践
- 使用枚举替代常量类:枚举提供了类型安全和可枚举性
- 使用 readonly:对不变属性标记为 readonly,防止意外修改
- 使用 never:明确标记不会返回的函数
- 使用 first-class callable:更简洁的闭包创建方式
下一节
继续学习:PHP 8.2 新特性