Support the ongoing development of Laravel.io →
Article Hero Image

The #[\Override] Attribute in PHP

5 Jul, 2024 7 min read

Photo by Getty Images on Unsplash

Introduction

A handy #[\Override] attribute was added in PHP 8.3 which allows you to mark a method as an override of a parent method.

In this Quickfire article, we're going to take a look at what the #[\Override] attribute is and how you can use it in your code. We'll also look at the benefits of using it and how it can help with code maintenance.

What is the #[\Override] attribute?

First of all, if you've not come across attributes before, you might be interested in reading my "Guide to PHP Attributes" article which explains what they are, how to use them, and how to create your own.

The #[\Override] attribute is a new attribute that was introduced in PHP 8.3. You can use it to signal that a method is an override of a parent method.

For example, say you had the following parent class:

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

And you had a child class that extended the parent class and overrode the someMethod method:

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

As we can see in the code example above, we're using the #[\Override] attribute to indicate that the someMethod method in the ChildClass class is overriding the someMethod method in the ParentClass class.

Benefits of Using the #[\Override] Attribute

By using the #[\Override] attribute, you can:

Detect Errors at Runtime

The #[\Override] attribute can only be applied to methods that actually override a parent method.

If you apply the attribute to a method that doesn't override a parent method, PHP will throw a fatal error when the method is called. This can help you to catch errors and prevent unexpected behaviour.

Sticking with our previous example, we'll imagine the someMethod method no longer exists in the ParentClass class. Maybe it was removed by another developer in your team, or removed from a dependency. If we were to run our application, we'd see the following error:

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

Detecting Errors with Static Analysis

How would you know if one of your child classes was attempting to override a method that didn't exist anymore in the parent class? Say the parent class belongs to a third-party Composer package that you're using. During an update, the package maintainer might have removed a method that you were overriding in your child class. You might not notice this until you run your application and see an error.

Using the #[\Override] attribute would allow static analysis tools (such as PHPStan) to help you detect errors without needing to run your application.

Using our previous example, if we were to run PHPStan on our codebase, we'd see the following error:

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

[ERROR] Found 1 error

This is great because it means you can get instant feedback on your code and catch errors before they make it to production.

As a side note, I actually have a chapter in "Battle Ready Laravel" that shows you how to use Larastan/PHPStan to audit and improve your Laravel applications.

Improved IDE Support

Another benefit of using the #[\Override] attribute is that you can signal to your integrated development environment (IDE) that a method is supposed to be overriding a parent method.

If you're using an IDE, such as PHPStorm, you'll have likely noticed that they already indicate when a method is overriding a parent method, usually through the use of an icon or button. But this is only showing you that a method is overriding a parent method. It doesn't show intention. Using our previous examples, if the someMethod method no longer existed in the ParentClass class, PHPStorm would simply not show the icon anymore to indicate that the method is overriding a parent method. So it wouldn't indicate to us that there's an issue and you might not notice.

By applying the #[\Override] attribute, if our method is no longer overriding a parent method, PHPStorm will display an error for us, indicating that there's an issue.

Clearer Code

Another great benefit of using the #[\Override] attribute is that it makes your code clearer and easier to understand.

Say you're looking at a class for the first time. It may not be clear at first glance which methods are overriding parent methods. By applying the #[\Override] attribute, you can get a quick overview. This can be particularly useful when working with large codebases or when working with code that you're not familiar with. So it can help to get you familiar with the class quicker.

Real-life Example Usage

To get a better idea of how you might use the #[\Override] attribute in a real-life scenario, let's look at an example.

We'll imagine we have an App\Models\User model class in our Laravel application that extends Laravel's Illuminate\Foundation\Auth\User class. The parent class provides an empty casts method that we can override in our child class to define the casts for our model attributes.

Our App\Models\User class might look something like this:

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',
        ];
    }
}

In our casts model above, we're defining that our model's email_verified_at attribute should use the datetime cast, meaning it'll typically be cast to an instance of Carbon\Carbon when retrieved from the database. We're also defining that the password attribute should use the hashed cast, meaning it'll be automatically hashed when saved to the database.

Now these are operations that we rely on automatically working, and they could have drastic consequences if they didn't work as expected. For example, we wouldn't want the password attribute to be stored in plain text in the database! We need the passwords to be hashed. Of course, Laravel's casting system is well-tested and reliable, and in an ideal world, you should also have tests in place to ensure that your fields are being cast as expected.

But hypothetically, let's say the casts method is replaced in a future version of Laravel with a new method called castsAttributes. So Laravel's Illuminate\Foundation\Auth\User no longer has an empty casts method anymore. This would mean our App\Models\User class would still have the casts method but it wouldn't be overriding anything anymore. And we can assume this means the casts wouldn't be applied to our fields anymore as expected.

But how would you know? It's entirely possible to have a method in your class that you think is overriding a parent method, but in fact, isn't. You might have missed the section mentioning it in the upgrade guide. Assuming it wasn't caught by any tests, you might not notice until you see unexpected behaviour in your application and bug reports start coming in.

We can apply the #[\Override] attribute to the casts method like so:

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',
        ];
    }
}

As a result of doing this, it now means if the casts method was to be removed, the #[\Override] attribute would cause a fatal error to be thrown and signal to us that there's an issue.

As you can imagine, this is really handy and is an extra tool to help us reduce the chances of bugs making it to production.

Conclusion

Hopefully, this article has given you an insight into what the #[\Override] attribute is and how you can use it in your code.

If you enjoyed reading this post, you might be interested in checking out my 220+ page ebook "Battle Ready Laravel" which covers similar topics in more depth.

Or, you might want to check out my other 440+ page ebook "Consuming APIs in Laravel" which teaches you how to use Laravel to consume APIs from other services.

Keep on building awesome stuff! 🚀

Last updated 3 weeks ago.

driesvints liked this article

1
Like this article? Let the author know and give them a clap!
ash-jc-allen (Ash Allen) I'm a freelance Laravel web developer from Preston, UK. I maintain the Ash Allen Design blog and get to work on loads of cool and exciting projects 🚀

Other articles you might like

Article Hero Image November 18th 2024

Laravel Custom Query Builders Over Scopes

Hello 👋 Alright, let's talk about Query Scopes. They're awesome, they make queries much easier to r...

Read article
Article Hero Image November 19th 2024

Access Laravel before and after running Pest tests

How to access the Laravel ecosystem by simulating the beforeAll and afterAll methods in a Pest test....

Read article
Article Hero Image November 11th 2024

🍣 Sushi — Your Eloquent model driver for other data sources

In Laravel projects, we usually store data in databases, create tables, and run migrations. But not...

Read article

We'd like to thank these amazing companies for supporting us

Your logo here?

Laravel.io

The Laravel portal for problem solving, knowledge sharing and community building.

© 2024 Laravel.io - All rights reserved.