The #[\Override] Attribute in PHP
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! 🚀
driesvints liked this article
Other articles you might like
Laravel Custom Query Builders Over Scopes
Hello 👋 Alright, let's talk about Query Scopes. They're awesome, they make queries much easier to r...
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....
Create a backup of your Laravel project on Dropbox
How to quickly create a backup of a Laravel project and store it on Dropbox using Spatie Laravel-bac...
The Laravel portal for problem solving, knowledge sharing and community building.
The community