Crell/AttributeUtils

FromReflectionMethod binds the class declaring the method but gives no information about the class on which the method was invoked on

rares-mollie opened this issue · 3 comments

When using ParseMethods together with an attribute implementing FromReflectionMethod the fromReflection method is called with a \ReflectionMethod parameter that contains information about the declaring class but no information about the "bound" or "analysed" class.

Detailed description

I think it would be an enhancement to pass also information about the analysed class to the method attributes, as it is the class in the context of which the method is callable. This is also the behaviour that FromReflectionClass has.

Context

I am using attributes to mark methods to be traced by a tracing library, I need information about the object that is actually being instantiated as opposed to its class hierarchy.

Possible implementation

Add an interface that takes the analysed class as a parameter similar to FromReflectionClass

Your environment

not relevant

To replicate:

#[Attribute(Attribute::TARGET_CLASS)]
class classAttribute implements ParseMethods, Inheritable, FromReflectionClass
{
    public function setMethods(array $methods): void
    {
    }

    public function includeMethodsByDefault(): bool
    {
        return false;
    }

    public function methodAttribute(): string
    {
        return methodAttribute::class;
    }

    public function fromReflection(\ReflectionClass $subject): void
    {
        var_dump("REFLECTION CLASS");
        var_dump($subject);
    }
}

#[Attribute(Attribute::TARGET_METHOD)]
class methodAttribute implements FromReflectionMethod {

    public function fromReflection(\ReflectionMethod $subject): void
    {
        var_dump("REFLECTION METHOD");
        var_dump($subject);
    }
}

#[classAttribute]
abstract class parentClass {

    #[methodAttribute]
    public function doSomething() {
        return true;
    }
}

class childClass extends parentClass {

}

$analyzer = new Analyzer();
$tracedClass = $analyzer->analyze(childClass::class, classAttribute::class);

// Outputs:
// string(16) "REFLECTION CLASS"
// object(ReflectionClass)#3671 (1) {
// ["name"]=>
//   string(10) "childClass"
// }
// string(17) "REFLECTION METHOD"
// object(ReflectionMethod)#3711 (2) {
// ["name"]=>
//   string(11) "doSomething"
// ["class"]=>
//   string(11) "parentClass"
// }
Crell commented

Fascinating case. I'm not sure how to do that directly, but the just released 0.8.2 version includes a new ReadsClass interface that the method attribute could use. It gets passed the class attribute, which could itself use FromReflectionClass to know information about itself that can get passed on to the method attribute.

Can you give that a try and see if that works?

Actually this works, I did have to add a check because the interface doesn't allow me to be specific enough:

    public function fromClassAttribute(object $class): void
    {
        if(!$class instanceof classAttribute) {
            throw new Exception("Invalid usage of tracing attribute");
        }
        $this->className = $class->getName();
    }
Crell commented

Ah, excellent. (Unfortunately we cannot make the type more specific until PHP lets us use never as a parameter type.) Marking this done, then.