支持Laravel.io的持续开发 →

PHP中的#[\Override]属性

2024年7月5日 阅读时间:7分钟

引言

PHP 8.3 添加了一个实用的 #[\Override] 属性,允许您标记一个方法为父方法的重写方法。

在这篇 快速热身 文章中,我们将探讨 #[\Override] 属性是什么,以及如何在您的代码中使用它。我们还将讨论使用它的好处以及它如何有助于代码维护。

什么是 #[\Override] 属性?

首先,如果您还没有遇到过属性,您可能想阅读我的 "PHP 属性指南" 文章,该文解释了它们是什么,如何使用它们以及如何创建自己的属性。

#[\Override] 属性是 PHP 8.3 中引入的一个新属性,您可以使用它来指示一个方法是一个父方法的重写方法。

例如,假设您有一个以下父类

class ParentClass
{
    protected function someMethod(): void
    {
        // ...
    }
}

并且您有一个子类扩展了父类并重写了 someMethod 方法

class ChildClass extends ParentClass
{
    #[\Override]
    protected function someMethod(): void
    {
        // ...
    }
}

正如上面的代码示例所示,我们使用 #[\Override] 属性来说明 ChildClass 类中的 someMethod 方法正在重写 ParentClass 类中的 someMethod 方法。

使用 #[\Override] 属性的好处

使用 #[\Override] 属性,您可以

在运行时检测错误

属性#[\Override]只能应用于实际覆盖了父方法的方法。

如果您将此属性应用于没有覆盖父方法的方法,当调用该方法时,PHP将会引发致命错误。这可以帮助您捕捉到错误并防止意外行为。

继续我们之前的例子,我们假设在父类ParentClass中不再存在someMethod方法。也许它被您的团队中的其他开发者删除了,或者从依赖项中删除了。如果我们运行我们的应用程序,我们会看到以下错误

Fatal error: ChildClass::someMethod() has #[\Override] attribute, but no matching parent method exists

使用静态分析检测错误

您是如何知道您的子类之一试图覆盖已从父类中删除的方法呢?假设父类属于您正在使用的第三方Composer包。在更新过程中,包维护者可能会删除您在子类中覆盖的方法。您可能直到运行应用程序并看到错误时才会注意到这一点。

使用#[\Override]属性可以使静态分析工具(如PHPStan)帮助您在没有运行应用程序的情况下检测错误。

使用我们的之前例子,如果我们对我们整个代码库运行PHPStan,我们会看到以下错误

 ------ ----------------------------------------------------------------------------------------------- 
  Line   ChildClass.php                                                                                
 ------ ----------------------------------------------------------------------------------------------- 
  42     Method ChildClass::someMethod() has #[\Override] attribute but does not override any method.  
 ------ ----------------------------------------------------------------------------------------------- 

[ERROR] Found 1 error

这很好,因为它意味着您可以立即对代码提供反馈,并在其成为生产环境之前发现错误。

顺便提一下,我实际上在《Battle Ready Laravel》这本书中有一个章节,介绍了如何使用Larastan/PHPStan来审计和改进您的Laravel应用程序。

改进的IDE支持

使用#[\Override]属性的另一项好处是,您可以向您的集成开发环境(IDE)发出信号,表明一个方法应该覆盖父方法。

如果您使用的是一个IDE,比如PHPStorm,您可能已经注意到它们已经表明了当一个方法覆盖父方法时的情况,通常是通过图标或按钮。但这只显示了一个方法正在覆盖父方法。它并没有显示意图。使用我们之前的例子,如果someMethod方法不再在ParentClass类中存在,PHPStorm将不再显示该图标来表示此方法正在覆盖父方法。因此,它不会向我们显示问题,这可能会导致我们未注意到。

通过应用#[\Override]属性,如果我们的方法不再覆盖父方法,PHPStorm会为我们显示错误,表明存在问题。

更清晰的代码

使用#[\Override]属性的另一大好处是,它使代码更清晰,更易于理解。

假设您首次查看一个类。一开始可能并不清楚哪些方法是覆盖了父方法。通过应用#[\Override]属性,您可以快速获取概览。这在处理大代码库或者处理您不熟悉的代码时特别有用。因此,它可以帮助您更快地熟悉类。

真实世界用法示例

为了更好地了解您如何在真实场景中使用#[\Override]属性,让我们看看一个例子。

让我们假设在我们的Laravel应用程序中有一个扩展Laravel的Illuminate\Foundation\Auth\User类的App\Models\User模型类。父类提供了一个空的方法casts,我们可以在子类中覆盖它来为模型属性定义casts。

我们的App\Models\User类可能看起来像这样

declare(strict_types=1);

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

final class User extends Authenticatable
{
    // ...

    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}

在我们上面的casts模型中,我们定义了模型中的email_verified_at属性应使用datetime转换,这意味着在从数据库检索时通常会将其转换为Carbon\Carbon的一个实例。我们还定义了password属性应使用hashed转换,这意味着当保存到数据库时将被自动加密

现在这些是我们依赖于自动执行的操作,如果它们没有按预期工作,它们可能会有严重的后果。例如,我们不希望password属性以明文形式存储在数据库中!我们需要密码被加密。当然,Laravel的转换系统经过了充分测试并且非常可靠,在一个理想的世界中,你也应该在代码中实施测试来确保你的字段被转换为预期。

但是,假设在Laravel的未来版本中,casts方法被一个新的方法castsAttributes所取代。这意味着Laravel的Illuminate\Foundation\Auth\User不再有空白的casts方法了。这意味着我们的App\Models\User类仍然有casts方法,但它不再覆盖任何内容。我们可以假设这意味着转换不再像预期的那样应用到我们的字段上。

但是你怎么会知道?有可能在你的类中有一个你认为是覆盖父类方法的方法,但实际上并没有。你可能错过了升级指南中提到的部分。如果没有通过任何测试捕获,你可能直到看到应用程序中出现意外的行为和漏洞报告开始出现才会意识到。

我们可以将#[\Override]属性应用到casts方法上,如下所示

declare(strict_types=1);

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Override;

final class User extends Authenticatable
{
    // ...

    #[Override]
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}

结果,如果casts方法被删除,#[\Override]属性将导致抛出一个致命错误,并提示我们存在问题。

如你所想,这非常实用,可以帮助我们减少将错误引入生产的可能性。

结论

希望这篇文章能让你对#[\Override]属性有了一定的了解,以及如何在代码中使用它。

如果你喜欢阅读这篇帖子,你可能会对我的220多页电子书感兴趣,“Battle Ready Laravel”,它更深入地涵盖了类似的主题。

或者,你可能想查看我的另一本440多页的电子书“Consuming APIs in Laravel”,它教你如何使用Laravel从其他服务中消费API。

继续打造出色的东西!🚀

最新更新1周前。

driesvints 喜欢了这篇文章

1
喜欢这篇文章? 让作者知道并给予他们掌声!
ash-jc-allen (Ash Allen) 我是来自英国普雷斯顿的自由职业Laravel网页开发者。我维护Ash Allen Design博客,并且有机会参与许多酷炫和令人兴奋的项目 🚀

你可能还喜欢的其他文章

2024年3月11日

如何使用Larastan将您的Laravel应用程序从0到9提升

在使用Laravel应用程序之前检测到错误是可能的,多亏了Larastan,它...

阅读文章
2024年7月19日

无需特性标准化API响应

我发现大多数用于API响应的库都是使用特性实现的...

阅读文章
2024年7月17日

通过Discord通知在您的Laravel项目中收集反馈

如何在Laravel项目中创建反馈模块并在收到消息时通过Discord通知...

阅读文章

感谢这些 精彩的公司 对我们的支持

您的标志在这里?

Laravel.io

Laravel问题解决、知识共享和社区建设的门户。

© 2024 Laravel.io - 版权所有。