ock / reflector-aware-attributes
Requires
- php: >=8.3
Requires (Dev)
- phpunit/phpunit: ^10.5.38
This package is auto-updated.
Last update: 2025-05-30 14:26:16 UTC
README
Provides mechanisms for attribute objects to know about the symbol they are attached to.
Motivation
An attribute may want to know about the symbol it describes, to:
- Perform validation, and throw an exception if it is attached to the wrong symbol.
- Populate default values based on the symbol. E.g., an attribute attached to a parameter may populate a property value based on the parameter type.
Usage
Using AttributeConstructor::getReflector().
Create an attribute class that calls AttributeConstructor::getReflector()
in the constructor.
use Ock\ReflectorAwareAttributes\AttributeConstructor; use Ock\ReflectorAwareAttributes\ReflectorAwareAttributeInterface; #[\Attribute(\Attribute::TARGET_ALL)] class MyAttribute { public readonly \Reflector $reflector; public function __construct(): void { $this->reflector = AttributeConstructor::getReflector(); } }
Attach the attribute to a class or other symbol.
#[MyAttribute]
class MyClass {}
Call get_attributes()
to extract attributes instances from the class.
use function Ock\ReflectorAwareAttributes\get_attributes; $reflection_class = new \ReflectionClass(MyClass::class); $attribute_instances = get_attributes($reflection_class, MyAttribute::class); assert($attribute_instances[0] instanceof MyAttribute); assert($attribute_instances[0]->reflector === $reflection_class);
Using the interface with ->setReflector().
Create an attribute class that implements ReflectorAwareAttributeInterface
.
use Ock\ReflectorAwareAttributes\ReflectorAwareAttributeInterface; #[\Attribute(\Attribute::TARGET_ALL)] class MyReflectorAwareAttribute implements ReflectorAwareAttributeInterface { public readonly \Reflector $reflector; public function setReflector(\Reflector $reflector): void { $this->reflector = $reflector; } }
Attach the attribute to a class or other symbol.
#[MyReflectorAwareAttribute] function foo() {}
Call get_attributes()
to extract attributes instances from the function.
use function Ock\ReflectorAwareAttributes\get_attributes; $reflection_function = new \ReflectionFunction('foo'); $attribute_instances = get_attributes($reflection_function, MyReflectorAwareAttribute::class); assert($attribute_instances[0] instanceof MyReflectorAwareAttribute); assert($attribute_instances[0]->reflector === $reflection_function);
Using AttributeReader.
The AttributeReader class allows to support attribute types that do not use the mechanisms from this package.
Assume you have an attribute class like this, coming from a 3rd party package:
#[\Attribute(\Attribute::TARGET_CLASS)] class ThirdPartyAttribute { public readonly \Reflector $reflector; }
You can create a custom instantiator that will populate the property.
Ideally this should use the decorator pattern, so that multiple operations can be applied.
use Ock\ReflectorAwareAttributes\Instantiator\AttributeInstantiatorInterface; use Ock\ReflectorAwareAttributes\Reader\AttributeReader; class MyInstantiator implements AttributeInstantiatorInterface { public function __construct( private readonly AttributeInstantiatorInterface $decorated, ) {} public function newInstance( \ReflectionAttribute $attribute, \ReflectionClassConstant|\ReflectionParameter|\ReflectionClass|\ReflectionProperty|\ReflectionFunctionAbstract $reflector, ): object { $instance = $this->decorated->newInstance($attribute, $reflector); if ($instance instanceof ThirdPartyAttribute) { $instance->reflector = $reflector; } return $instance; } }
Now we can use a reader with this instantiator decorator to get the attribute instances.
#[ThirdPartyAttribute] class C {} $reader = AttributeReader::basic() ->withDecoratingInstantiator( fn (AttributeInstantiatorInterface $decorated) => new MyInstantiator($decorated), ); $reflection_class = new \ReflectionClass(C::class); $instances = $reader->getInstances($reflection_class, ThirdPartyAttribute::class); assert($instances[0] instanceof ThirdPartyAttribute); assert($instances[0]->reflector === $reflection_class);