Problem
On PHP 8.3+, weaving a class whose intercepted method comes from a trait and carries #[\Override] produces a proxy that cannot be loaded.
Reproduce (master 4e95fdd)
namespace Bug3;
class Base { public function hello(): string { return 'base'; } }
trait HelloTrait
{
#[\Override]
public function hello(): string { return 'trait'; }
}
class Child extends Base
{
use HelloTrait;
}
The aspect is #[Before('execution(public Bug3\Child->hello(*))')].
Generated proxy:
class Child extends \Bug3\Base implements \Go\Aop\Proxy
{
use ChildOriginalTrait {
ChildOriginalTrait::hello as private helloOriginalAlias;
}
// …
Result: Fatal error: Bug3\Child::helloOriginalAlias() has #[\Override] attribute, but no matching parent method exists (app/Classes.php:7).
Cause
- PHP copies a method's attributes to its trait alias, and
helloOriginalAlias overrides nothing.
- For methods declared in the class body,
WeavingTransformer::stripOverrideAttributeFromInterceptedMethods() removes #[\Override] from the woven source (see src/Instrument/AGENTS.md, "#[\Override] on intercepted methods").
- For a method that comes from a trait, it skips on purpose (
WeavingTransformer.php:634, declaring class ≠ class). The attribute lives in the trait's own file, which is shared and cannot be edited per class. The alias still inherits it.
Decision (for now)
Do not try to weave such methods.
- Detect the case: an intercepted method that comes from a trait and carries
#[\Override].
- Fail clearly: throw an exception with an actionable message. It should name the class, the method and the trait, and advise to exclude the method from the pointcut with
!matchInherited(). This follows the existing pattern for by-reference returns ("Exclude it from the pointcut with && !matchReturningByReference()").
- Document it: add the limitation and the workaround to
src/Instrument/AGENTS.md (and to the user-facing docs, if that section exists there).
A real fix (weaving the trait method without the copied attribute) can come later.
Acceptance
- Weaving fixture or test: the case above throws the documented exception with the
!matchInherited() hint.
- The same class with a pointcut that adds
&& !matchInherited() weaves and loads, and the method stays unintercepted.
- Methods declared in the class body with
#[\Override] keep working as today.
Problem
On PHP 8.3+, weaving a class whose intercepted method comes from a trait and carries
#[\Override]produces a proxy that cannot be loaded.Reproduce (master
4e95fdd)The aspect is
#[Before('execution(public Bug3\Child->hello(*))')].Generated proxy:
Result:
Fatal error: Bug3\Child::helloOriginalAlias() has #[\Override] attribute, but no matching parent method exists(app/Classes.php:7).Cause
helloOriginalAliasoverrides nothing.WeavingTransformer::stripOverrideAttributeFromInterceptedMethods()removes#[\Override]from the woven source (seesrc/Instrument/AGENTS.md, "#[\Override] on intercepted methods").WeavingTransformer.php:634, declaring class ≠ class). The attribute lives in the trait's own file, which is shared and cannot be edited per class. The alias still inherits it.Decision (for now)
Do not try to weave such methods.
#[\Override].!matchInherited(). This follows the existing pattern for by-reference returns ("Exclude it from the pointcut with&& !matchReturningByReference()").src/Instrument/AGENTS.md(and to the user-facing docs, if that section exists there).A real fix (weaving the trait method without the copied attribute) can come later.
Acceptance
!matchInherited()hint.&& !matchInherited()weaves and loads, and the method stays unintercepted.#[\Override]keep working as today.