Skip to content

refactor: replace the __aop__ prefix with OriginalTrait / OriginalAlias suffixes - #662

Merged
lisachenko merged 2 commits into
masterfrom
claude/trait-alias-naming-wh3xrh
Sep 4, 2026
Merged

refactor: replace the __aop__ prefix with OriginalTrait / OriginalAlias suffixes#662
lisachenko merged 2 commits into
masterfrom
claude/trait-alias-naming-wh3xrh

Conversation

@lisachenko

@lisachenko lisachenko commented Sep 4, 2026

Copy link
Copy Markdown
Member

Why

__aop__fooBar reads badly in generated code. Moving the marker to the end is much easier on the eye, and the same applies to the __AopProxied trait suffix. The two markers get distinct names so a trait-use block says which is which.

What changed

Before After
AspectContainer::AOP_PROXIED_SUFFIX = '__AopProxied' AspectContainer::AOP_PROXIED_SUFFIX = 'OriginalTrait'
trait Foo__AopProxied trait FooOriginalTrait
AbstractMethodInvocation::TRAIT_ALIAS_PREFIX = '__aop__' AbstractMethodInvocation::TRAIT_ALIAS_SUFFIX = 'OriginalAlias'
$this->__aop__doSomething(...) $this->doSomethingOriginalAlias(...)
$this->__aop____construct(...) $this->__constructOriginalAlias(...)
InitializationAware::__aop__initialization() InitializationAware::__initialization()
StaticInitializationAware::__aop__staticInitialization() StaticInitializationAware::__staticInitialization()

The constructor alias is composed from TRAIT_ALIAS_SUFFIX instead of being spelled out, so there is a single source of truth for it.

A generated proxy now looks like:

class Foo extends ParentClass implements \Go\Aop\Proxy
{
    use \Ns\FooOriginalTrait {
        \Ns\FooOriginalTrait::doSomething as private doSomethingOriginalAlias;
    }

    public function doSomething(): void
    {
        static $__joinPoint = InterceptorInjector::forMethod(
            self::class,
            'doSomething',
            [Interceptor::before(The::aspect(MonitorAspect::class)->beforeMethodExecution(...))],
            $this->doSomethingOriginalAlias(...),
        );
        $__joinPoint->__invoke($this, []);
    }
}

Anchoring the marker checks

The old __AopProxied was collision-proof, so the places that recognised it could get away with a loose str_contains(). An Original… marker is ordinary English and can appear in real class names, so each check is now anchored to where the marker can actually occur:

  • SourceTransformingLoader::saveToCache() — the cache file name is matched with str_ends_with(), and a woven source is recognised by its trait <Name>OriginalTrait declaration rather than by any mention of the word. Without this, a class such as OriginalTraitRegistry would have had its generated proxy and its woven body written to the same cache file, silently clobbering the proxy.
  • MagicConstantTransformer::resolveFileName() — only a trailing OriginalTrait.php is stripped, so OriginalTraitRegistry.php keeps its name.
  • DebugWeavingCommand — skips only files actually ending in OriginalTrait.php.
  • AdviceMatcher — the proxied parent is matched with str_ends_with() on the class name.

MetadataLoadInterceptor was already anchored with /.+(<suffix>)$/ and is unchanged.

Tests

Four new tests cover the anchoring:

  • SourceTransformingLoaderTest::testWovenBodyTraitIsCachedUnderTheProxiedSuffix()
  • SourceTransformingLoaderTest::testTransformedSourceMerelyMentioningTheSuffixKeepsItsCacheFileName()
  • MagicConstantTransformerTest::testTransformerDropsProxiedSuffixFromWovenBodyFileName()
  • MagicConstantTransformerTest::testTransformerKeepsFileNameThatOnlyContainsProxiedSuffix()

All proxy/weaving fixtures under tests/Instrument/Transformer/_files/ were regenerated with the new names, and the docs (README.md, CHANGELOG.md, docs/php85-limitations.md, the AGENTS.md files) were updated to match.

Verified locally: php-cs-fixer check clean, phpstan (level 10) clean, phpunit 2803 tests with only the 32 failures that already fail on the base commit in this container (the fixture-project functional tests, which shell out to bin/console cache:warmup:aop and fail there on pointcut parsing before and after this change — they pass in CI).

BC

A [BC BREAK] entry was added to the unreleased 4.0.0 changelog: TRAIT_ALIAS_PREFIX is gone, AOP_PROXIED_SUFFIX has a new value, the two *Aware interface methods are renamed, and caches generated by an earlier 4.0 development version must be regenerated.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CExUbcQgZTC1EmbWrTTTYS

…enerated code

Generated proxies read much better when the marker trails the name instead of
leading it, so every `__aop__`-prefixed symbol becomes an `Original`-suffixed
one:

* `AbstractMethodInvocation::TRAIT_ALIAS_PREFIX` becomes `TRAIT_ALIAS_SUFFIX`
  and the private trait aliases backing the original method bodies are now
  `doSomethingOriginal` instead of `__aop__doSomething` (the constructor alias
  is `__constructOriginal`, composed from the same constant).
* `AspectContainer::AOP_PROXIED_SUFFIX` becomes `Original`, so the trait
  holding the original class body is `FooOriginal` instead of
  `Foo__AopProxied`.
* The two remaining prefixed hooks drop it entirely: `InitializationAware` and
  `StaticInitializationAware` now declare `__initialization()` and
  `__staticInitialization()`.

`Original` is a far more common substring than `__AopProxied`, so the places
that sniffed for the marker are anchored to where it can actually appear: the
cache-file marker is matched at the end of the file name, the proxied parent at
the end of the class name, and a woven source is recognised by its
`trait <Name>Original` declaration rather than by any mention of the word.
Without this a class such as `OriginalRequest` would have had its proxy and its
woven body written to the same cache file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CExUbcQgZTC1EmbWrTTTYS
@codecov

codecov Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.01%. Comparing base (02b281b) to head (80b36ce).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##             master     #662   +/-   ##
=========================================
  Coverage     96.00%   96.01%           
- Complexity     1637     1638    +1     
=========================================
  Files            99       99           
  Lines          4510     4514    +4     
=========================================
+ Hits           4330     4334    +4     
  Misses          180      180           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

`Original` served both roles, which read ambiguously wherever the two appear
side by side in a trait-use block. Each marker now says what it is:

* `AspectContainer::AOP_PROXIED_SUFFIX` is `OriginalTrait`, so the trait
  holding the original class body is `FooOriginalTrait`.
* `AbstractMethodInvocation::TRAIT_ALIAS_SUFFIX` is `OriginalAlias`, so the
  private aliases backing the original method bodies are `fooBarOriginalAlias`
  (and `__constructOriginalAlias` for the constructor).

The `__initialization()` / `__staticInitialization()` hooks are unchanged, as
are the anchored marker checks — both markers stay recognisable only where they
can actually occur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CExUbcQgZTC1EmbWrTTTYS
@lisachenko lisachenko changed the title refactor: replace the __aop__ prefix with an Original suffix in generated code refactor: replace the __aop__ prefix with OriginalTrait / OriginalAlias suffixes Sep 4, 2026
@lisachenko
lisachenko marked this pull request as ready for review September 4, 2026 21:03
@lisachenko
lisachenko merged commit b8aeb26 into master Sep 4, 2026
12 checks passed
@lisachenko
lisachenko deleted the claude/trait-alias-naming-wh3xrh branch September 4, 2026 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants