Skip to content

枚举基础概念

PHP 8.1 引入了枚举(Enum)功能,枚举是一种特殊的数据类型,它允许你定义一组命名的常量,使用枚举可以提高代码的可读性和类型安全性,

PHP 的枚举肉眼可见的参考了 Rust 的语法

php
<?php
// 定义一个简单的单位枚举
enum Status {
    case PENDING;
    case APPROVED;
    case REJECTED;
}

// 使用枚举
$orderStatus = Status::PENDING;
echo $orderStatus->name; // 输出: PENDING

// 检查枚举值
if ($orderStatus === Status::PENDING) {
    echo "订单待处理";
}

// 遍历所有枚举值
foreach (Status::cases() as $status) {
    echo $status->name . "\n";
}
// 输出:
// PENDING
// APPROVED
// REJECTED

标量枚举(带值的枚举)

标量枚举允许你为每个枚举值分配一个标量值(字符串或整数),

php
<?php
// 字符串值枚举
enum Color: string {
    case RED = '#FF0000';
    case GREEN = '#00FF00';
    case BLUE = '#0000FF';
}

// 整数值枚举
enum Priority: int {
    case LOW = 1;
    case MEDIUM = 2;
    case HIGH = 3;
    case CRITICAL = 4;
}

// 使用标量枚举
$favoriteColor = Color::RED;
echo $favoriteColor->value; // 输出: #FF0000
echo $favoriteColor->name;  // 输出: RED

$taskPriority = Priority::HIGH;
echo $taskPriority->value; // 输出: 3

// 从值创建枚举实例
$colorFromValue = Color::from('#00FF00');
echo $colorFromValue->name; // 输出: GREEN

// 安全的值转换(如果值不存在会返回null)
$maybeColor = Color::tryFrom('#FFFFFF');
var_dump($maybeColor); // 输出: null

// 检查枚举值是否存在
try {
    $invalidColor = Color::from('#XXXXXX'); // 会抛出 ValueError
} catch (ValueError $e) {
    echo "无效的颜色值\n";
}

单位枚举(不带值的枚举)

单位枚举是不带值的枚举,每个枚举值都是唯一的实例,

php
<?php
enum Direction {
    case NORTH;
    case SOUTH;
    case EAST;
    case WEST;

    // 可以为单位枚举添加方法
    public function getOpposite(): self {
        return match($this) {
            self::NORTH => self::SOUTH,
            self::SOUTH => self::NORTH,
            self::EAST => self::WEST,
            self::WEST => self::EAST,
        };
    }

    public function getDescription(): string {
        return match($this) {
            self::NORTH => '北方',
            self::SOUTH => '南方',
            self::EAST => '东方',
            self::WEST => '西方',
        };
    }
}

// 使用单位枚举
$facing = Direction::NORTH;
echo $facing->getDescription(); // 输出: 北方

$opposite = $facing->getOpposite();
echo $opposite->name; // 输出: SOUTH
echo $opposite->getDescription(); // 输出: 南方

枚举方法

枚举可以定义自己的方法,包括静态方法和实例方法,

php
<?php
enum UserRole: string {
    case ADMIN = 'admin';
    case MODERATOR = 'moderator';
    case USER = 'user';
    case GUEST = 'guest';

    // 实例方法
    public function canEdit(): bool {
        return in_array($this, [self::ADMIN, self::MODERATOR]);
    }

    public function canDelete(): bool {
        return $this === self::ADMIN;
    }

    public function getLevel(): int {
        return match($this) {
            self::ADMIN => 4,
            self::MODERATOR => 3,
            self::USER => 2,
            self::GUEST => 1,
        };
    }

    // 静态方法
    public static function getEditableRoles(): array {
        return array_filter(self::cases(), function($role) {
            return $role->canEdit();
        });
    }

    public function getColor(): string {
        return match($this) {
            self::ADMIN => 'red',
            self::MODERATOR => 'blue',
            self::USER => 'green',
            self::GUEST => 'gray',
        };
    }
}

// 使用枚举方法
$userRole = UserRole::MODERATOR;

var_dump($userRole->canEdit());    // bool(true)
var_dump($userRole->canDelete());  // bool(false)
echo $userRole->getLevel();        // 3

// 获取所有可编辑角色
$editableRoles = UserRole::getEditableRoles();
foreach ($editableRoles as $role) {
    echo $role->name . "\n"; // ADMIN 和 MODERATOR
}

枚举与接口

枚举可以实现接口,这使得枚举可以遵循特定的行为规范,

php
<?php
// 定义接口
interface PermissionInterface {
    public function hasPermission(string $action): bool;
    public function getDisplayName(): string;
}

// 枚举实现接口
enum AccountType: string implements PermissionInterface {
    case FREE = 'free';
    case PREMIUM = 'premium';
    case ENTERPRISE = 'enterprise';

    public function hasPermission(string $action): bool {
        return match($this) {
            self::FREE => in_array($action, ['read', 'comment']),
            self::PREMIUM => in_array($action, ['read', 'comment', 'upload']),
            self::ENTERPRISE => true, // 所有权限
        };
    }

    public function getDisplayName(): string {
        return match($this) {
            self::FREE => '免费账户',
            self::PREMIUM => '高级账户',
            self::ENTERPRISE => '企业账户',
        };
    }

    // 额外的枚举特定方法
    public function getStorageLimit(): int {
        return match($this) {
            self::FREE => 100,      // MB
            self::PREMIUM => 1000,  // MB
            self::ENTERPRISE => PHP_INT_MAX, // 无限制
        };
    }
}

// 使用实现接口的枚举
$account = AccountType::PREMIUM;

echo $account->getDisplayName();                    // 高级账户
var_dump($account->hasPermission('upload'));       // bool(true)
var_dump($account->hasPermission('admin_panel'));  // bool(false)
echo $account->getStorageLimit();                  // 1000

枚举的类型安全

枚举提供了严格的类型安全,确保变量只能是预定义的枚举值之一,

php
<?php
enum PaymentStatus: string {
    case PENDING = 'pending';
    case COMPLETED = 'completed';
    case FAILED = 'failed';
    case REFUNDED = 'refunded';
}

class PaymentProcessor {
    private PaymentStatus $status;

    public function __construct(PaymentStatus $initialStatus = PaymentStatus::PENDING) {
        $this->status = $initialStatus;
    }

    public function process(): void {
        if ($this->status !== PaymentStatus::PENDING) {
            throw new LogicException('只能处理待处理的支付');
        }

        // 模拟支付处理
        $this->status = PaymentStatus::COMPLETED;
    }

    public function refund(): void {
        if ($this->status !== PaymentStatus::COMPLETED) {
            throw new LogicException('只能退款已完成的支付');
        }

        $this->status = PaymentStatus::REFUNDED;
    }

    public function getStatus(): PaymentStatus {
        return $this->status;
    }

    public function getStatusName(): string {
        return match($this->status) {
            PaymentStatus::PENDING => '待处理',
            PaymentStatus::COMPLETED => '已完成',
            PaymentStatus::FAILED => '失败',
            PaymentStatus::REFUNDED => '已退款',
        };
    }
}

// 使用示例
$payment = new PaymentProcessor();
echo $payment->getStatusName(); // 待处理

$payment->process();
echo $payment->getStatusName(); // 已完成

$payment->refund();
echo $payment->getStatusName(); // 已退款

// 类型安全示例 - 以下代码会报错,因为参数必须是PaymentStatus枚举值
// $invalidPayment = new PaymentProcessor('invalid_status'); // 这在运行时会报错

枚举的实际应用示例

以下是一个完整的实际应用示例,展示枚举在真实项目中的使用:

php
<?php
// HTTP状态码枚举
enum HttpStatusCode: int {
    case OK = 200;
    case CREATED = 201;
    case BAD_REQUEST = 400;
    case UNAUTHORIZED = 401;
    case FORBIDDEN = 403;
    case NOT_FOUND = 404;
    case INTERNAL_SERVER_ERROR = 500;

    public function getReasonPhrase(): string {
        return match($this) {
            self::OK => 'OK',
            self::CREATED => 'Created',
            self::BAD_REQUEST => 'Bad Request',
            self::UNAUTHORIZED => 'Unauthorized',
            self::FORBIDDEN => 'Forbidden',
            self::NOT_FOUND => 'Not Found',
            self::INTERNAL_SERVER_ERROR => 'Internal Server Error',
        };
    }

    public function isSuccessful(): bool {
        return $this->value >= 200 && $this->value < 300;
    }

    public function isClientError(): bool {
        return $this->value >= 400 && $this->value < 500;
    }

    public function isServerError(): bool {
        return $this->value >= 500 && $this->value < 600;
    }
}

// API响应类
class ApiResponse {
    public function __construct(
        private readonly mixed $data,
        private readonly HttpStatusCode $status = HttpStatusCode::OK,
        private readonly array $headers = []
    ) {}

    public function send(): void {
        http_response_code($this->status->value);

        foreach ($this->headers as $name => $value) {
            header("$name: $value");
        }

        header('Content-Type: application/json');
        echo json_encode([
            'status' => $this->status->value,
            'message' => $this->status->getReasonPhrase(),
            'data' => $this->data
        ]);
    }

    public static function success(mixed $data, HttpStatusCode $status = HttpStatusCode::OK): self {
        return new self($data, $status);
    }

    public static function error(string $message, HttpStatusCode $status = HttpStatusCode::INTERNAL_SERVER_ERROR): self {
        return new self(['error' => $message], $status);
    }
}

// 使用示例
try {
    // 模拟API请求处理
    $userData = ['id' => 1, 'name' => '张三', 'email' => 'zhangsan@example.com'];
    $response = ApiResponse::success($userData, HttpStatusCode::OK);
    $response->send();
} catch (Exception $e) {
    $errorResponse = ApiResponse::error($e->getMessage(), HttpStatusCode::INTERNAL_SERVER_ERROR);
    $errorResponse->send();
}

// 枚举验证示例
function validateStatus(HttpStatusCode $status): string {
    if ($status->isSuccessful()) {
        return "成功状态码: {$status->value}";
    } elseif ($status->isClientError()) {
        return "客户端错误: {$status->value}";
    } elseif ($status->isServerError()) {
        return "服务器错误: {$status->value}";
    }
    return "其他状态码: {$status->value}";
}

echo validateStatus(HttpStatusCode::NOT_FOUND);     // 客户端错误: 404
echo validateStatus(HttpStatusCode::INTERNAL_SERVER_ERROR); // 服务器错误: 500

PHP枚举提供了一种类型安全的方式来处理预定义的值集合,相比传统的常量定义, 枚举提供了更好的代码组织和类型检查,枚举特别适用于状态、类型、选项等场景, 能够显著提高代码的可读性和维护性,

Released under the MIT License.