Hi

Am 2026-05-16 16:22, schrieb Daniel Scherzer:
[…]

Okay, fair enough. I can see how running code for additional validation can be useful and just the targets themselves are insufficient. This would also make sense to align the capabilities of internal code and userland code.

I would then suggest putting the extra validation where the existing validation is: Namely into the #[\Attribute] attribute. Closures and first class callables are supported in attributes since PHP 8.5 and would allow for a very straight-forward solution that keeps the attribute itself “pure metadata” in a value object.

    <?php

    #[Attribute]
    final class MyAttribute {
        public function __construct(
            public int $target,
            public Closure $validate,
        ) { }
    }

#[MyAttribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION, MyNoDiscard::validate(...))]
    final class MyNoDiscard
    {
        public readonly ?string $message;

        public function __construct(?string $message = null) { }

private static function validate(ReflectionMethod|ReflectionFunction $f) {
            if ($f instanceof ReflectionMethod) {
                if ($f->getName()[0] === '$') {
                    throw new Error('May not on property hooks');
                }
            }
        }
    }

    class X {
        public string $x {
            #[MyNoDiscard]
            get { return 'x'; }
        }
    }

    $r = new ReflectionClass(MyNoDiscard::class);
    $validator = $r->getAttributes()[0]->newInstance()->validate;

    $r = new ReflectionClass(X::class);
    $h = $r->getProperty('x')->getHook(PropertyHookType::Get);

    $validator($h); // Throws

Best regards
Tim Düsterhus

Reply via email to