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

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? Monitor your application for free

Inspector is a Code Execution Monitoring tool specifically designed for software developers. You don’t need to install anything in the infrastructure, 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 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

Laravel Http Client Overview and Monitoring

Laravel HTTP client was introduced starting from version 10 of the framework, and then also made available in all previous versions. It stands out as a powerful tool for making HTTP requests and handling responses from external services. This article will delve into the technical foundations of the Laravel HTTP client, its motivations, and how

Laravel Form Request and Data Validation Tutorial

In this article I will talk about Laravel Form Request to send data from your application frontend to the backend. In web applications, data is usually sent via HTML forms: the data entered by the user into the browser is sent to the server and stored in the database eventually. Laravel makes it extremely simple

Upload File in Laravel

You can upload file in Laravel using its beautiful unified API to interact with many different types of storage systems, from local disk to remote object storage like S3. As many other Laravel components you can interact with the application filesystem through the Storage Facade: Illuminate/Support/Facades/Storage This class allows you to access storage drivers called