Skip to content
Windwalker

Attributes

Component
PHP8 Attributes decorator component.

Miscellaneous

About AttributeHandler

AttributeHandler is the only parameter of our attribute processor.

php
use Windwalker\Attributes\AttributeHandler;
use Windwalker\Attributes\AttributeInterface;
#[\Attribute]
class MyAttribute implements AttributeInterface
{
    public function __invoke(AttributeHandler $handler): callable
    {
        /** 
         * $ref can be:
         * @see \ReflectionObject for classes type 
         * @see \ReflectionClass  for classes type
         * @see \ReflectionFunctionAbstract for callable type
         * @see \ReflectionParameter for parameters type
         * @see \ReflectionProperty for properties type
         */
        $ref = $handler->getReflector();
      
        // The AttributesResolver object
        $resolver = $handler->getReflector(); 
        // Get previous result
        $result = $handler(...);
    }
}

Integrate to Any Objects

You can create AttributesResolver in some object to help this object handle attributes, here we use EventDispatcher as example:

php
use Windwalker\Attributes\AttributesAwareTrait;
use Windwalker\Attributes\AttributesResolver;
use Windwalker\Attributes\AttributeType;
class EventDispatcher 
{
    use AttributesAwareTrait;
    public function __construct()
    {
        $this->prepareAttributes($this->getAttributesResolver());
    }
    protected function prepareAttributes(AttributesResolver $resolver)
    {
        $resolver->registerAttribute(\ListenerTo::class, AttributeType::METHODS);
        $resolver->setOption('dispatcher', $this);
    }
    
    public function addListener(callable $callable)
    {
        // Register listener        
    }
    
    public function subscribe(object $subscriber)
    {
        $this->getAttributesResolver()->resolverMethods($subscriber);        
    }
}

Set object to option, then you can access it in attribute handler:

php
use Windwalker\Attributes\AttributeHandler;
use Windwalker\Attributes\AttributeInterface;
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class ListenTo implements AttributeInterface
{
    public function __construct(protected string $event) 
    {
        //
    }
    public function __invoke(AttributeHandler $handler): callable
    {
        return function () use ($handler) {
            $provider = $handler->getResolver()->getOption('dispatcher');
            $listener = $handler();
            $provider->addListener(
                $this->event,
                $listener
            );
            return $listener;
        };
    }
}

Run if Attributes Exists

AttributesResolver provides a simple static methods to run any callback if attribute exists.

php
use Windwalker\Attributes\AttributesAccessor;
$object = new Foo();
AttributesAccessor::runAttributeIfExists(
    new ReflectionObject($object), // Send any reflections
    SomeAttribute::class,
    function (SomeAttribute $attr) {
        // Run anything you want
    }
);
$ref = new ReflectionObject($object);
AttributesAccessor::runAttributeIfExists(
    $ref->getMethod('foo'), // Send ReflectionMethod
    SomeAttribute::class,
    function (SomeAttribute $attr) {
        // Run anything you want
    }
);

Available Handling Methods

MethodDescription
createObject(string $class, ...$args): objectCreate object by class and decorate it.
decorateObject(object $object): objectDecorate an exists object.
call(callable $callable, $args = [], ?object $context = null): mixedCall a callable, this will resolve methods, functions and their parameters.
resolveProperties(object $instance): objectModify object properties values.
resolveMethods(object $instance): objectResolve methods but won't change anything, just call your custom handler.
resolveConstants(object $instance): objectResolve class constants but won't change anything, just call your custom handler.
resolveObjectMembers(object $instance): objectThis will run resolveProperties(), resolveConstants() and resolveConstants() one time.

Released under the MIT License.