Skip to content

Intercepting a method imported from a trait with #[\Override] fails: OriginalAlias "has #[\Override] attribute, but no matching parent method exists" #761

Description

@lisachenko

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions