PHP 8:“之前”和“之后”代码(与PHP 7.4相比)





PHP 8推出还剩几个月,而且此发行版中确实有很多不错的东西。在精简版下,我们将告诉您这些创新如何开始改变作者编写代码的方法。



具有属性的事件订阅者



我将尽量不要过度使用属性,但是例如在设置事件侦听器的情况下,它们非常有用。



最近,我一直在研究存在很多此类设置的系统。让我们举个例子:



class CartsProjector implements Projector
{
    use ProjectsEvents;

    protected array $handlesEvents = [
        CartStartedEvent::class => 'onCartStarted',
        CartItemAddedEvent::class => 'onCartItemAdded',
        CartItemRemovedEvent::class => 'onCartItemRemoved',
        CartExpiredEvent::class => 'onCartExpired',
        CartCheckedOutEvent::class => 'onCartCheckedOut',
        CouponAddedToCartItemEvent::class => 'onCouponAddedToCartItem',
    ];

    public function onCartStarted(CartStartedEvent $event): void
    { /* … */ }

    public function onCartItemAdded(CartItemAddedEvent $event): void
    { /* … */ }

    public function onCartItemRemoved(CartItemRemovedEvent $event): void
    { /* … */ }

    public function onCartCheckedOut(CartCheckedOutEvent $event): void
    { /* … */ }

    public function onCartExpired(CartExpiredEvent $event): void
    { /* … */ }

    public function onCouponAddedToCartItem(CouponAddedToCartItemEvent $event): void
    { /* … */ }
}


PHP



8中的PHP 7属性具有两个优点:



  • 设置事件侦听器和处理程序的代码放在一个地方,我不必滚动到开头即可查看侦听器的设置是否正确。
  • 我不再需要担心以字符串形式编写和处理方法名称(当IDE无法自动完成它们时,就不会对输入错误进行静态分析,并且方法的重命名也不起作用)。


class CartsProjector implements Projector
{
    use ProjectsEvents;

    @@SubscribesTo(CartStartedEvent::class)
    public function onCartStarted(CartStartedEvent $event): void
    { /* … */ }

    @@SubscribesTo(CartItemAddedEvent::class)
    public function onCartItemAdded(CartItemAddedEvent $event): void
    { /* … */ }

    @@SubscribesTo(CartItemRemovedEvent::class)
    public function onCartItemRemoved(CartItemRemovedEvent $event): void
    { /* … */ }

    @@SubscribesTo(CartCheckedOutEvent::class)
    public function onCartCheckedOut(CartCheckedOutEvent $event): void
    { /* … */ }

    @@SubscribesTo(CartExpiredEvent::class)
    public function onCartExpired(CartExpiredEvent $event): void
    { /* … */ }

    @@SubscribesTo(CouponAddedToCartItemEvent::class)
    public function onCouponAddedToCartItem(CouponAddedToCartItemEvent $event): void
    { /* … */ }
}


PHP 8



静态而不是文档块



这不是很大的变化,但我每天都看到。我经常发现当我需要指定一个函数具有静态返回类型时,我仍然需要文档块。



如果在PHP 7.4中,我需要编写:



/**
 * @return static
 */
public static function new()
{
    return new static();
}


PHP 7.4



然后就足够了:



public static function new(): static
{
    return new static();
}


PHP 8



DTO,传递属性和命名参数



我已经写了很多有关使用PHP类型系统和DTO(数据传输对象模式的文章自然,我在自己的代码中经常使用DTO,所以您可以想象我现在能够重写此代码多么高兴:



class CustomerData extends DataTransferObject
{
    public string $name;

    public string $email;

    public int $age;
    
    public static function fromRequest(
        CustomerRequest $request
    ): self {
        return new self([
            'name' => $request->get('name'),
            'email' => $request->get('email'),
            'age' => $request->get('age'),
        ]);
    }
}

$data = CustomerData::fromRequest($customerRequest);


PHP 7.4



在此更好:



class CustomerData
{
    public function __construct(
        public string $name,
        public string $email,
        public int $age,
    ) {}
}

$data = new CustomerData(...$customerRequest->validated());


PHP 8



请注意,将传递构造函数属性用作命名参数。是的,可以使用命名数组和Spread运算符传递它们。



枚举和匹配



您是否将枚举与一些根据枚举的特定值返回结果的方法一起使用?



/**
 * @method static self PENDING()
 * @method static self PAID()
 */
class InvoiceState extends Enum
{
    private const PENDING = 'pending';
    private const PAID = 'paid';

    public function getColour(): string
    {
        return [
            self::PENDING => 'orange',
            self::PAID => 'green',
        ][$this->value] ?? 'gray';   
    }
}


PHP 7.4



我要说的是,对于更复杂的条件,最好使用State模式,但是在某些情况下枚举就足够了。这种奇怪的数组语法已经是更麻烦的条件表达式的简写形式:



/**
 * @method static self PENDING()
 * @method static self PAID()
 */
class InvoiceState extends Enum
{
    private const PENDING = 'pending';
    private const PAID = 'paid';

    public function getColour(): string
    {
        if ($this->value === self::PENDING) {
            return 'orange';
        }
    
        if ($this->value === self::PAID) {
            return 'green'
        }

        return 'gray';
    }
}


PHP 7.4-替代方法



但是在PHP 8中,我们可以改用match。



/**
 * @method static self PENDING()
 * @method static self PAID()
 */
class InvoiceState extends Enum
{
    private const PENDING = 'pending';
    private const PAID = 'paid';

    public function getColour(): string
    {
        return match ($this->value) {
            self::PENDING => 'orange',
            self::PAID => 'green',
            default => 'gray',
        };
}


PHP 8



联接而不是文档块



这的工作方式与先前针对静态返回类型所述的方式类似。



/**
 * @param string|int $input
 *
 * @return string 
 */
public function sanitize($input): string;


PHP 7.4



public function sanitize(string|int $input): string;


PHP 8



抛出异常



以前,您不能在表达式中使用throw,这意味着您必须编写例如以下检查:



public function (array $input): void
{
    if (! isset($input['bar'])) {
        throw BarIsMissing::new();
    }
    
    $bar = $input['bar'];

    // …
}


PHP 7.4



在PHP 8中,throw成为一个表达式,这意味着您可以像这样使用它:



public function (array $input): void
{
    $bar = $input['bar'] ?? throw BarIsMissing::new();

    // …
}


PHP 8



空安全运算符



如果您熟悉空合并运算符,那么您会知道它的缺点:它不适用于方法调用。因此,我经常需要适合于此目的的中间检查或框架功能:



$startDate = $booking->getStartDate();
$dateAsString = $startDate ? $startDate->asDateTimeString() : null;


PHP 7.4



通过引入nullsafe运算符,我可以更轻松地解决此问题。



$dateAsString = $booking->getStartDate()?->asDateTimeString();


PHP 8



您认为PHP 8中的哪些创新很重要?






广告



用于开发和托管项目的服务器每个服务器都连接到受DDoS攻击保护的500兆通道,可以使用高速本地网络。我们提供多种关税计划,一键更改关税。非常方便的服务器控制面板和使用API​​的能力。快点检查!






All Articles