PHP Attributes: how to use PHP Attributes and create custom attribute classes – Fast Tips

Valerio Barbera

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 ParentClass {
    #[Override]
    public function someMethod() {
        // Method implementation
    }
}

@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:

use Symfony\Component\Routing\Annotation\Route;
class SomeController
{
    #[Route('/path', 'action_name')]
    public function someAction()
    {
        // ...
    }
}

Given the native support at the language level, and the possibility of interacting through the Reflection APIs, attributes offer advantages related to simplicity of query and performance, compared to parsing annotations in comments.

Attributes in the Inspector PHP library

The issue in Inspector’s PHP library appeared because in PHP 8 the ArrayAccess interface introduced attributes to alert developers that the signature of some interface methods will be changed in future versions.

The original implementation if ArrayAccess in the library was:

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 attributes 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 a SaaS environment where scalability, maintainability, and efficiency are paramount.

New to Inspector? Try it for free now

Are you responsible for application development in your company? Consider trying my product Inspector to find out bugs and bottlenecks in your code automatically. Before your customers stumble onto the problem.

Inspector is usable by any IT leader who doesn’t need anything complicated. If you want effective automation, deep insights, and the ability to forward alerts and notifications into your messaging environment try Inspector for free. Register your account.

Or learn more on the website: https://inspector.dev

Related Posts

Create a query for histogram charts with MySQL – Tutorial

To create a statistical query to build a histogram chart with MySQL, you can use the COUNT() function along with GROUP BY to count occurrences of values within a specified range or category created by the grouping constraint.  Especially for time series data there are a lot of use cases for histograms like monitoring the

Try the new Google Chat notification channel

Hi, I’m happy to announce that Google Chat is now available as a notification channel for your application. Now you can receive all important notifications of what happens in your application in your Google Chat environment in real time. Inspector Notification Channels allow you to create an automated and  informed workplace, helping your collaborators to

AI content creation to help you improve the Developer Experience

Co-founders are the first people in the trenches for any start-up, taking the idea and turning it into a sustainable business that can really make other people’s life better. In these early years of Inspector I have never told about my co-founders thanks to whom I was able to spend all my time building the