PHP attributes were introduced in PHP 8.0. This version marked a significant milestone for the language, bringing several new features and improvements, including the introduction of attributes for adding metadata to code declarations.
The first time I had to deal with attributes was due to an issue in the Inspector’s PHP library. Check on GitHub. Before digging deeper into the solution, let’s take an overview of what attributes are and how they can be used in your PHP code.
Attributes are a powerful feature that allows you to add metadata to declarations like classes, methods, or properties. These metadata can be retrieved programmatically, opening up new possibilities for cleaner, more organized, and efficient code.
Remeber, Attributes have no effect at runtime. They will be available in the Reflection APIs to make your application aware of things you want to run based on attributes attached to a class, method, or a property.
Built-in Attributes
PHP comes with several built-in attributes that serve different purposes. Here are a few notable ones:
Deprecated
Marks a function or method as deprecated, signaling that it should be avoided as it may be removed in future versions.
#[Deprecated("Use newFunction() instead")]
function oldFunction()
{
// Function implementation
}
Override
Ensures that the method in a child class is intended to override a method in the parent class.
class Child extends Parent {
#[Override]
public function defaultMethod()
{
// Method implementation
}
}
If defaultMethod()
will eventually change its name in the parent class this is no longer an override. In this case PHP will raises a warning because we explicitly declared we expect to be an override and alert us about the misalignement.
Anyway, a good IDE should allow us to not incur in this kind of mistakes.
SuppressWarnings
Suppresses specific warnings for a particular piece of code.
#[SuppressWarnings("SomeWarning")]
function someFunction()
{
// Function implementation
}
Creating Custom Attribute Classes
Now, let's create a custom attribute class. This is beneficial when you want to encapsulate specific behavior within an attribute.
#[Attribute]
class CustomAttribute
{
public string $message;
public function __construct(string $message)
{
$this->message = $message;
}
}
You can then use this custom attribute on various elements:
class MyClass
{
#[CustomAttribute("This is a custom attribute")]
public $myProperty;
#[CustomAttribute("Another custom attribute")]
public function myMethod()
{
// Method implementation
}
}
Usage Examples
Let's explore a practical example. Suppose you’re building a web application, and you want to create a custom attribute to define the length of a string:
#[Attribute(Attribute::TARGET_PROPERTY)]
class MaxLength
{
public int $maxLength;
public function __construct(int $maxLength)
{
$this->maxLength = $maxLength;
}
}
In the example above we restricted the ability to apply the attribute only to class properties. Now we can use it in the User class:
class User
{
#[MaxLength(20, message: "Username must be 20 characters or less")]
public string $username;
// Other properties and methods
}
As mentioned before adding the attribute to the property have no impact during execution. But we can now retrive this information using reflection to take some action eventually.
Adoption by PHP frameworks
The most used PHP frameworks like Symfony and Laravel are already adopting attributes to basically replace "Annotations". In Symfony 5.2 or greater you are able to declare a controller and wire it up to a route using attributes:
public class Arrayable implements \ArrayAccess
{
…
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
…
}
Since PHP 8 the definition of ArrayAccess
interface changed to:
With use of attributes they enforced the declaration of data type for the arguments in offsets functions. Using the LanguageLevelTypeAware attribute, if the implementation doesn’t provide the data type for arguments it fires a "deprecation warning".
But declaring the data type in function arguments breaks the compatibility with older versions of PHP that don't support arguments data type declaration.
Since it was just a warning for future changes we solved the problem with another built-in PHP attribute to suppress the warning:
public class Arrayable implements \ArrayAccess
{
…
#[\ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
…
}
The ReturnTypeWillChange
attribute simply tell PHP that we know about the future changes in the language, and we have already planned the necessary updates.
For this change in particular they will be definitely implemented in PHP 9.
Remember to use attributes judiciously, keeping your codebase clean and well-documented. This is particularly crucial in SaaS product development where scalability, maintainability, and efficiency are paramount.
Monitor your PHP application for free
Inspector is a Code Execution Monitoring tool specifically designed for software developers. You don't need to install anything at the server level, just install the composer package and you are ready to go.
Unlike other complex, all-in-one platforms, Inspector is super easy, and PHP friendly. You can try our Laravel or Symfony package.
If you are looking for HTTP monitoring, query insights, and the ability to forward alerts and notifications into your preferred messaging environment try Inspector for free. Register your account.
Or learn more on the website: https://inspector.dev
Top comments (4)
Nice summary, I haven't yet gotten to use attributes a lot in PHP but they're definitely a nice addition (much better than relying on PhpDoc annotations and the Reflection API).
Also you've inadvertantly tagged a couple dev.to profiles by using @ in your titles, you should probably replace them with the actual
#[]
syntax 😉Oh yes, I didn't realize that. I didn't know de.to allowed mentioning other accounts in articles.
Wow...thank you, that made it very clear to me.
🙏