PHP 8.3新特性实战:枚举与只读类的企业级应用 | PHP现代化开发

2025-10-09 0 234

1. PHP枚举的深度解析

PHP 8.1引入了枚举(Enums),在8.3中得到了进一步增强。枚举不仅替代了传统的常量定义,更提供了类型安全和丰富的方法支持。

1.1 基础枚举与回溯枚举

<?php
// 基础枚举
enum OrderStatus {
    case PENDING;
    case PROCESSING;
    case SHIPPED;
    case DELIVERED;
    case CANCELLED;
}

// 回溯枚举
enum PaymentMethod: string {
    case CREDIT_CARD = 'credit_card';
    case PAYPAL = 'paypal';
    case BANK_TRANSFER = 'bank_transfer';
    case CRYPTO = 'cryptocurrency';
    
    public function getDisplayName(): string {
        return match($this) {
            self::CREDIT_CARD => '信用卡支付',
            self::PAYPAL => 'PayPal',
            self::BANK_TRANSFER => '银行转账',
            self::CRYPTO => '加密货币'
        };
    }
    
    public static function fromDisplayName(string $displayName): self {
        return match($displayName) {
            '信用卡支付' => self::CREDIT_CARD,
            'PayPal' => self::PAYPAL,
            '银行转账' => self::BANK_TRANSFER,
            '加密货币' => self::CRYPTO,
            default => throw new ValueError("未知的支付方式: $displayName")
        };
    }
}

// 使用示例
$payment = PaymentMethod::CREDIT_CARD;
echo $payment->value; // 输出: credit_card
echo $payment->getDisplayName(); // 输出: 信用卡支付
?>

2. 只读类的企业级应用

PHP 8.2引入的只读类(readonly class)为不可变对象提供了语言级别的支持,特别适合领域驱动设计(DDD)。

2.1 构建不可变领域模型

<?php
readonly class Money {
    public function __construct(
        public float $amount,
        public Currency $currency
    ) {
        if ($amount currency->equals($other->currency)) {
            throw new InvalidArgumentException("货币类型不匹配");
        }
        
        return new Money($this->amount + $other->amount, $this->currency);
    }
    
    public function multiply(float $multiplier): Money {
        return new Money($this->amount * $multiplier, $this->currency);
    }
    
    public function equals(Money $other): bool {
        return $this->amount === $other->amount && 
               $this->currency->equals($other->currency);
    }
}

readonly class Currency {
    public function __construct(
        public string $code,
        public string $symbol
    ) {
        if (strlen($code) !== 3) {
            throw new InvalidArgumentException("货币代码必须是3个字符");
        }
    }
    
    public function equals(Currency $other): bool {
        return $this->code === $other->code;
    }
}

// 使用示例
$usd = new Currency('USD', '$');
$money1 = new Money(100.50, $usd);
$money2 = new Money(50.25, $usd);

$total = $money1->add($money2);
echo $total->amount; // 输出: 150.75
?>

3. 构建类型安全的电商系统

结合枚举和只读类,我们可以构建一个类型安全的电商领域模型。

3.1 核心领域对象

<?php
readonly class ProductId {
    public function __construct(public string $value) {
        if (!preg_match('/^PROD-[A-Z0-9]{8}$/', $value)) {
            throw new InvalidArgumentException("无效的产品ID格式");
        }
    }
}

readonly class CustomerId {
    public function __construct(public string $value) {
        if (!preg_match('/^CUST-[A-Z0-9]{8}$/', $value)) {
            throw new InvalidArgumentException("无效的客户ID格式");
        }
    }
}

enum ProductStatus {
    case ACTIVE;
    case INACTIVE;
    case OUT_OF_STOCK;
    case DISCONTINUED;
}

readonly class Product {
    public function __construct(
        public ProductId $id,
        public string $name,
        public string $description,
        public Money $price,
        public int $stockQuantity,
        public ProductStatus $status
    ) {}
    
    public function reduceStock(int $quantity): Product {
        if ($quantity > $this->stockQuantity) {
            throw new InvalidArgumentException("库存不足");
        }
        
        return new Product(
            $this->id,
            $this->name,
            $this->description,
            $this->price,
            $this->stockQuantity - $quantity,
            $this->status
        );
    }
    
    public function isAvailable(): bool {
        return $this->status === ProductStatus::ACTIVE && 
               $this->stockQuantity > 0;
    }
}
?>

3.2 订单聚合根

<?php
readonly class OrderId {
    public function __construct(public string $value) {
        if (!preg_match('/^ORD-[A-Z0-9]{8}$/', $value)) {
            throw new InvalidArgumentException("无效的订单ID格式");
        }
    }
}

enum OrderStatus {
    case CREATED;
    case PAID;
    case SHIPPED;
    case DELIVERED;
    case CANCELLED;
    
    public function canTransitionTo(self $newStatus): bool {
        return match($this) {
            self::CREATED => in_array($newStatus, [self::PAID, self::CANCELLED]),
            self::PAID => in_array($newStatus, [self::SHIPPED, self::CANCELLED]),
            self::SHIPPED => $newStatus === self::DELIVERED,
            self::DELIVERED, self::CANCELLED => false
        };
    }
}

readonly class OrderItem {
    public function __construct(
        public ProductId $productId,
        public string $productName,
        public int $quantity,
        public Money $unitPrice
    ) {
        if ($quantity unitPrice->multiply($this->quantity);
    }
}

readonly class Order {
    public function __construct(
        public OrderId $id,
        public CustomerId $customerId,
        public array $items,
        public OrderStatus $status,
        public Money $totalAmount,
        public DateTimeImmutable $createdAt
    ) {
        if (empty($items)) {
            throw new InvalidArgumentException("订单必须包含商品");
        }
        
        // 验证总金额
        $calculatedTotal = array_reduce(
            $items,
            fn(Money $total, OrderItem $item) => $total->add($item->subtotal()),
            new Money(0, $this->totalAmount->currency)
        );
        
        if (!$calculatedTotal->equals($this->totalAmount)) {
            throw new InvalidArgumentException("总金额计算错误");
        }
    }
    
    public static function create(CustomerId $customerId, array $items): self {
        if (empty($items)) {
            throw new InvalidArgumentException("订单必须包含商品");
        }
        
        $currency = $items[0]->unitPrice->currency;
        $total = array_reduce(
            $items,
            fn(Money $total, OrderItem $item) => $total->add($item->subtotal()),
            new Money(0, $currency)
        );
        
        return new self(
            new OrderId('ORD-' . bin2hex(random_bytes(4))),
            $customerId,
            $items,
            OrderStatus::CREATED,
            $total,
            new DateTimeImmutable()
        );
    }
    
    public function cancel(): self {
        if (!$this->status->canTransitionTo(OrderStatus::CANCELLED)) {
            throw new InvalidArgumentException("订单无法取消");
        }
        
        return new self(
            $this->id,
            $this->customerId,
            $this->items,
            OrderStatus::CANCELLED,
            $this->totalAmount,
            $this->createdAt
        );
    }
}
?>

4. 服务层与业务逻辑

使用PHP 8.3的新特性构建健壮的服务层。

4.1 订单服务

<?php
class OrderService {
    public function __construct(
        private OrderRepository $orderRepository,
        private ProductRepository $productRepository,
        private EventDispatcher $eventDispatcher
    ) {}
    
    public function createOrder(CustomerId $customerId, array $productQuantities): Order {
        // 验证产品库存
        $orderItems = [];
        foreach ($productQuantities as $productId => $quantity) {
            $product = $this->productRepository->findById($productId);
            
            if (!$product || !$product->isAvailable()) {
                throw new DomainException("产品不可用: " . $productId->value);
            }
            
            if ($product->stockQuantity name);
            }
            
            $orderItems[] = new OrderItem(
                $product->id,
                $product->name,
                $quantity,
                $product->price
            );
        }
        
        // 创建订单
        $order = Order::create($customerId, $orderItems);
        
        // 保存订单
        $this->orderRepository->save($order);
        
        // 减少库存
        foreach ($productQuantities as $productId => $quantity) {
            $product = $this->productRepository->findById($productId);
            $updatedProduct = $product->reduceStock($quantity);
            $this->productRepository->save($updatedProduct);
        }
        
        // 发布领域事件
        $this->eventDispatcher->dispatch(new OrderCreated($order));
        
        return $order;
    }
    
    public function cancelOrder(OrderId $orderId): Order {
        $order = $this->orderRepository->findById($orderId);
        
        if (!$order) {
            throw new DomainException("订单不存在");
        }
        
        $cancelledOrder = $order->cancel();
        $this->orderRepository->save($cancelledOrder);
        
        // 恢复库存
        foreach ($cancelledOrder->items as $item) {
            $product = $this->productRepository->findById($item->productId);
            $updatedProduct = new Product(
                $product->id,
                $product->name,
                $product->description,
                $product->price,
                $product->stockQuantity + $item->quantity,
                $product->status
            );
            $this->productRepository->save($updatedProduct);
        }
        
        $this->eventDispatcher->dispatch(new OrderCancelled($cancelledOrder));
        
        return $cancelledOrder;
    }
}

// 领域事件
readonly class OrderCreated {
    public function __construct(public Order $order) {}
}

readonly class OrderCancelled {
    public function __construct(public Order $order) {}
}
?>

5. 数据访问层实现

使用PHP 8.3的改进构建类型安全的数据访问层。

5.1 仓储模式实现

<?php
interface OrderRepository {
    public function findById(OrderId $id): ?Order;
    public function save(Order $order): void;
    public function findByCustomerId(CustomerId $customerId): array;
    public function findByStatus(OrderStatus $status): array;
}

class DatabaseOrderRepository implements OrderRepository {
    public function __construct(private PDO $pdo) {}
    
    public function findById(OrderId $id): ?Order {
        $stmt = $this->pdo->prepare("
            SELECT o.*, oi.product_id, oi.product_name, oi.quantity, oi.unit_price_amount, oi.unit_price_currency
            FROM orders o
            LEFT JOIN order_items oi ON o.id = oi.order_id
            WHERE o.id = ?
        ");
        
        $stmt->execute([$id->value]);
        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        if (empty($rows)) {
            return null;
        }
        
        return $this->hydrateOrder($rows[0], $rows);
    }
    
    private function hydrateOrder(array $orderData, array $itemsData): Order {
        $currency = new Currency($orderData['total_amount_currency'], '$');
        
        $items = array_map(function($itemData) use ($currency) {
            return new OrderItem(
                new ProductId($itemData['product_id']),
                $itemData['product_name'],
                (int)$itemData['quantity'],
                new Money((float)$itemData['unit_price_amount'], $currency)
            );
        }, $itemsData);
        
        return new Order(
            new OrderId($orderData['id']),
            new CustomerId($orderData['customer_id']),
            $items,
            OrderStatus::from($orderData['status']),
            new Money((float)$orderData['total_amount'], $currency),
            new DateTimeImmutable($orderData['created_at'])
        );
    }
    
    public function save(Order $order): void {
        $this->pdo->beginTransaction();
        
        try {
            // 保存订单主表
            $stmt = $this->pdo->prepare("
                INSERT INTO orders (id, customer_id, status, total_amount, total_amount_currency, created_at)
                VALUES (?, ?, ?, ?, ?, ?)
                ON DUPLICATE KEY UPDATE 
                    status = VALUES(status),
                    total_amount = VALUES(total_amount),
                    total_amount_currency = VALUES(total_amount_currency)
            ");
            
            $stmt->execute([
                $order->id->value,
                $order->customerId->value,
                $order->status->name,
                $order->totalAmount->amount,
                $order->totalAmount->currency->code,
                $order->createdAt->format('Y-m-d H:i:s')
            ]);
            
            // 删除原有订单项
            $this->pdo->prepare("DELETE FROM order_items WHERE order_id = ?")
                     ->execute([$order->id->value]);
            
            // 插入新订单项
            $itemStmt = $this->pdo->prepare("
                INSERT INTO order_items (order_id, product_id, product_name, quantity, unit_price_amount, unit_price_currency)
                VALUES (?, ?, ?, ?, ?, ?)
            ");
            
            foreach ($order->items as $item) {
                $itemStmt->execute([
                    $order->id->value,
                    $item->productId->value,
                    $item->productName,
                    $item->quantity,
                    $item->unitPrice->amount,
                    $item->unitPrice->currency->code
                ]);
            }
            
            $this->pdo->commit();
        } catch (Exception $e) {
            $this->pdo->rollBack();
            throw $e;
        }
    }
    
    public function findByCustomerId(CustomerId $customerId): array {
        // 实现根据客户ID查询订单的逻辑
        return [];
    }
    
    public function findByStatus(OrderStatus $status): array {
        // 实现根据状态查询订单的逻辑
        return [];
    }
}
?>

6. 测试策略与验证

为只读类和枚举构建可靠的测试套件。

6.1 单元测试示例

<?php
class OrderTest extends PHPUnitFrameworkTestCase {
    public function testOrderCreation(): void {
        $customerId = new CustomerId('CUST-12345678');
        $currency = new Currency('USD', '$');
        
        $items = [
            new OrderItem(
                new ProductId('PROD-12345678'),
                '测试产品',
                2,
                new Money(50.0, $currency)
            )
        ];
        
        $order = Order::create($customerId, $items);
        
        $this->assertInstanceOf(Order::class, $order);
        $this->assertEquals(OrderStatus::CREATED, $order->status);
        $this->assertEquals(100.0, $order->totalAmount->amount);
    }
    
    public function testOrderCancellation(): void {
        $customerId = new CustomerId('CUST-12345678');
        $currency = new Currency('USD', '$');
        
        $items = [
            new OrderItem(
                new ProductId('PROD-12345678'),
                '测试产品',
                1,
                new Money(100.0, $currency)
            )
        ];
        
        $order = Order::create($customerId, $items);
        $cancelledOrder = $order->cancel();
        
        $this->assertEquals(OrderStatus::CANCELLED, $cancelledOrder->status);
        $this->assertNotSame($order, $cancelledOrder);
    }
    
    public function testInvalidOrderCreation(): void {
        $this->expectException(InvalidArgumentException::class);
        
        $customerId = new CustomerId('CUST-12345678');
        Order::create($customerId, []);
    }
}

class MoneyTest extends PHPUnitFrameworkTestCase {
    public function testMoneyAddition(): void {
        $currency = new Currency('USD', '$');
        $money1 = new Money(100.0, $currency);
        $money2 = new Money(50.0, $currency);
        
        $result = $money1->add($money2);
        
        $this->assertEquals(150.0, $result->amount);
        $this->assertNotSame($money1, $result);
    }
    
    public function testDifferentCurrencyAddition(): void {
        $this->expectException(InvalidArgumentException::class);
        
        $usd = new Currency('USD', '$');
        $eur = new Currency('EUR', '€');
        
        $money1 = new Money(100.0, $usd);
        $money2 = new Money(50.0, $eur);
        
        $money1->add($money2);
    }
}
?>

7. 性能优化与最佳实践

7.1 只读类的性能优势

<?php
// 性能测试:只读类 vs 传统类
function benchmarkClassCreation(): void {
    $iterations = 100000;
    
    // 传统可变类
    $start = microtime(true);
    for ($i = 0; $i < $iterations; $i++) {
        $obj = new TraditionalMoney($i, new TraditionalCurrency('USD'));
    }
    $traditionalTime = microtime(true) - $start;
    
    // 只读类
    $start = microtime(true);
    for ($i = 0; $i < $iterations; $i++) {
        $obj = new Money($i, new Currency('USD', '$'));
    }
    $readonlyTime = microtime(true) - $start;
    
    echo "传统类: " . number_format($traditionalTime, 4) . " 秒n";
    echo "只读类: " . number_format($readonlyTime, 4) . " 秒n";
    echo "性能提升: " . number_format(($traditionalTime - $readonlyTime) / $traditionalTime * 100, 1) . "%n";
}

class TraditionalMoney {
    public function __construct(
        public float $amount,
        public TraditionalCurrency $currency
    ) {}
}

class TraditionalCurrency {
    public function __construct(public string $code) {}
}
?>

7.2 内存使用分析

<?php
function analyzeMemoryUsage(): void {
    $objectsCount = 10000;
    
    // 传统类内存使用
    $memoryBefore = memory_get_usage();
    $traditionalObjects = [];
    for ($i = 0; $i < $objectsCount; $i++) {
        $traditionalObjects[] = new TraditionalMoney($i, new TraditionalCurrency('USD'));
    }
    $traditionalMemory = memory_get_usage() - $memoryBefore;
    
    // 只读类内存使用
    $memoryBefore = memory_get_usage();
    $readonlyObjects = [];
    for ($i = 0; $i < $objectsCount; $i++) {
        $readonlyObjects[] = new Money($i, new Currency('USD', '$'));
    }
    $readonlyMemory = memory_get_usage() - $memoryBefore;
    
    echo "传统类内存: " . number_format($traditionalMemory / 1024, 2) . " KBn";
    echo "只读类内存: " . number_format($readonlyMemory / 1024, 2) . " KBn";
    echo "内存节省: " . number_format(($traditionalMemory - $readonlyMemory) / $traditionalMemory * 100, 1) . "%n";
}
?>

总结

PHP 8.3的枚举和只读类为企业级应用开发带来了重大改进:

  • 枚举提供了类型安全的常量定义和丰富的方法支持
  • 只读类确保了对象的不可变性,增强了线程安全性
  • 两者结合可以构建健壮的领域驱动设计架构
  • 性能优化和内存效率得到显著提升
  • 代码可维护性和可测试性大幅改善

这些新特性使得PHP在现代Web开发中继续保持竞争力,特别是在需要高度可靠性和可维护性的企业应用中。开发者应该积极采用这些现代PHP特性,以构建更健壮、更安全的应用程序。

PHP 8.3新特性实战:枚举与只读类的企业级应用 | PHP现代化开发
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 php PHP 8.3新特性实战:枚举与只读类的企业级应用 | PHP现代化开发 https://www.taomawang.com/server/php/1181.html

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务