This content was deleted by the author. You can see it from Blockchain History logs.

A robust, powerful, easy to use attribute parser for PHP

Announcing AttributeUtils

After sitting on it for a while, I am pleased to release a PHP library for robust, powerful attribute handling in PHP 8.1: Crell/AttributeUtils. It's a robust, centralized tool for slicing, dicing, and reverse engineering classes in PHP using attributes.

The basics

The basic model of Attribute Utils is to analyze a class "with respect to" some attribute. That attribute may be defined on the class, or not. (Both options have meaning.) That attribute may be associated with attributes on properties or methods, which are all scanned together to produce a picture of a class.

For example, this attribute class implements an interface that tells the library to also scan properties of a class for the MyProperty attribute.

#[\Attribute(\Attribute::TARGET_CLASS)]
class MyClass implements ParseProperties
{
    public readonly array $properties;

    public function propertyAttribute(): string
    {
        return MyProperty::class;
    }

    public function setProperties(array $properties): void
    {
        $this->properties = $properties;
    }

    public function includePropertiesByDefault(): bool
    {
        return true;
    }
}

#[\Attribute(\Attribute::TARGET_PROPERTY)]
class MyProperty
{
    public function __construct(
        public readonly string $column = '',
    ) {}
}

#[MyClass]
class Something
{
    #[MyProperty(column: 'beep')]
    protected property $foo;
    
    private property $bar;
}

$attrib = $analyzer->analyze(Something::class, MyClass::class);

The same works for methods, constants, and methods can also scan their parameters in the same way.

Reflection

The Analzyer class can also opt-in to being passed the reflection object whence it came. That allows and an attribute to set its own defaults based on reflection information on the class, property, method, etc. that it came from.

#[\Attribute]
class AttribWithName implements FromReflectionClass 
{
    public readonly string $name;
    
    public function __construct(?string $name = null) 
    {
        if ($name) {
            $this->name = $name;
        }
    }
    
    public function fromReflection(\ReflectionClass $subject): void
    {
        $this->name ??= $subject->getShortName();
    }
}

Advanced features

There's a lot more that the Attribute Utils Analzyer can do, which is documented in the project README. It can opt-in to default values for missing attributes, excluding values, allowing attributes to be inherited, multi-instance attributes, even transitive-inheritance from referenced classes. All of that is documented in detail on the project page.

Of special note is support for multiple scopes; that is, the ability to have different variants of the same attribute that may be scanned separately from each other. If you've used "serialization groups" before, or want different attributes for different languages, scopes make that easy.

Intended audience

This library is aimed mainly at other library authors. Its goal is to support anyone building libraries that want to leverage attributes in robust ways to enable other functionality, by making working with attributes simple and fast.

If in the past you would have thought to use Doctrine Annotations, think of this as an Attribute-centric alternative on steroids. It takes all the power of PHP attributes and punches them up to 11, in a simple, declarative way.

At the moment, I would consider Attribute Utils in Release Candidate. It's ready to use, and close to feature complete, but I'm looking for feedback before declaring it fully stable. I do not anticipate any BC breaks at this point.

So please, give it a whirl, give feedback, and let your attributes fly in your own projects!