diff --git a/CHANGELOG.md b/CHANGELOG.md index e58e5cd0..d8cf1bef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,15 @@ Changelog * [BC BREAK] Requires PHP 8.4+ * [Feature] **First-class callable advices** — the main way advices are now wired into woven code. Generated proxies declare each aspect-method advice as `Interceptor::before(The::aspect(MonitorAspect::class)->beforeMethodExecution(...))` — an eager first-class callable, since the interceptor list is only built while the intercepted method or hook is already executing (`LazyAdvisorAccessor` is removed). Compiled advisor cache files use the same facade in its lazy static-data form, e.g. `Interceptor::before(MonitorAspect::class, 'beforeMethodExecution')`, which returns a native PHP lazy proxy: interceptor construction, aspect resolution from the container and advice callable creation are all deferred until the advice is actually used, so cached advices whose pointcut never matches never instantiate their aspect. Advices registered in the container as plain closures (not aspect methods) are resolved through the new `The::advice('advisorId')` accessor, which unwraps `Advisor` and interceptor values down to the raw advice closure. * [BC BREAK] **Aspect advice methods must be public.** Because generated proxies call advices as first-class callables on the aspect instance, an advice method annotated with `#[Before]`, `#[After]`, `#[Around]` or `#[AfterThrowing]` can no longer be `protected` or `private` — the aspect loader now throws an `AspectException` for non-public advice methods. Methods holding only a `#[Pointcut]` attribute may keep any visibility. +* [BC BREAK] Generated code no longer uses the `__aop__` prefix. The trait holding the original class body is now suffixed with `OriginalTrait` (`FooOriginalTrait` instead of `Foo__AopProxied`) and the private trait aliases that back the original method bodies carry their own `OriginalAlias` suffix (`doSomethingOriginalAlias` instead of `__aop__doSomething`). Accordingly `AbstractMethodInvocation::TRAIT_ALIAS_PREFIX` is replaced by `AbstractMethodInvocation::TRAIT_ALIAS_SUFFIX` (`'OriginalAlias'`), `AspectContainer::AOP_PROXIED_SUFFIX` is now `'OriginalTrait'`, and the hooks declared by `InitializationAware` and `StaticInitializationAware` are renamed to `__initialization()` and `__staticInitialization()`. Cached proxies generated by an earlier 4.0 development version must be regenerated. * [BC BREAK] Removed the `AdviceBefore`, `AdviceAfter` and `AdviceAround` marker interfaces. The `Advice` interface now requires `getType(): AdviceTypeEnum`, and the new `AdviceTypeEnum` backed enum (`Before`, `After`, `AfterThrowing`, `Around`, `Introduction`) carries both the advice kind and its invocation priority used for joinpoint sorting. -* [BC BREAK] Proxy engine switched from inheritance-based to **trait-based**: the original class body is converted to a PHP trait (`Foo__AopProxied`) and the proxy class uses it via `use` with private method aliases instead of extending the renamed class. This removes the `__AopProxied` parent from the inheritance chain. -* [BC BREAK] All invocation class constructors (`DynamicTraitAliasMethodInvocation`, `StaticTraitAliasMethodInvocation`, `ReflectionFunctionInvocation`) now require a `Closure $closureToCall` parameter (non-nullable). Generated proxy code always passes a first-class callable: `$this->__aop__method(...)` for own instance methods, `self::__aop__method(...)` for own static methods, `parent::method(...)` for inherited methods, and `\functionName(...)` for functions. +* [BC BREAK] Proxy engine switched from inheritance-based to **trait-based**: the original class body is converted to a PHP trait (`FooOriginalTrait`) and the proxy class uses it via `use` with private method aliases instead of extending the renamed class. This removes the renamed `OriginalTrait` parent from the inheritance chain. +* [BC BREAK] All invocation class constructors (`DynamicTraitAliasMethodInvocation`, `StaticTraitAliasMethodInvocation`, `ReflectionFunctionInvocation`) now require a `Closure $closureToCall` parameter (non-nullable). Generated proxy code always passes a first-class callable: `$this->methodOriginalAlias(...)` for own instance methods, `self::methodOriginalAlias(...)` for own static methods, `parent::method(...)` for inherited methods, and `\functionName(...)` for functions. * [Feature] **Private method interception** — both dynamic (`private function foo()`) and static (`private static function bar()`) private methods can now be intercepted by aspects. This was impossible with the old extend-based engine because PHP does not allow overriding private methods in subclasses. * [Feature] **PHP 8.5+ first-class callable default values** — proxy generation for method parameters and constructor-promoted properties now supports `Closure` default values expressed via first-class callable syntax (e.g., `function foo($cb = strlen(...))`). The raw AST expression node is preserved through the proxy-generation pipeline so the generated source code reproduces the original FCC default verbatim. -* [Feature] **PHP 8.1+ enum interception** — instance and static methods on both unit (pure) and backed enums can now be intercepted by aspects. The enum body is extracted into a trait (`Foo__AopProxied`); a proxy enum re-declares the cases and dispatches intercepted methods via per-method `static $__joinPoint` caching. Built-in enum methods (`cases`, `from`, `tryFrom`) and initialization joinpoints are never woven. +* [Feature] **PHP 8.1+ enum interception** — instance and static methods on both unit (pure) and backed enums can now be intercepted by aspects. The enum body is extracted into a trait (`FooOriginalTrait`); a proxy enum re-declares the cases and dispatches intercepted methods via per-method `static $__joinPoint` caching. Built-in enum methods (`cases`, `from`, `tryFrom`) and initialization joinpoints are never woven. * [Feature] `self::` in proxied classes now resolves to the proxy class naturally (via PHP trait semantics), removing the need for `SelfValueTransformer`. -* [Feature] **First-class callable syntax** — generated proxy code and invocation constructors use PHP 8.1+ first-class callable syntax (`$this->__aop__method(...)`, `parent::method(...)`, `\func(...)`) to reference original method and function bodies, eliminating the need for `Closure::bind` at construction time. +* [Feature] **First-class callable syntax** — generated proxy code and invocation constructors use PHP 8.1+ first-class callable syntax (`$this->methodOriginalAlias(...)`, `parent::method(...)`, `\func(...)`) to reference original method and function bodies, eliminating the need for `Closure::bind` at construction time. * [BC BREAK] Removed `Features::PARAMETER_WIDENING` and the parameter-widening code path in the proxy generators. The feature was a PHP 7.0/7.1 compatibility aid (parameter type widening, wiki.php.net/rfc/parameter-no-type-variance) that has been a no-op concern since the PHP 7.2 baseline: generated proxies always keep the original parameter types. Remove the flag from `AspectKernel::configureAop()` options if you passed it. * [BC BREAK] Removed DeclareError support, including the `DeclareError` attribute, `DeclareErrorInterceptor`, and `PointcutBuilder::declareError()`. Use `Before` or `Around` interceptors to emit user warnings or throw exceptions instead. * [BC BREAK] Removed support for the "dynamic" pointcut (`dynamic(public Foo->method*(*))`), including `MagicMethodDynamicPointcut`, `DynamicInvocationMatcherInterceptor`, the `Pointcut::KIND_DYNAMIC` constant and the `$instanceOrScope`/`$arguments` parameters of `Pointcut::matches()`. Use a traditional execution pointcut for the magic methods instead, e.g. `execution(public Foo->__call(*))` or `execution(public Foo::__callStatic(*))`, and check the invoked method name from `$invocation->getArguments()[0]` inside the advice. diff --git a/README.md b/README.md index ff3ce745..08cec1a3 100644 --- a/README.md +++ b/README.md @@ -285,7 +285,7 @@ static $__joinPoint = InterceptorInjector::forMethod( [ Interceptor::before(The::aspect(MonitorAspect::class)->beforeMethodExecution(...)), ], - $this->__aop__doSomething(...), + $this->doSomethingOriginalAlias(...), ); ``` diff --git a/docs/php85-limitations.md b/docs/php85-limitations.md index 88ee3f4d..c73ba157 100644 --- a/docs/php85-limitations.md +++ b/docs/php85-limitations.md @@ -12,7 +12,7 @@ Several of the limitations below are actively being worked on — they are phras ### Pipe operator `|>` The [pipe operator](https://wiki.php.net/rfc/pipe-operator-v3) works inside woven method bodies. -Method bodies are moved verbatim into the `__AopProxied` trait, so any PHP 8.5 expression syntax +Method bodies are moved verbatim into the `OriginalTrait` body trait, so any PHP 8.5 expression syntax inside them is preserved. ### `clone with` diff --git a/phpstan-baseline-tests.php b/phpstan-baseline-tests.php index 39848f74..5507b13f 100644 --- a/phpstan-baseline-tests.php +++ b/phpstan-baseline-tests.php @@ -6,7 +6,7 @@ $ignoreErrors = []; // MetadataLoadInterceptorTest deliberately builds Doctrine ClassMetadata for -// fake "__AopProxied" class names with intentionally malformed mapping data, +// fake "OriginalTrait" class names with intentionally malformed mapping data, // and then asserts that MetadataLoadInterceptor::loadClassMetadata() reset it // at runtime. Static analysis can neither accept the fake class-strings nor // see the interceptor's mutations, so every finding below is a false positive @@ -18,31 +18,31 @@ 'path' => __DIR__ . '/tests/Aop/Bridge/Doctrine/MetadataLoadInterceptorTest.php', ]; $ignoreErrors[] = [ - 'message' => '#^Parameter \\#1 \\$name of class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata constructor expects class\\-string\\<__AopProxied\\\\Some\\\\Class\\\\Name\\>, string given\\.$#', + 'message' => '#^Parameter \\#1 \\$name of class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata constructor expects class\\-string\\, string given\\.$#', 'identifier' => 'argument.type', 'count' => 1, 'path' => __DIR__ . '/tests/Aop/Bridge/Doctrine/MetadataLoadInterceptorTest.php', ]; $ignoreErrors[] = [ - 'message' => '#^Parameter \\#1 \\$name of class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata constructor expects class\\-string\\<__AopProxied\\>, string given\\.$#', + 'message' => '#^Parameter \\#1 \\$name of class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata constructor expects class\\-string\\, string given\\.$#', 'identifier' => 'argument.type', 'count' => 1, 'path' => __DIR__ . '/tests/Aop/Bridge/Doctrine/MetadataLoadInterceptorTest.php', ]; $ignoreErrors[] = [ - 'message' => '#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\\\:\\:\\$table \\(array\\{name\\: string, schema\\?\\: string, indexes\\?\\: array, uniqueConstraints\\?\\: array, options\\?\\: array\\, quoted\\?\\: bool\\}\\) does not accept array\\{\'table_name\'\\}\\.$#', + 'message' => '#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\\\:\\:\\$table \\(array\\{name\\: string, schema\\?\\: string, indexes\\?\\: array, uniqueConstraints\\?\\: array, options\\?\\: array\\, quoted\\?\\: bool\\}\\) does not accept array\\{\'table_name\'\\}\\.$#', 'identifier' => 'assign.propertyType', 'count' => 1, 'path' => __DIR__ . '/tests/Aop/Bridge/Doctrine/MetadataLoadInterceptorTest.php', ]; $ignoreErrors[] = [ - 'message' => '#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\\\:\\:\\$customRepositoryClassName \\(class\\-string\\\\|null\\) does not accept string\\.$#', + 'message' => '#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\\\:\\:\\$customRepositoryClassName \\(class\\-string\\\\|null\\) does not accept string\\.$#', 'identifier' => 'assign.propertyType', 'count' => 1, 'path' => __DIR__ . '/tests/Aop/Bridge/Doctrine/MetadataLoadInterceptorTest.php', ]; $ignoreErrors[] = [ - 'message' => '#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\\\:\\:\\$fieldMappings \\(array\\\\) does not accept array\\\\|Doctrine\\\\ORM\\\\Mapping\\\\FieldMapping\\>\\.$#', + 'message' => '#^Property Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata\\\\:\\:\\$fieldMappings \\(array\\\\) does not accept array\\\\|Doctrine\\\\ORM\\\\Mapping\\\\FieldMapping\\>\\.$#', 'identifier' => 'assign.propertyType', 'count' => 1, 'path' => __DIR__ . '/tests/Aop/Bridge/Doctrine/MetadataLoadInterceptorTest.php', diff --git a/src/Aop/AGENTS.md b/src/Aop/AGENTS.md index 6845cc68..7d38293e 100644 --- a/src/Aop/AGENTS.md +++ b/src/Aop/AGENTS.md @@ -27,8 +27,8 @@ Proxy generators use TypeGenerator::renderTypeForPhpDoc() to emit V as 2nd gener ## Implementations (src/Aop/Framework/) | Class | Implements | Key behavior | |-----------------------------------|-------------------------|------------------------------------------------------------------------------------------------------------------------| -| AbstractMethodInvocation | MethodInvocation | Base; protected readonly Closure $closureToCall (FCC); TRAIT_ALIAS_PREFIX='__aop__'; keeps method reflection | -| DynamicTraitAliasMethodInvocation | DynamicMethodInvocation | receives $this->__aop__m(...) or parent::m(...); proceed() via ReflectionMethod::invokeArgs (handles by-ref correctly) | +| AbstractMethodInvocation | MethodInvocation | Base; protected readonly Closure $closureToCall (FCC); TRAIT_ALIAS_SUFFIX='OriginalAlias'; keeps method reflection | +| DynamicTraitAliasMethodInvocation | DynamicMethodInvocation | receives $this->mOriginalAlias(...) or parent::m(...); proceed() via ReflectionMethod::invokeArgs (handles by-ref correctly) | | StaticTraitAliasMethodInvocation | StaticMethodInvocation | FCC shim: static fn(array $args) => forward_static_call_array(...); bindTo(null, $scope) per call | | ReflectionConstructorInvocation | ConstructorInvocation | newInstanceWithoutConstructor() then call constructor (requires INTERCEPT_INITIALIZATIONS feature) | | ReflectionFunctionInvocation | FunctionInvocation | receives FCC to global fn (e.g. \strlen(...) with leading \ to avoid recursive proxy call) | diff --git a/src/Aop/Framework/AbstractMethodInvocation.php b/src/Aop/Framework/AbstractMethodInvocation.php index 084de970..988d1598 100644 --- a/src/Aop/Framework/AbstractMethodInvocation.php +++ b/src/Aop/Framework/AbstractMethodInvocation.php @@ -27,10 +27,10 @@ abstract class AbstractMethodInvocation extends AbstractInvocation implements MethodInvocation { /** - * Prefix used for trait method aliases that back the original method body. - * The proxy class aliases each intercepted method as `private __aop__` in the trait-use block. + * Suffix used for trait method aliases that back the original method body. + * The proxy class aliases each intercepted method as `private OriginalAlias` in the trait-use block. */ - public const string TRAIT_ALIAS_PREFIX = '__aop__'; + public const string TRAIT_ALIAS_SUFFIX = 'OriginalAlias'; protected readonly ReflectionMethod $reflectionMethod; diff --git a/src/Aop/Framework/DynamicTraitAliasMethodInvocation.php b/src/Aop/Framework/DynamicTraitAliasMethodInvocation.php index 75c09d45..4cbefd7e 100644 --- a/src/Aop/Framework/DynamicTraitAliasMethodInvocation.php +++ b/src/Aop/Framework/DynamicTraitAliasMethodInvocation.php @@ -22,7 +22,7 @@ * Dynamic trait-alias method invocation calls instance methods via reflection. * * The callable is provided by the generated proxy code and points to the original method body: - * - For methods declared in the proxied class: `$this->__aop__(...)` — the private + * - For methods declared in the proxied class: `$this->OriginalAlias(...)` — the private * alias created in the proxy's trait-use block. * - For inherited methods (no trait alias): `parent::(...)`. * @@ -54,7 +54,7 @@ final class DynamicTraitAliasMethodInvocation extends AbstractMethodInvocation i /** * ReflectionMethod pointing to the original method body: - * - For methods with a trait alias: the private `__aop__` alias. + * - For methods with a trait alias: the private `OriginalAlias` alias. * - For inherited methods without a trait alias: the prototype method from the parent class. */ private readonly ReflectionMethod $originalMethodToCall; @@ -64,7 +64,7 @@ final class DynamicTraitAliasMethodInvocation extends AbstractMethodInvocation i * @param class-string $className Class, containing method to invoke * @param non-empty-string $methodName Name of the method to invoke * @param Closure $closureToCall First-class callable to the original method body, - * e.g. `$this->__aop__method(...)` for trait-aliased + * e.g. `$this->methodOriginalAlias(...)` for trait-aliased * methods or `parent::method(...)` for inherited ones. */ public function __construct(array $advices, string $className, string $methodName, Closure $closureToCall) diff --git a/src/Aop/Framework/InterceptorInjector.php b/src/Aop/Framework/InterceptorInjector.php index db980f32..2e8650be 100644 --- a/src/Aop/Framework/InterceptorInjector.php +++ b/src/Aop/Framework/InterceptorInjector.php @@ -32,7 +32,7 @@ final class InterceptorInjector * @param non-empty-string $methodName * @param non-empty-list $interceptors * @param Closure $closureToCall First-class callable to the original method body, - * e.g. `$this->__aop__method(...)` for trait-aliased methods or + * e.g. `$this->methodOriginalAlias(...)` for trait-aliased methods or * `parent::method(...)` for inherited methods. * @return DynamicMethodInvocation */ @@ -52,7 +52,7 @@ public static function forMethod(string $className, string $methodName, array $i * @param non-empty-string $methodName * @param non-empty-list $interceptors * @param Closure $closureToCall First-class callable to the original static method body, - * e.g. `self::__aop__method(...)` for trait-aliased methods or + * e.g. `self::methodOriginalAlias(...)` for trait-aliased methods or * `parent::method(...)` for inherited methods. * @return StaticMethodInvocation */ diff --git a/src/Aop/Framework/StaticTraitAliasMethodInvocation.php b/src/Aop/Framework/StaticTraitAliasMethodInvocation.php index 28bb7d0f..6099715c 100644 --- a/src/Aop/Framework/StaticTraitAliasMethodInvocation.php +++ b/src/Aop/Framework/StaticTraitAliasMethodInvocation.php @@ -21,7 +21,7 @@ * that is rebound to each caller's late-static-binding scope on invocation. * * The callable is provided by the generated proxy code and points to the original method body: - * - For methods declared in the proxied class: `self::__aop__(...)` — the private + * - For methods declared in the proxied class: `self::OriginalAlias(...)` — the private * alias created in the proxy's trait-use block. * - For inherited methods (no trait alias): `parent::(...)`. * @@ -57,7 +57,7 @@ final class StaticTraitAliasMethodInvocation extends AbstractMethodInvocation im * @param class-string $className Class, containing method to invoke * @param non-empty-string $methodName Name of the method to invoke * @param Closure $closureToCall First-class callable to the original static method body, - * e.g. `self::__aop__method(...)` or `parent::method(...)`. + * e.g. `self::methodOriginalAlias(...)` or `parent::method(...)`. */ public function __construct(array $advices, string $className, string $methodName, Closure $closureToCall) { diff --git a/src/Aop/InitializationAware.php b/src/Aop/InitializationAware.php index 4399c6cd..8dedd3c6 100644 --- a/src/Aop/InitializationAware.php +++ b/src/Aop/InitializationAware.php @@ -23,5 +23,5 @@ interface InitializationAware * @param list $arguments * @return T */ - public static function __aop__initialization(array $arguments = []): object; + public static function __initialization(array $arguments = []): object; } diff --git a/src/Aop/StaticInitializationAware.php b/src/Aop/StaticInitializationAware.php index e508f132..b4454ab5 100644 --- a/src/Aop/StaticInitializationAware.php +++ b/src/Aop/StaticInitializationAware.php @@ -17,5 +17,5 @@ */ interface StaticInitializationAware { - public static function __aop__staticInitialization(): void; + public static function __staticInitialization(): void; } diff --git a/src/Console/Command/DebugWeavingCommand.php b/src/Console/Command/DebugWeavingCommand.php index 66038625..4153085a 100644 --- a/src/Console/Command/DebugWeavingCommand.php +++ b/src/Console/Command/DebugWeavingCommand.php @@ -113,10 +113,10 @@ private function getProxies(CachePathManager $cachePathManager): array continue; } $pathname = $splFileInfo->getPathname(); - if (str_contains($pathname, AspectContainer::AOP_PROXIED_SUFFIX)) { + if (str_ends_with($pathname, AspectContainer::AOP_PROXIED_SUFFIX . '.php')) { continue; } - // Only collect proxy files: they have a sibling __AopProxied trait file + // Only collect proxy files: they have a sibling `OriginalTrait.php` file $traitSibling = str_replace('.php', AspectContainer::AOP_PROXIED_SUFFIX . '.php', $pathname); if (!file_exists($traitSibling)) { continue; diff --git a/src/Core/AdviceMatcher.php b/src/Core/AdviceMatcher.php index ccb34de9..89810856 100644 --- a/src/Core/AdviceMatcher.php +++ b/src/Core/AdviceMatcher.php @@ -84,7 +84,7 @@ public function getAdvicesForClass(ReflectionClass $class, array $advisors): arr $parentClass = $class->getParentClass(); $originalClass = $class; - if ($parentClass && str_contains($parentClass->name, AspectContainer::AOP_PROXIED_SUFFIX)) { + if ($parentClass && str_ends_with($parentClass->name, AspectContainer::AOP_PROXIED_SUFFIX)) { $originalClass = $parentClass; } @@ -166,7 +166,7 @@ private function getClassLevelAdvicesFromAdvisor( // Check methods in class only for method filters if (($pointcutKind & Pointcut::KIND_METHOD) !== 0) { // Private methods are supported by the trait-based proxy engine: the original private method - // body lives in the trait and is aliased as __aop__; the proxy overrides it with the + // body lives in the trait and is aliased as OriginalAlias; the proxy overrides it with the // same private visibility so the join-point chain is invoked on every in-class call. $mask = ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED | ReflectionMethod::IS_PRIVATE; foreach ($class->getMethods($mask) as $method) { diff --git a/src/Core/AspectContainer.php b/src/Core/AspectContainer.php index 49838607..b7b1c9ef 100644 --- a/src/Core/AspectContainer.php +++ b/src/Core/AspectContainer.php @@ -63,7 +63,7 @@ interface AspectContainer /** * Suffix, that will be added to all proxied class names */ - public const string AOP_PROXIED_SUFFIX = '__AopProxied'; + public const string AOP_PROXIED_SUFFIX = 'OriginalTrait'; /** * Returns a service from the container. diff --git a/src/Instrument/AGENTS.md b/src/Instrument/AGENTS.md index 15b1b2ab..aacfc324 100644 --- a/src/Instrument/AGENTS.md +++ b/src/Instrument/AGENTS.md @@ -19,7 +19,7 @@ WeavingTransformer converts original class to trait + proxy class. Two generated ### Woven file (replaces original in php stream filtering) ```php -trait Foo__AopProxied { /* original methods verbatim */ } +trait FooOriginalTrait { /* original methods verbatim */ } include_once AOP_CACHE_DIR . '/Foo.php'; ``` @@ -27,8 +27,8 @@ include_once AOP_CACHE_DIR . '/Foo.php'; ```php class Foo extends OriginalParent implements OriginalInterfaces, \Go\Aop\Proxy { - use \Ns\Foo__AopProxied { - \Ns\Foo__AopProxied::interceptedMethod as private __aop__interceptedMethod; + use \Ns\FooOriginalTrait { + \Ns\FooOriginalTrait::interceptedMethod as private interceptedMethodOriginalAlias; } public function interceptedMethod(ArgType $arg): ReturnType { /** @var \Go\Aop\Intercept\DynamicMethodInvocation $__joinPoint */ @@ -38,7 +38,7 @@ class Foo extends OriginalParent implements OriginalInterfaces, \Go\Aop\Proxy [ Interceptor::before(The::aspect(SomeAspect::class)->adviceMethod(...)), ], - $this->__aop__interceptedMethod(...), + $this->interceptedMethodOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$arg]); } @@ -54,8 +54,8 @@ GeneratedInterceptor descriptors (string advisor ids are rejected). - self:: in trait body → proxy class (no rewrite needed) - Private methods interceptable (impossible with old extend-based engine) - FCC 4th arg to InterceptorInjector: - - `$this->__aop__m(...)` — own dynamic methods - - `self::__aop__m(...)` — own static methods + - `$this->mOriginalAlias(...)` — own dynamic methods + - `self::mOriginalAlias(...)` — own static methods - `parent::m(...)` — inherited methods (no trait alias) - `\fn(...)` — function proxies diff --git a/src/Instrument/ClassLoading/SourceTransformingLoader.php b/src/Instrument/ClassLoading/SourceTransformingLoader.php index addee3e5..1b28d67e 100644 --- a/src/Instrument/ClassLoading/SourceTransformingLoader.php +++ b/src/Instrument/ClassLoading/SourceTransformingLoader.php @@ -287,8 +287,13 @@ private static function saveToCache( } if ($result === TransformerResultEnum::RESULT_TRANSFORMED) { - if (!str_contains($cacheUri, AspectContainer::AOP_PROXIED_SUFFIX) - && str_contains($transformedSource, AspectContainer::AOP_PROXIED_SUFFIX) + // A woven source carries the original class body as a `trait OriginalTrait` + // declaration and is cached next to the generated proxy, under the same marker. + // Both checks are anchored so that a class merely named `...OriginalTrait` is not + // mistaken for a woven body. + $originalBodyTrait = '/\btrait\s+\w+' . preg_quote(AspectContainer::AOP_PROXIED_SUFFIX, '/') . '\b/'; + if (!str_ends_with($cacheUri, AspectContainer::AOP_PROXIED_SUFFIX . '.php') + && preg_match($originalBodyTrait, $transformedSource) === 1 ) { $cacheUri = str_replace('.php', AspectContainer::AOP_PROXIED_SUFFIX . '.php', $cacheUri); } diff --git a/src/Instrument/Transformer/ConstructorExecutionTransformer.php b/src/Instrument/Transformer/ConstructorExecutionTransformer.php index eef1f497..2d823457 100644 --- a/src/Instrument/Transformer/ConstructorExecutionTransformer.php +++ b/src/Instrument/Transformer/ConstructorExecutionTransformer.php @@ -130,7 +130,7 @@ protected static function construct(string $fullClassName, array $arguments = [] if (is_subclass_of($fullClassName, InitializationAware::class)) { /** @var class-string> $fullClassName */ - return $fullClassName::__aop__initialization($arguments); + return $fullClassName::__initialization($arguments); } $cachedInvocation = self::$constructorInvocationsCache[$fullClassName]; diff --git a/src/Instrument/Transformer/MagicConstantTransformer.php b/src/Instrument/Transformer/MagicConstantTransformer.php index c48cf57b..76a833b4 100644 --- a/src/Instrument/Transformer/MagicConstantTransformer.php +++ b/src/Instrument/Transformer/MagicConstantTransformer.php @@ -87,7 +87,12 @@ public static function resolveFileName(string $fileName): string } if (self::$rewriteToPath !== '' && str_starts_with($fileName, self::$rewriteToPath)) { $fileName = str_replace(self::$rewriteToPath, self::$rootPath, $fileName); - $fileName = str_replace(AspectContainer::AOP_PROXIED_SUFFIX . '.php', '.php', $fileName); + // Only the trailing marker of the woven-body file is dropped, so a class that simply + // carries the suffix word in its own name (e.g. `OriginalTraitRegistry.php`) keeps its name + $proxiedFileSuffix = AspectContainer::AOP_PROXIED_SUFFIX . '.php'; + if (str_ends_with($fileName, $proxiedFileSuffix)) { + $fileName = substr($fileName, 0, -strlen($proxiedFileSuffix)) . '.php'; + } } return $fileName; diff --git a/src/Instrument/Transformer/WeavingTransformer.php b/src/Instrument/Transformer/WeavingTransformer.php index fd934b1b..7b821293 100644 --- a/src/Instrument/Transformer/WeavingTransformer.php +++ b/src/Instrument/Transformer/WeavingTransformer.php @@ -247,7 +247,7 @@ private function getPositionAfterAttributeGroups(ClassLike $classNode): ?int * Performs the following token-stream modifications in-place: * - Removes 'final' and 'abstract' modifiers from the class keyword * - Changes 'class' keyword text to 'trait' - * - Renames the class to $newClassName (__AopProxied suffix) + * - Renames the class to $newClassName (OriginalTrait suffix) * - Removes the 'extends X' and 'implements Y, Z' clauses (moved to the proxy class) * * @param array>> $advices List of class advices @@ -321,7 +321,7 @@ private function convertClassToTrait( } while (true); // Strip #[\Override] from intercepted methods. - // PHP copies attributes to alias names (e.g. __aop__foo). Since __aop__foo has no parent + // PHP copies attributes to alias names (e.g. fooOriginalAlias). Since fooOriginalAlias has no parent // match, PHP would raise a fatal error if #[\Override] were present on the alias. $this->commentOutInterceptedPropertiesInTraitBody($class, $advices, $streamMetaData); $this->stripOverrideAttributeFromInterceptedMethods($class, $advices, $streamMetaData); @@ -445,7 +445,7 @@ private function removeAdjacentAttributeComma(int $start, int $end, StreamMetaDa * * Performs the following token-stream modifications in-place: * - Changes 'enum' keyword text to 'trait' - * - Renames the enum to $newClassName (__AopProxied suffix) + * - Renames the enum to $newClassName (OriginalTrait suffix) * - Removes the backed type (': string' / ': int') and any 'implements ...' clause * - Removes all enum case declarations from the body (cases live in the proxy enum instead) * @@ -524,7 +524,7 @@ private function convertEnumToTrait( } // Strip #[\Override] from intercepted methods to prevent fatal errors on the alias. - // PHP copies attributes to alias names (e.g. __aop__label), and since __aop__label has + // PHP copies attributes to alias names (e.g. labelOriginalAlias), and since labelOriginalAlias has // no matching parent method, #[\Override] on the alias would be a fatal error. $this->stripOverrideAttributeFromInterceptedMethods($class, $advices, $streamMetaData); } @@ -533,7 +533,7 @@ private function convertEnumToTrait( * Removes #[\Override] attribute groups from all intercepted methods in the token stream. * * When a class method is aliased in the proxy trait-use block (e.g. - * `SomeTrait::method as private __aop__method`), PHP copies the method's attributes to + * `SomeTrait::method as private methodOriginalAlias`), PHP copies the method's attributes to * the alias. If the original method had `#[\Override]`, the alias name has no matching * parent method → fatal error. We strip the attribute only from methods that will be aliased * (those with dynamic or static method advices). diff --git a/src/Proxy/AGENTS.md b/src/Proxy/AGENTS.md index d4f76602..b5dea958 100644 --- a/src/Proxy/AGENTS.md +++ b/src/Proxy/AGENTS.md @@ -2,7 +2,7 @@ ## Proxy generators - ClassProxyGenerator — trait-based proxy for regular classes - - Constructor takes $traitName (Foo__AopProxied FQCN) as 2nd arg + - Constructor takes $traitName (FooOriginalTrait FQCN) as 2nd arg - Always emits `use $traitName` (even for introduction-only aspects) - FunctionProxyGenerator — function wrappers - TraitProxyGenerator — trait proxies @@ -11,7 +11,7 @@ ## Proxy parts (src/Proxy/Part/) - InterceptedMethodGenerator — wraps a method with join-point dispatch - InterceptedConstructorGenerator — wraps constructor - - Calls `$this->__aop____construct()` when constructor is in trait, `parent::__construct()` otherwise + - Calls `$this->__constructOriginalAlias()` when constructor is in trait, `parent::__construct()` otherwise - InterceptedPropertyGenerator — re-declares properties with native get/set hooks → ClassFieldAccess ## Generators (src/Proxy/Generator/) diff --git a/src/Proxy/ClassProxyGenerator.php b/src/Proxy/ClassProxyGenerator.php index 79a04a5f..b89b0758 100644 --- a/src/Proxy/ClassProxyGenerator.php +++ b/src/Proxy/ClassProxyGenerator.php @@ -69,11 +69,11 @@ class ClassProxyGenerator * * The original class has been converted to a trait named $traitName by WeavingTransformer. * The proxy class re-exposes the same name, parent, and interfaces as the original, uses - * that trait, and aliases each intercepted method as `private __aop__` so the + * that trait, and aliases each intercepted method as `private OriginalAlias` so the * overriding method body can delegate to the original via a Closure::bind proceed closure. * * @param ReflectionClass $originalClass Original class reflection (before transformation) - * @param string $traitName FQCN of the generated trait (e.g. Ns\Foo__AopProxied) + * @param string $traitName FQCN of the generated trait (e.g. Ns\FooOriginalTrait) * @param array>> $classAdviceNames List of advices for class */ public function __construct( @@ -178,14 +178,14 @@ public function __construct( // explicit addTraits call only matters when $interceptedMethods is empty. $classGenerator->addTraits([$effectiveTraitName]); - // Alias each intercepted method as private __aop__ + // Alias each intercepted method as private OriginalAlias foreach ($interceptedMethods as $methodName) { $reflectionMethod = $originalClass->getMethod($methodName); if ($reflectionMethod->class !== $originalClass->name) { continue; } - $classGenerator->addTraitAlias($effectiveTraitName, $methodName, AbstractMethodInvocation::TRAIT_ALIAS_PREFIX . $methodName, Visibility::PRIVATE); + $classGenerator->addTraitAlias($effectiveTraitName, $methodName, $methodName . AbstractMethodInvocation::TRAIT_ALIAS_SUFFIX, Visibility::PRIVATE); } // Add any AOP-introduced traits $classGenerator->addTraits($introducedTraits); @@ -241,7 +241,7 @@ public function generate(): string $staticInitializationAdvices = $this->adviceNames[AspectContainer::STATIC_INIT_PREFIX]['root'] ?? []; if ($staticInitializationAdvices !== []) { - $classCode .= "\n" . $this->generator->name . '::__aop__staticInitialization();'; + $classCode .= "\n" . $this->generator->name . '::__staticInitialization();'; } return $classCode; @@ -346,7 +346,7 @@ protected function getJoinpointInvocationBody(ReflectionMethod $method, ?Reflect // Determine the first-class callable expression for the original method. // - // Methods declared in the proxied class have a private `__aop__` alias in the + // Methods declared in the proxied class have a private `OriginalAlias` alias in the // proxy's trait-use block. These first-class callables are rebound per-call. // // Inherited methods have no such alias. For static calls, `parent::method(...)` is used @@ -357,8 +357,8 @@ protected function getJoinpointInvocationBody(ReflectionMethod $method, ?Reflect $hasTraitAlias = $originalClass !== null && ($method->class === $originalClass->name); if ($hasTraitAlias) { $callableExpression = $isStatic - ? 'self::' . AbstractMethodInvocation::TRAIT_ALIAS_PREFIX . $method->name . '(...)' - : '$this->' . AbstractMethodInvocation::TRAIT_ALIAS_PREFIX . $method->name . '(...)'; + ? 'self::' . $method->name . AbstractMethodInvocation::TRAIT_ALIAS_SUFFIX . '(...)' + : '$this->' . $method->name . AbstractMethodInvocation::TRAIT_ALIAS_SUFFIX . '(...)'; } else { // Inherited method (no trait alias): use parent:: first-class callable for both static and dynamic. // DynamicTraitAliasMethodInvocation uses ReflectionMethod internally, so the callable is stored @@ -388,7 +388,7 @@ private function createStaticInitializationMethod(array $advisorNames): MethodGe { $advicesCode = (new InterceptorListGenerator($advisorNames))->generate(); - $method = new MethodGenerator('__aop__staticInitialization'); + $method = new MethodGenerator('__staticInitialization'); $method->static = true; $method->returnType = 'void'; $method->body = <<generate(); - $method = new MethodGenerator('__aop__initialization'); + $method = new MethodGenerator('__initialization'); $method->static = true; $method->returnType = 'static'; $argumentsParameter = new ParameterGenerator( diff --git a/src/Proxy/EnumProxyGenerator.php b/src/Proxy/EnumProxyGenerator.php index 90af484b..153d0bf0 100644 --- a/src/Proxy/EnumProxyGenerator.php +++ b/src/Proxy/EnumProxyGenerator.php @@ -49,7 +49,7 @@ * enum declaration that: * - Declares the same backed type (string|int) or is a pure unit enum * - Implements the same interfaces as the original plus \Go\Aop\Proxy - * - Uses the trait and aliases each intercepted method as `private __aop__` + * - Uses the trait and aliases each intercepted method as `private OriginalAlias` * - Re-declares all enum cases (they cannot live in traits) * - Overrides each intercepted method with per-method lazy joinpoint dispatch */ @@ -80,7 +80,7 @@ class EnumProxyGenerator extends ClassProxyGenerator * API for each case. * * @param ReflectionClass $originalClass Original enum reflection (before transformation) - * @param string $traitName FQCN of the generated trait (e.g. Ns\Foo__AopProxied) + * @param string $traitName FQCN of the generated trait (e.g. Ns\FooOriginalTrait) * @param array>> $classAdviceNames List of advices for enum */ public function __construct( @@ -154,12 +154,12 @@ public function __construct( // Always include the original enum body trait $enumGenerator->addTraits([$effectiveTraitName]); - // Alias each intercepted method as private __aop__ + // Alias each intercepted method as private OriginalAlias foreach ($interceptedMethods as $methodName) { $enumGenerator->addTraitAlias( $effectiveTraitName, $methodName, - AbstractMethodInvocation::TRAIT_ALIAS_PREFIX . $methodName, + $methodName . AbstractMethodInvocation::TRAIT_ALIAS_SUFFIX, Visibility::PRIVATE, ); } @@ -213,7 +213,7 @@ public function generate(): string * This mirrors TraitProxyGenerator::getJoinpointInvocationBody() because enums, * like traits, cannot hold a class-level $__joinPoints property. * - * All intercepted enum methods have __aop__ aliases from the enum's trait-use block. + * All intercepted enum methods have `OriginalAlias` aliases from the enum's trait-use block. */ protected function getJoinpointInvocationBody(ReflectionMethod $method, ?ReflectionClass $originalClass = null): string { @@ -251,10 +251,10 @@ protected function getJoinpointInvocationBody(ReflectionMethod $method, ?Reflect ? 'StaticMethodInvocation' : 'DynamicMethodInvocation'; - // All intercepted enum methods have __aop__ aliases from the enum's trait-use block. + // All intercepted enum methods have `OriginalAlias` aliases from the enum's trait-use block. $callableExpression = $isStatic - ? 'self::' . AbstractMethodInvocation::TRAIT_ALIAS_PREFIX . $method->name . '(...)' - : '$this->' . AbstractMethodInvocation::TRAIT_ALIAS_PREFIX . $method->name . '(...)'; + ? 'self::' . $method->name . AbstractMethodInvocation::TRAIT_ALIAS_SUFFIX . '(...)' + : '$this->' . $method->name . AbstractMethodInvocation::TRAIT_ALIAS_SUFFIX . '(...)'; return <<getNumberOfParameters() > 0 ? '...' : ''; if ($constructorIsInTrait) { - $constructorCallBody = '$this->__aop____construct(' . $splatPrefix . $callArguments->generate() . ');'; + $constructorAlias = '__construct' . AbstractMethodInvocation::TRAIT_ALIAS_SUFFIX; + $constructorCallBody = '$this->' . $constructorAlias . '(' . $splatPrefix . $callArguments->generate() . ');'; } else { $constructorCallBody = 'parent::__construct(' . $splatPrefix . $callArguments->generate() . ');'; } diff --git a/src/Proxy/TraitProxyGenerator.php b/src/Proxy/TraitProxyGenerator.php index bb984e9a..638afc84 100644 --- a/src/Proxy/TraitProxyGenerator.php +++ b/src/Proxy/TraitProxyGenerator.php @@ -88,7 +88,7 @@ public function __construct( foreach ($interceptedMethods as $methodName) { $fullName = $parentNormalizedName . '::' . $methodName; - $traitGenerator->addTraitAlias($fullName, AbstractMethodInvocation::TRAIT_ALIAS_PREFIX . $methodName, Visibility::PRIVATE); + $traitGenerator->addTraitAlias($fullName, $methodName . AbstractMethodInvocation::TRAIT_ALIAS_SUFFIX, Visibility::PRIVATE); } // Register use-imports for AOP classes referenced in generated method bodies. @@ -122,7 +122,7 @@ public function __construct( /** * Creates string definition for trait method body by method reflection * - * In a trait proxy, all intercepted methods always have a private __aop__ alias in the + * In a trait proxy, all intercepted methods always have a private `OriginalAlias` alias in the * trait-use block (from the parent trait). So the callable always references the alias. */ protected function getJoinpointInvocationBody(ReflectionMethod $method, ?ReflectionClass $originalClass = null): string @@ -162,10 +162,10 @@ protected function getJoinpointInvocationBody(ReflectionMethod $method, ?Reflect ? 'StaticMethodInvocation' : 'DynamicMethodInvocation'; - // All intercepted methods in a trait proxy have __aop__ aliases from the parent trait. + // All intercepted methods in a trait proxy have `OriginalAlias` aliases from the parent trait. $callableExpression = $isStatic - ? 'self::' . AbstractMethodInvocation::TRAIT_ALIAS_PREFIX . $method->name . '(...)' - : '$this->' . AbstractMethodInvocation::TRAIT_ALIAS_PREFIX . $method->name . '(...)'; + ? 'self::' . $method->name . AbstractMethodInvocation::TRAIT_ALIAS_SUFFIX . '(...)' + : '$this->' . $method->name . AbstractMethodInvocation::TRAIT_ALIAS_SUFFIX . '(...)'; return <<createMock(EntityManager::class); @@ -84,7 +84,7 @@ trait SimpleTrait private mixed $mappedField; } -class Entity__AopProxied +class EntityOriginalTrait { use SimpleTrait; } diff --git a/tests/Aop/Framework/DynamicTraitAliasMethodInvocationTest.php b/tests/Aop/Framework/DynamicTraitAliasMethodInvocationTest.php index fad0026f..72df2a88 100644 --- a/tests/Aop/Framework/DynamicTraitAliasMethodInvocationTest.php +++ b/tests/Aop/Framework/DynamicTraitAliasMethodInvocationTest.php @@ -21,7 +21,7 @@ class DynamicTraitAliasMethodInvocationTest extends TestCase { /** - * Verifies that invoking routes through __aop__ (the trait alias), + * Verifies that invoking routes through OriginalAlias (the trait alias), * not through the overridden public method that returns the sentinel -1. */ /** @@ -146,7 +146,7 @@ public static function dynamicMethodsBatch(): array { return [ // publicMethod is overridden in TraitAliasProxy to return -1; - // the invocation must go through __aop__publicMethod which returns T_PUBLIC. + // the invocation must go through publicMethodOriginalAlias which returns T_PUBLIC. ['publicMethod', T_PUBLIC], ['protectedMethod', T_PROTECTED], ]; @@ -191,7 +191,7 @@ public function testReflectionDispatchCallsEachInstanceCorrectly(): void } /** - * The dispatch must route through the original method body (the __aop__ alias), not through + * The dispatch must route through the original method body (the Original-suffixed alias), not through * the overridden public method that returns the sentinel -1. */ public function testDispatchInvokesOriginalMethodBody(): void diff --git a/tests/Aop/Framework/StaticTraitAliasMethodInvocationTest.php b/tests/Aop/Framework/StaticTraitAliasMethodInvocationTest.php index 9abcc94f..9870b359 100644 --- a/tests/Aop/Framework/StaticTraitAliasMethodInvocationTest.php +++ b/tests/Aop/Framework/StaticTraitAliasMethodInvocationTest.php @@ -22,7 +22,7 @@ class StaticTraitAliasMethodInvocationTest extends TestCase { /** - * Verifies that invoking routes through __aop__ (the trait alias), + * Verifies that invoking routes through OriginalAlias (the trait alias), * not through the overridden static method that returns the sentinel -1. */ public function testStaticMethodInvocation(): void @@ -112,7 +112,7 @@ public function testGetScopeReturnsProxyClassName(): void /** * Regression: when a subclass of the proxy is used as the static scope (LSB — e.g. via * static::class in the generated override method), the joinpoint must still route through - * the private __aop__ alias defined on the parent proxy class and execute the original + * the private Original-suffixed alias defined on the parent proxy class and execute the original * method body correctly. */ public function testLateStaticBindingWithSubclassScope(): void diff --git a/tests/Core/AdviceMatcherTest.php b/tests/Core/AdviceMatcherTest.php index 4ab50125..cbe18293 100644 --- a/tests/Core/AdviceMatcherTest.php +++ b/tests/Core/AdviceMatcherTest.php @@ -341,7 +341,7 @@ public function testAbstractMethodIsNotMatched(): void /** * Verifies that when a class' parent is an AOP-proxied trait-holder (name contains the - * __AopProxied suffix), advice matching resolves methods against that original parent class + * Original suffix), advice matching resolves methods against that original parent class * instead of the (proxy) class passed in - private methods declared directly on the original * class must still be matched. */ @@ -359,7 +359,7 @@ public function testResolvesOriginalClassWhenParentIsAopProxied(): void $advices = $this->adviceMatcher->getAdvicesForClass($reflectionClass, ['advisor' => $advisor]); $methodAdvices = $advices[AspectContainer::METHOD_PREFIX] ?? []; - // privateOriginal is declared directly on the __AopProxied parent, so it is matched + // privateOriginal is declared directly on the Original parent, so it is matched // only if the original (parent) class was used for the declaring-class comparison. $this->assertArrayHasKey('privateOriginal', $methodAdvices); } @@ -550,13 +550,13 @@ abstract public function abstractMethod(): void; public function concreteMethod(): void {} } -class AdviceMatcherTestFoo__AopProxied +class AdviceMatcherTestFooOriginalTrait { // @phpstan-ignore method.unused (only ever reached via reflection in AdviceMatcher, never called directly) private function privateOriginal(): void {} } -class AdviceMatcherTestProxyChild extends AdviceMatcherTestFoo__AopProxied {} +class AdviceMatcherTestProxyChild extends AdviceMatcherTestFooOriginalTrait {} trait AdviceMatcherTestIntroducedTrait {} diff --git a/tests/Functional/TraitCompositionTest.php b/tests/Functional/TraitCompositionTest.php index 9abbc2c7..8b83e3d2 100644 --- a/tests/Functional/TraitCompositionTest.php +++ b/tests/Functional/TraitCompositionTest.php @@ -94,7 +94,7 @@ public function testClassWithPrivateMethodsIsWoven(): void /** * Private method interception: the trait-based engine can intercept private methods - * by aliasing them as `private __aop__` in the proxy trait-use block. + * by aliasing them as `private OriginalAlias` in the proxy trait-use block. * This was impossible with the old extend-based engine. */ public function testPrivateMethodIsWoven(): void diff --git a/tests/Instrument/ClassLoading/SourceTransformingLoaderTest.php b/tests/Instrument/ClassLoading/SourceTransformingLoaderTest.php index 26d3f47e..5e927432 100644 --- a/tests/Instrument/ClassLoading/SourceTransformingLoaderTest.php +++ b/tests/Instrument/ClassLoading/SourceTransformingLoaderTest.php @@ -64,7 +64,12 @@ protected function tearDown(): void // Deletes only the exact files this test writes, never a glob/recursive sweep: // a wrong directory value must not be able to erase anything else $this->assertStringStartsWith(sys_get_temp_dir() . '/goaop-stl-', $this->cacheDir); - foreach ([$this->cacheDir . '/src/Some.php', $this->originalFile] as $knownFile) { + $knownFiles = [ + $this->cacheDir . '/src/Some.php', + $this->cacheDir . '/src/Some' . AspectContainer::AOP_PROXIED_SUFFIX . '.php', + $this->originalFile, + ]; + foreach ($knownFiles as $knownFile) { if (is_file($knownFile)) { unlink($knownFile); } @@ -180,6 +185,42 @@ public function testCacheMissRunsTransformerChainAndPersistsWovenFile(): void $this->assertSame($cacheFile, $cacheState['cacheUri']); } + public function testWovenBodyTraitIsCachedUnderTheProxiedSuffix(): void + { + $wovenSource = "createTransformerStub(TransformerResultEnum::RESULT_TRANSFORMED, $wovenSource); + $this->registerLoader([$transformer]); + + $this->assertSame($wovenSource, $this->filterOriginalFile()); + + // The generated proxy claims the plain name in the cache, so the original body + // trait has to move aside to its own sibling file + $cacheFile = $this->cacheDir . '/src/Some' . AspectContainer::AOP_PROXIED_SUFFIX . '.php'; + $this->assertFileExists($cacheFile); + $this->assertFileDoesNotExist($this->cacheDir . '/src/Some.php'); + $cacheState = $this->cachePathManager->queryCacheState($this->originalFile); + $this->assertNotNull($cacheState); + $this->assertSame($cacheFile, $cacheState['cacheUri']); + } + + public function testTransformedSourceMerelyMentioningTheSuffixKeepsItsCacheFileName(): void + { + // Only a `trait OriginalTrait` declaration marks a woven body; a class that just + // carries the suffix word in its own name must not be moved aside + $wovenSource = "createTransformerStub(TransformerResultEnum::RESULT_TRANSFORMED, $wovenSource); + $this->registerLoader([$transformer]); + + $this->assertSame($wovenSource, $this->filterOriginalFile()); + + $cacheFile = $this->cacheDir . '/src/Some.php'; + $this->assertFileExists($cacheFile); + $this->assertFileDoesNotExist($this->cacheDir . '/src/Some' . AspectContainer::AOP_PROXIED_SUFFIX . '.php'); + $cacheState = $this->cachePathManager->queryCacheState($this->originalFile); + $this->assertNotNull($cacheState); + $this->assertSame($cacheFile, $cacheState['cacheUri']); + } + public function testStaleCacheRecordFallsBackToTransformerChain(): void { $transformer = $this->createTransformerStub(TransformerResultEnum::RESULT_TRANSFORMED, self::WOVEN_SOURCE); diff --git a/tests/Instrument/Transformer/ConstructorExecutionTransformerTest.php b/tests/Instrument/Transformer/ConstructorExecutionTransformerTest.php index 6320ff71..6bdf4180 100644 --- a/tests/Instrument/Transformer/ConstructorExecutionTransformerTest.php +++ b/tests/Instrument/Transformer/ConstructorExecutionTransformerTest.php @@ -108,7 +108,7 @@ public function testMagicMethodCallCreatesInstanceWithArguments(): void } /** - * Classes woven with an initialization advice expose __aop__initialization(); instantiation + * Classes woven with an initialization advice expose __initialization(); instantiation * has to be delegated to it instead of going through a ReflectionConstructorInvocation. */ public function testInitializationAwareClassIsCreatedThroughItsEntryPoint(): void diff --git a/tests/Instrument/Transformer/MagicConstantTransformerTest.php b/tests/Instrument/Transformer/MagicConstantTransformerTest.php index c4d3a89f..89342e25 100644 --- a/tests/Instrument/Transformer/MagicConstantTransformerTest.php +++ b/tests/Instrument/Transformer/MagicConstantTransformerTest.php @@ -116,4 +116,26 @@ public function testTransformerResolvesFileName(): void $class = get_class($this->transformer); $this->assertStringStartsWith(dirname(__DIR__), $class::resolveFileName(__FILE__)); } + + public function testTransformerDropsProxiedSuffixFromWovenBodyFileName(): void + { + $class = get_class($this->transformer); + + $this->assertSame( + dirname(__DIR__) . '/Some.php', + $class::resolveFileName(__DIR__ . '/Some' . AspectContainer::AOP_PROXIED_SUFFIX . '.php'), + ); + } + + public function testTransformerKeepsFileNameThatOnlyContainsProxiedSuffix(): void + { + $class = get_class($this->transformer); + + // The marker is only meaningful at the very end of the file name: a class that happens + // to carry the suffix word inside its own name must keep it + $this->assertSame( + dirname(__DIR__) . '/' . AspectContainer::AOP_PROXIED_SUFFIX . 'Request.php', + $class::resolveFileName(__DIR__ . '/' . AspectContainer::AOP_PROXIED_SUFFIX . 'Request.php'), + ); + } } diff --git a/tests/Instrument/Transformer/Stubs/InitializationAwareStub.php b/tests/Instrument/Transformer/Stubs/InitializationAwareStub.php index fc6ea1b5..9a9bc263 100644 --- a/tests/Instrument/Transformer/Stubs/InitializationAwareStub.php +++ b/tests/Instrument/Transformer/Stubs/InitializationAwareStub.php @@ -17,7 +17,7 @@ /** * Stand-in for a woven proxy that exposes the initialization interceptor entry point. * - * ConstructorExecutionTransformer must delegate instantiation to __aop__initialization() + * ConstructorExecutionTransformer must delegate instantiation to __initialization() * for such classes instead of building its own ReflectionConstructorInvocation. * * @implements InitializationAware @@ -25,7 +25,7 @@ class InitializationAwareStub implements InitializationAware { /** - * Arguments the last __aop__initialization() call received + * Arguments the last __initialization() call received * * @var list */ @@ -34,7 +34,7 @@ class InitializationAwareStub implements InitializationAware /** * @param list $arguments */ - public static function __aop__initialization(array $arguments = []): object + public static function __initialization(array $arguments = []): object { $instance = new self(); $instance->receivedArguments = $arguments; diff --git a/tests/Instrument/Transformer/Stubs/WeavingTraitStub.php b/tests/Instrument/Transformer/Stubs/WeavingTraitStub.php index 657cbaba..3cc99dff 100644 --- a/tests/Instrument/Transformer/Stubs/WeavingTraitStub.php +++ b/tests/Instrument/Transformer/Stubs/WeavingTraitStub.php @@ -14,7 +14,7 @@ /** * Trait weaving input: traits keep the legacy weaving strategy — the original trait is - * renamed to __AopProxied in place and TraitProxyGenerator emits a child trait + * renamed to OriginalTrait in place and TraitProxyGenerator emits a child trait * with the original name. Intercepted properties are commented out of the original body. */ trait WeavingTraitStub diff --git a/tests/Instrument/Transformer/WeavingTransformerTest.php b/tests/Instrument/Transformer/WeavingTransformerTest.php index 11e277ba..2566cb6e 100644 --- a/tests/Instrument/Transformer/WeavingTransformerTest.php +++ b/tests/Instrument/Transformer/WeavingTransformerTest.php @@ -350,8 +350,8 @@ public function testWovenEnumWithConstantExpressionCaseValuesWorksAtRuntime(): v /** * PHP 8.3 #[\Override] attribute must be stripped from intercepted methods. * - * When a method is aliased in the proxy's trait-use block (e.g. __aop__overriddenMethod), - * PHP copies attributes to the alias. Since __aop__overriddenMethod has no matching parent + * When a method is aliased in the proxy's trait-use block (e.g. overriddenMethodOriginalAlias), + * PHP copies attributes to the alias. Since overriddenMethodOriginalAlias has no matching parent * method, #[\Override] would cause a fatal error — so WeavingTransformer must remove it. */ public function testWeaverStripsOverrideAttributeFromInterceptedMethods(): void @@ -453,7 +453,7 @@ public function testWeaverCopiesNonScalarAttributeArgumentsFromAst(): void * Class-level attributes (with and without arguments) must survive the class→trait * conversion untouched (issue #598). Previously the first token inside `#[...]` was * renamed to the trait name and the rest of the attribute plus the real class header - * was deleted, producing a parse error like `#[Foo__AopProxied {`. + * was deleted, producing a parse error like `#[FooOriginalTrait {`. */ public function testWeaverKeepsClassLevelAttributesOnWovenTrait(): void { @@ -755,7 +755,7 @@ public function testWeaverDemotesFinalPromotedProperty(): void /** * Traits keep the legacy weaving strategy: the original trait is renamed to - * __AopProxied in place (adjustOriginalTrait) and TraitProxyGenerator emits a child + * Original in place (adjustOriginalTrait) and TraitProxyGenerator emits a child * trait with the original name. Intercepted properties are commented out of the original * trait body — the child trait re-declares them with interception hooks. */ @@ -779,8 +779,8 @@ public function testWeaverForTraitRenamesOriginalTraitAndMovesInterceptedPropert $actual = $this->normalizeWhitespaces($metadata->source); - // The trait keyword stays a trait, only the name gets the __AopProxied suffix - $this->assertStringContainsString('trait WeavingTraitStub__AopProxied', $actual); + // The trait keyword stays a trait, only the name gets the Original suffix + $this->assertStringContainsString('trait WeavingTraitStubOriginalTrait', $actual); $this->assertStringNotContainsString('trait WeavingTraitStub' . PHP_EOL, $actual); // Intercepted property is commented out, the untouched one survives verbatim @@ -796,9 +796,9 @@ public function testWeaverForTraitRenamesOriginalTraitAndMovesInterceptedPropert // The generated child trait keeps the original short name and uses the renamed trait $this->assertStringContainsString('trait WeavingTraitStub', $proxyContent); - $this->assertStringContainsString('WeavingTraitStub__AopProxied', $proxyContent); - $this->assertStringContainsString('__aop__traitMethod', $proxyContent); - $this->assertStringContainsString('__aop__traitStaticMethod', $proxyContent); + $this->assertStringContainsString('WeavingTraitStubOriginalTrait', $proxyContent); + $this->assertStringContainsString('traitMethodOriginalAlias', $proxyContent); + $this->assertStringContainsString('traitStaticMethodOriginalAlias', $proxyContent); $this->assertStringContainsString('$interceptedProperty', $proxyContent); } @@ -820,7 +820,7 @@ public function testWeaverStripsAbstractModifierFromWovenTrait(): void $actual = $this->normalizeWhitespaces($metadata->source); - $this->assertStringContainsString('trait TestAbstractClass__AopProxied', $actual); + $this->assertStringContainsString('trait TestAbstractClassOriginalTrait', $actual); $this->assertStringNotContainsString('abstract trait', $actual); $this->assertStringNotContainsString('abstract class', $actual); // Abstract methods are legal in traits and must survive untouched @@ -848,7 +848,7 @@ public function testWeaverRemovesLeadingCommaWhenIncompatibleAttributeIsLastInGr $this->assertStringContainsString('#[\FakeMarkerAttr]', $actual); $this->assertStringNotContainsString('\Attribute', $actual); $this->assertStringNotContainsString('#[\FakeMarkerAttr,', $actual); - $this->assertStringContainsString('trait TestLastGroupedAttributeClass__AopProxied', $actual); + $this->assertStringContainsString('trait TestLastGroupedAttributeClassOriginalTrait', $actual); // The woven trait must still be parseable PHP after the comma surgery $parser = (new \PhpParser\ParserFactory())->createForHostVersion(); @@ -931,7 +931,7 @@ public function testWeaverInterceptsInheritedMembersWithoutTouchingTraitBody(): $actual = $this->normalizeWhitespaces($metadata->source); - $this->assertStringContainsString('trait InheritedMethodChild__AopProxied', $actual); + $this->assertStringContainsString('trait InheritedMethodChildOriginalTrait', $actual); // The `extends` clause is moved from the trait to the proxy $this->assertStringNotContainsString('extends InheritedMethodBase', $actual); // Nothing of the parent declarations may be commented out in the child's trait body @@ -946,8 +946,8 @@ public function testWeaverInterceptsInheritedMembersWithoutTouchingTraitBody(): $proxyContent, ); // Own method is aliased in the trait-use block, inherited one goes through parent:: - $this->assertStringContainsString('as private __aop__ownMethod;', $proxyContent); - $this->assertStringNotContainsString('__aop__inheritedMethod', $proxyContent); + $this->assertStringContainsString('as private ownMethodOriginalAlias;', $proxyContent); + $this->assertStringNotContainsString('inheritedMethodOriginalAlias', $proxyContent); $this->assertStringContainsString('parent::inheritedMethod(...)', $proxyContent); } @@ -1125,7 +1125,7 @@ public function testWeaverWithoutCacheDirectoryEmitsNoIncludes(): void $actual = $this->normalizeWhitespaces($metadata->source); $this->assertSame(TransformerResultEnum::RESULT_TRANSFORMED, $result); - $this->assertStringContainsString('trait TestAbstractClass__AopProxied', $actual); + $this->assertStringContainsString('trait TestAbstractClassOriginalTrait', $actual); $this->assertStringNotContainsString('AOP_CACHE_DIR', $actual); } diff --git a/tests/Instrument/Transformer/_files/class-proxy.php b/tests/Instrument/Transformer/_files/class-proxy.php index 8f4bdb19..57a0460b 100644 --- a/tests/Instrument/Transformer/_files/class-proxy.php +++ b/tests/Instrument/Transformer/_files/class-proxy.php @@ -8,14 +8,14 @@ use Go\Aop\Intercept\StaticMethodInvocation; class TestClass implements \Go\Aop\Proxy { - use TestClass__AopProxied { - TestClass__AopProxied::publicMethod as private __aop__publicMethod; - TestClass__AopProxied::protectedMethod as private __aop__protectedMethod; - TestClass__AopProxied::publicStaticMethod as private __aop__publicStaticMethod; - TestClass__AopProxied::protectedStaticMethod as private __aop__protectedStaticMethod; - TestClass__AopProxied::publicMethodDynamicArguments as private __aop__publicMethodDynamicArguments; - TestClass__AopProxied::publicMethodFixedArguments as private __aop__publicMethodFixedArguments; - TestClass__AopProxied::methodWithSpecialTypeArguments as private __aop__methodWithSpecialTypeArguments; + use TestClassOriginalTrait { + TestClassOriginalTrait::publicMethod as private publicMethodOriginalAlias; + TestClassOriginalTrait::protectedMethod as private protectedMethodOriginalAlias; + TestClassOriginalTrait::publicStaticMethod as private publicStaticMethodOriginalAlias; + TestClassOriginalTrait::protectedStaticMethod as private protectedStaticMethodOriginalAlias; + TestClassOriginalTrait::publicMethodDynamicArguments as private publicMethodDynamicArgumentsOriginalAlias; + TestClassOriginalTrait::publicMethodFixedArguments as private publicMethodFixedArgumentsOriginalAlias; + TestClassOriginalTrait::methodWithSpecialTypeArguments as private methodWithSpecialTypeArgumentsOriginalAlias; } public function publicMethod() { @@ -26,7 +26,7 @@ public function publicMethod() [ Interceptor::before(The::advice('advisor.Test\ns1\TestClass->publicMethod')), ], - $this->__aop__publicMethod(...), + $this->publicMethodOriginalAlias(...), ); return $__joinPoint->__invoke($this); } @@ -39,7 +39,7 @@ protected function protectedMethod() [ Interceptor::before(The::advice('advisor.Test\ns1\TestClass->protectedMethod')), ], - $this->__aop__protectedMethod(...), + $this->protectedMethodOriginalAlias(...), ); return $__joinPoint->__invoke($this); } @@ -52,7 +52,7 @@ public static function publicStaticMethod() [ Interceptor::before(The::advice('advisor.Test\ns1\TestClass->publicStaticMethod')), ], - self::__aop__publicStaticMethod(...), + self::publicStaticMethodOriginalAlias(...), ); return $__joinPoint->__invoke(static::class); } @@ -65,7 +65,7 @@ protected static function protectedStaticMethod() [ Interceptor::before(The::advice('advisor.Test\ns1\TestClass->protectedStaticMethod')), ], - self::__aop__protectedStaticMethod(...), + self::protectedStaticMethodOriginalAlias(...), ); return $__joinPoint->__invoke(static::class); } @@ -78,7 +78,7 @@ public function publicMethodDynamicArguments($a, &$b) [ Interceptor::before(The::advice('advisor.Test\ns1\TestClass->publicMethodDynamicArguments')), ], - $this->__aop__publicMethodDynamicArguments(...), + $this->publicMethodDynamicArgumentsOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$a, &$b]); } @@ -91,7 +91,7 @@ public function publicMethodFixedArguments($a, $b, $c = null) [ Interceptor::before(The::advice('advisor.Test\ns1\TestClass->publicMethodFixedArguments')), ], - $this->__aop__publicMethodFixedArguments(...), + $this->publicMethodFixedArgumentsOriginalAlias(...), ); return $__joinPoint->__invoke($this, \array_slice([$a, $b, $c], 0, \func_num_args())); } @@ -104,7 +104,7 @@ public function methodWithSpecialTypeArguments(self $instance) [ Interceptor::before(The::advice('advisor.Test\ns1\TestClass->methodWithSpecialTypeArguments')), ], - $this->__aop__methodWithSpecialTypeArguments(...), + $this->methodWithSpecialTypeArgumentsOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$instance]); } diff --git a/tests/Instrument/Transformer/_files/class-typehint-woven.php b/tests/Instrument/Transformer/_files/class-typehint-woven.php index 501994df..02c2c025 100644 --- a/tests/Instrument/Transformer/_files/class-typehint-woven.php +++ b/tests/Instrument/Transformer/_files/class-typehint-woven.php @@ -1,6 +1,6 @@ publicMethod')), ], - $this->__aop__publicMethod(...), + $this->publicMethodOriginalAlias(...), ); return $__joinPoint->__invoke($this); } @@ -35,7 +35,7 @@ public function anotherMethod(int $x): int [ Interceptor::before(The::advice('advisor.Test\ns1\TestReadonlyClass->anotherMethod')), ], - $this->__aop__anotherMethod(...), + $this->anotherMethodOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$x]); } @@ -48,7 +48,7 @@ public static function staticMethod(): string [ Interceptor::before(The::advice('advisor.Test\ns1\TestReadonlyClass->staticMethod')), ], - self::__aop__staticMethod(...), + self::staticMethodOriginalAlias(...), ); return $__joinPoint->__invoke(static::class); } diff --git a/tests/Instrument/Transformer/_files/final-readonly-class-woven.php b/tests/Instrument/Transformer/_files/final-readonly-class-woven.php index 06d4a974..7ba2ba43 100644 --- a/tests/Instrument/Transformer/_files/final-readonly-class-woven.php +++ b/tests/Instrument/Transformer/_files/final-readonly-class-woven.php @@ -2,7 +2,7 @@ declare(strict_types=1); namespace Test\ns1; -trait TestReadonlyClass__AopProxied +trait TestReadonlyClassOriginalTrait { public function publicMethod(): string { diff --git a/tests/Instrument/Transformer/_files/multiple-classes-woven.php b/tests/Instrument/Transformer/_files/multiple-classes-woven.php index 7e3436fb..08a37ade 100644 --- a/tests/Instrument/Transformer/_files/multiple-classes-woven.php +++ b/tests/Instrument/Transformer/_files/multiple-classes-woven.php @@ -2,17 +2,17 @@ declare(strict_types = 1); namespace Test\ns3; -trait TestClass1__AopProxied { +trait TestClass1OriginalTrait { public static function test() {} } include_once AOP_CACHE_DIR . '/Transformer/_files/multiple-classes.php'; TestClass1::test(); -trait TestClass11__AopProxied { +trait TestClass11OriginalTrait { public static function test() {} } include_once AOP_CACHE_DIR . '/Transformer/_files/multiple-classes.php'; TestClass11::test(); -trait TestClass2__AopProxied { +trait TestClass2OriginalTrait { public static function test() {} } include_once AOP_CACHE_DIR . '/Transformer/_files/multiple-classes.php'; diff --git a/tests/Instrument/Transformer/_files/multiple-ns-woven.php b/tests/Instrument/Transformer/_files/multiple-ns-woven.php index 1ce72e95..62ded0ad 100644 --- a/tests/Instrument/Transformer/_files/multiple-ns-woven.php +++ b/tests/Instrument/Transformer/_files/multiple-ns-woven.php @@ -1,13 +1,13 @@ stringSth')), ], - $this->__aop__stringSth(...), + $this->stringSthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$arg]); } @@ -48,7 +48,7 @@ public function floatSth(float $arg) [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->floatSth')), ], - $this->__aop__floatSth(...), + $this->floatSthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$arg]); } @@ -61,7 +61,7 @@ public function boolSth(bool $arg) [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->boolSth')), ], - $this->__aop__boolSth(...), + $this->boolSthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$arg]); } @@ -74,7 +74,7 @@ public function intSth(int $arg) [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->intSth')), ], - $this->__aop__intSth(...), + $this->intSthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$arg]); } @@ -87,7 +87,7 @@ public function callableSth(callable $arg) [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->callableSth')), ], - $this->__aop__callableSth(...), + $this->callableSthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$arg]); } @@ -100,7 +100,7 @@ public function arraySth(array $arg) [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->arraySth')), ], - $this->__aop__arraySth(...), + $this->arraySthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$arg]); } @@ -113,7 +113,7 @@ public function variadicStringSthByRef(string &...$args) [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->variadicStringSthByRef')), ], - $this->__aop__variadicStringSthByRef(...), + $this->variadicStringSthByRefOriginalAlias(...), ); return $__joinPoint->__invoke($this, $args); } @@ -126,7 +126,7 @@ public function exceptionArg(\Exception $exception, \Test\ns1\Exception $localEx [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->exceptionArg')), ], - $this->__aop__exceptionArg(...), + $this->exceptionArgOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$exception, $localException]); } @@ -139,7 +139,7 @@ public function stringRth(string $arg): string [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->stringRth')), ], - $this->__aop__stringRth(...), + $this->stringRthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$arg]); } @@ -152,7 +152,7 @@ public function floatRth(float $arg): float [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->floatRth')), ], - $this->__aop__floatRth(...), + $this->floatRthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$arg]); } @@ -165,7 +165,7 @@ public function boolRth(bool $arg): bool [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->boolRth')), ], - $this->__aop__boolRth(...), + $this->boolRthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$arg]); } @@ -178,7 +178,7 @@ public function intRth(int $arg): int [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->intRth')), ], - $this->__aop__intRth(...), + $this->intRthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$arg]); } @@ -191,7 +191,7 @@ public function callableRth(callable $arg): callable [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->callableRth')), ], - $this->__aop__callableRth(...), + $this->callableRthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$arg]); } @@ -204,7 +204,7 @@ public function arrayRth(array $arg): array [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->arrayRth')), ], - $this->__aop__arrayRth(...), + $this->arrayRthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$arg]); } @@ -217,7 +217,7 @@ public function exceptionRth(\Exception $exception): \Exception [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->exceptionRth')), ], - $this->__aop__exceptionRth(...), + $this->exceptionRthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$exception]); } @@ -230,7 +230,7 @@ public function noRth(\Test\ns1\LocalException $exception) [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->noRth')), ], - $this->__aop__noRth(...), + $this->noRthOriginalAlias(...), ); return $__joinPoint->__invoke($this, [$exception]); } @@ -243,7 +243,7 @@ public function returnSelf(): self [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp7Class->returnSelf')), ], - $this->__aop__returnSelf(...), + $this->returnSelfOriginalAlias(...), ); return $__joinPoint->__invoke($this); } diff --git a/tests/Instrument/Transformer/_files/php7-class-woven.php b/tests/Instrument/Transformer/_files/php7-class-woven.php index 3cbb31db..111dbaf4 100644 --- a/tests/Instrument/Transformer/_files/php7-class-woven.php +++ b/tests/Instrument/Transformer/_files/php7-class-woven.php @@ -1,7 +1,7 @@ __construct')), ], - $this->__aop____construct(...), + $this->__constructOriginalAlias(...), ); return $__joinPoint->__invoke($this, \array_slice([$label, $items], 0, \func_num_args())); } @@ -40,7 +40,7 @@ public function describe(?\ArrayObject $extra = null): string [ Interceptor::before(The::advice('advisor.Test\ns1\TestPhp80To82SyntaxClass->describe')), ], - $this->__aop__describe(...), + $this->describeOriginalAlias(...), ); return $__joinPoint->__invoke($this, \array_slice([$extra], 0, \func_num_args())); } diff --git a/tests/Instrument/Transformer/_files/php80-82-syntax-woven.php b/tests/Instrument/Transformer/_files/php80-82-syntax-woven.php index 699b4a21..b2a91f75 100644 --- a/tests/Instrument/Transformer/_files/php80-82-syntax-woven.php +++ b/tests/Instrument/Transformer/_files/php80-82-syntax-woven.php @@ -14,7 +14,7 @@ enum SyntaxPriority: int * default, named arguments, match expression, nullsafe operator, enum usage, * readonly property, first-class callable and a typed class constant. */ -trait TestPhp80To82SyntaxClass__AopProxied +trait TestPhp80To82SyntaxClassOriginalTrait { public const int LIMIT = 10; diff --git a/tests/Instrument/Transformer/_files/php80-attribute-class-woven.php b/tests/Instrument/Transformer/_files/php80-attribute-class-woven.php index 8e237ed4..795accb1 100644 --- a/tests/Instrument/Transformer/_files/php80-attribute-class-woven.php +++ b/tests/Instrument/Transformer/_files/php80-attribute-class-woven.php @@ -10,7 +10,7 @@ * The proxy class copies the original attribute groups from the AST and keeps them. */ #[\FakeMarkerAttr] -trait TestGroupedAttributeClass__AopProxied +trait TestGroupedAttributeClassOriginalTrait { public function name(): string { @@ -20,7 +20,7 @@ public function name(): string include_once AOP_CACHE_DIR . '/Transformer/_files/php80-attribute-class.php'; -trait TestDynamicPropertiesClass__AopProxied +trait TestDynamicPropertiesClassOriginalTrait { public function touch(): bool { @@ -30,7 +30,7 @@ public function touch(): bool include_once AOP_CACHE_DIR . '/Transformer/_files/php80-attribute-class.php'; -trait TestCustomAttribute__AopProxied +trait TestCustomAttributeOriginalTrait { public function __construct(private string $reason = 'none') { diff --git a/tests/Instrument/Transformer/_files/php80-class-attribute-woven.php b/tests/Instrument/Transformer/_files/php80-class-attribute-woven.php index fa37dfd7..01a68ab3 100644 --- a/tests/Instrument/Transformer/_files/php80-class-attribute-woven.php +++ b/tests/Instrument/Transformer/_files/php80-class-attribute-woven.php @@ -10,7 +10,7 @@ * invalid on traits and are removed from the woven trait (issue #615). */ #[\FakeMarkerAttr] -trait TestClassWithPlainAttribute__AopProxied +trait TestClassWithPlainAttributeOriginalTrait { public function doSomething(): int { @@ -21,7 +21,7 @@ public function doSomething(): int #[\FakeMarkerAttr] -trait TestClassWithArgumentAttribute__AopProxied +trait TestClassWithArgumentAttributeOriginalTrait { public function doSomethingElse(): string { diff --git a/tests/Instrument/Transformer/_files/php80-promoted-property-proxy.php b/tests/Instrument/Transformer/_files/php80-promoted-property-proxy.php index fc3b4128..93dde23c 100644 --- a/tests/Instrument/Transformer/_files/php80-promoted-property-proxy.php +++ b/tests/Instrument/Transformer/_files/php80-promoted-property-proxy.php @@ -14,9 +14,9 @@ */ class PromotedPropertyClass implements \Go\Aop\Proxy { - use PromotedPropertyClass__AopProxied { - PromotedPropertyClass__AopProxied::__construct as private __aop____construct; - PromotedPropertyClass__AopProxied::getName as private __aop__getName; + use PromotedPropertyClassOriginalTrait { + PromotedPropertyClassOriginalTrait::__construct as private __constructOriginalAlias; + PromotedPropertyClassOriginalTrait::getName as private getNameOriginalAlias; } private string $name = 'initial' { get { @@ -75,7 +75,7 @@ public function __construct(string $name = 'initial', int $counter = 1, ?\ArrayO [ Interceptor::before(The::advice('advisor.Go\Tests\TestProject\Application\PromotedPropertyClass->__construct')), ], - $this->__aop____construct(...), + $this->__constructOriginalAlias(...), ); return $__joinPoint->__invoke($this, \array_slice([$name, $counter, $bag], 0, \func_num_args())); } @@ -88,7 +88,7 @@ public function getName(): string [ Interceptor::before(The::advice('advisor.Go\Tests\TestProject\Application\PromotedPropertyClass->getName')), ], - $this->__aop__getName(...), + $this->getNameOriginalAlias(...), ); return $__joinPoint->__invoke($this); } diff --git a/tests/Instrument/Transformer/_files/php80-promoted-property-single-line-proxy.php b/tests/Instrument/Transformer/_files/php80-promoted-property-single-line-proxy.php index abd9026d..38ec1aae 100644 --- a/tests/Instrument/Transformer/_files/php80-promoted-property-single-line-proxy.php +++ b/tests/Instrument/Transformer/_files/php80-promoted-property-single-line-proxy.php @@ -14,8 +14,8 @@ */ class SingleLinePromotedClass implements \Go\Aop\Proxy { - use SingleLinePromotedClass__AopProxied { - SingleLinePromotedClass__AopProxied::__construct as private __aop____construct; + use SingleLinePromotedClassOriginalTrait { + SingleLinePromotedClassOriginalTrait::__construct as private __constructOriginalAlias; } public string $tag = 'default' { get { @@ -50,7 +50,7 @@ public function __construct(string $tag = 'default') [ Interceptor::before(The::advice('advisor.Go\Tests\TestProject\Application\SingleLinePromotedClass->__construct')), ], - $this->__aop____construct(...), + $this->__constructOriginalAlias(...), ); return $__joinPoint->__invoke($this, \array_slice([$tag], 0, \func_num_args())); } diff --git a/tests/Instrument/Transformer/_files/php80-promoted-property-single-line-woven.php b/tests/Instrument/Transformer/_files/php80-promoted-property-single-line-woven.php index e2195c20..26bb3db9 100644 --- a/tests/Instrument/Transformer/_files/php80-promoted-property-single-line-woven.php +++ b/tests/Instrument/Transformer/_files/php80-promoted-property-single-line-woven.php @@ -7,7 +7,7 @@ * Class with a promoted constructor property in a single-line constructor used for * testing interception of promoted properties (issue #599). */ -trait SingleLinePromotedClass__AopProxied +trait SingleLinePromotedClassOriginalTrait { public function __construct(string $tag = 'default') { $this->tag = $tag;} } diff --git a/tests/Instrument/Transformer/_files/php80-promoted-property-woven.php b/tests/Instrument/Transformer/_files/php80-promoted-property-woven.php index 8d8d5987..e0fbd95d 100644 --- a/tests/Instrument/Transformer/_files/php80-promoted-property-woven.php +++ b/tests/Instrument/Transformer/_files/php80-promoted-property-woven.php @@ -7,7 +7,7 @@ * Class with promoted constructor properties (multi-line constructor) used for * testing interception of promoted properties (issue #599). */ -trait PromotedPropertyClass__AopProxied +trait PromotedPropertyClassOriginalTrait { public function __construct( string $name = 'initial', diff --git a/tests/Instrument/Transformer/_files/php81-attr-args-proxy.php b/tests/Instrument/Transformer/_files/php81-attr-args-proxy.php index 60f4be19..13d0ed39 100644 --- a/tests/Instrument/Transformer/_files/php81-attr-args-proxy.php +++ b/tests/Instrument/Transformer/_files/php81-attr-args-proxy.php @@ -8,9 +8,9 @@ use Go\Aop\Intercept\DynamicMethodInvocation; class TestAttributeArgsClass implements \Go\Aop\Proxy { - use TestAttributeArgsClass__AopProxied { - TestAttributeArgsClass__AopProxied::tagged as private __aop__tagged; - TestAttributeArgsClass__AopProxied::collected as private __aop__collected; + use TestAttributeArgsClassOriginalTrait { + TestAttributeArgsClassOriginalTrait::tagged as private taggedOriginalAlias; + TestAttributeArgsClassOriginalTrait::collected as private collectedOriginalAlias; } #[\Test\ns1\RichValueAttr(\Test\ns1\AttrStatus::Disabled, PHP_INT_MAX)] public function tagged( @@ -25,7 +25,7 @@ public function tagged( [ Interceptor::before(The::advice('advisor.Test\ns1\TestAttributeArgsClass->tagged')), ], - $this->__aop__tagged(...), + $this->taggedOriginalAlias(...), ); return $__joinPoint->__invoke($this, \array_slice([$x], 0, \func_num_args())); } @@ -39,7 +39,7 @@ public function collected(): array [ Interceptor::before(The::advice('advisor.Test\ns1\TestAttributeArgsClass->collected')), ], - $this->__aop__collected(...), + $this->collectedOriginalAlias(...), ); return $__joinPoint->__invoke($this); } diff --git a/tests/Instrument/Transformer/_files/php81-attr-args-woven.php b/tests/Instrument/Transformer/_files/php81-attr-args-woven.php index 25e3d3a9..194be6da 100644 --- a/tests/Instrument/Transformer/_files/php81-attr-args-woven.php +++ b/tests/Instrument/Transformer/_files/php81-attr-args-woven.php @@ -25,7 +25,7 @@ public function __construct( } } -trait TestAttributeArgsClass__AopProxied +trait TestAttributeArgsClassOriginalTrait { #[RichValueAttr(AttrStatus::Disabled, PHP_INT_MAX)] public function tagged(#[RichValueAttr(AttrStatus::Active)] int $x = 8): int diff --git a/tests/Instrument/Transformer/_files/php81-enum-const-expr-proxy.php b/tests/Instrument/Transformer/_files/php81-enum-const-expr-proxy.php index 11988376..82560cf7 100644 --- a/tests/Instrument/Transformer/_files/php81-enum-const-expr-proxy.php +++ b/tests/Instrument/Transformer/_files/php81-enum-const-expr-proxy.php @@ -8,8 +8,8 @@ use Go\Aop\Intercept\DynamicMethodInvocation; enum ConstExprStatus : int implements \Go\Aop\Proxy { - use ConstExprStatus__AopProxied { - ConstExprStatus__AopProxied::describe as private __aop__describe; + use ConstExprStatusOriginalTrait { + ConstExprStatusOriginalTrait::describe as private describeOriginalAlias; } case Negative = -1; case Shifted = 1 << 2; @@ -23,7 +23,7 @@ public function describe(): string [ Interceptor::before(The::advice('advisor.Test\ns1\ConstExprStatus->describe')), ], - $this->__aop__describe(...), + $this->describeOriginalAlias(...), ); return $__joinPoint->__invoke($this); } diff --git a/tests/Instrument/Transformer/_files/php81-enum-const-expr-woven.php b/tests/Instrument/Transformer/_files/php81-enum-const-expr-woven.php index c8f4f3ae..59f857bc 100644 --- a/tests/Instrument/Transformer/_files/php81-enum-const-expr-woven.php +++ b/tests/Instrument/Transformer/_files/php81-enum-const-expr-woven.php @@ -5,7 +5,7 @@ /** * PHP 8.1 backed enum whose case values are constant expressions, not plain literals (issue #600). */ -trait ConstExprStatus__AopProxied +trait ConstExprStatusOriginalTrait { private const int SHIFT = 2; diff --git a/tests/Instrument/Transformer/_files/php81-enum-proxy.php b/tests/Instrument/Transformer/_files/php81-enum-proxy.php index 0396b4ed..de280333 100644 --- a/tests/Instrument/Transformer/_files/php81-enum-proxy.php +++ b/tests/Instrument/Transformer/_files/php81-enum-proxy.php @@ -7,8 +7,8 @@ use Go\Aop\Intercept\DynamicMethodInvocation; enum TestStatus : string implements \Go\Aop\Proxy { - use TestStatus__AopProxied { - TestStatus__AopProxied::label as private __aop__label; + use TestStatusOriginalTrait { + TestStatusOriginalTrait::label as private labelOriginalAlias; } case Active = 'active'; case Inactive = 'inactive'; @@ -21,7 +21,7 @@ public function label(): string [ Interceptor::before(The::advice('advisor.Test\ns1\TestStatus->label')), ], - $this->__aop__label(...), + $this->labelOriginalAlias(...), ); return $__joinPoint->__invoke($this); } diff --git a/tests/Instrument/Transformer/_files/php81-enum-woven.php b/tests/Instrument/Transformer/_files/php81-enum-woven.php index ca6d98cd..2c119228 100644 --- a/tests/Instrument/Transformer/_files/php81-enum-woven.php +++ b/tests/Instrument/Transformer/_files/php81-enum-woven.php @@ -5,7 +5,7 @@ /** * PHP 8.1 backed enum — woven by extracting methods into a trait and re-declaring cases in the proxy enum. */ -trait TestStatus__AopProxied +trait TestStatusOriginalTrait { diff --git a/tests/Instrument/Transformer/_files/php83-override-proxy.php b/tests/Instrument/Transformer/_files/php83-override-proxy.php index 7c1e8678..5daa18a4 100644 --- a/tests/Instrument/Transformer/_files/php83-override-proxy.php +++ b/tests/Instrument/Transformer/_files/php83-override-proxy.php @@ -8,13 +8,13 @@ /** * PHP 8.3 — class with #[\Override] on an intercepted method. * WeavingTransformer must strip the attribute from the generated trait so that - * the proxy's __aop__overriddenMethod alias does not trigger a fatal error. + * the proxy's overriddenMethodOriginalAlias alias does not trigger a fatal error. */ class TestClassWithOverride implements \Go\Aop\Proxy { - use TestClassWithOverride__AopProxied { - TestClassWithOverride__AopProxied::overriddenMethod as private __aop__overriddenMethod; - TestClassWithOverride__AopProxied::normalMethod as private __aop__normalMethod; + use TestClassWithOverrideOriginalTrait { + TestClassWithOverrideOriginalTrait::overriddenMethod as private overriddenMethodOriginalAlias; + TestClassWithOverrideOriginalTrait::normalMethod as private normalMethodOriginalAlias; } #[\Override] public function overriddenMethod(): string @@ -26,7 +26,7 @@ public function overriddenMethod(): string [ Interceptor::before(The::advice('advisor.Test\ns1\TestClassWithOverride->overriddenMethod')), ], - $this->__aop__overriddenMethod(...), + $this->overriddenMethodOriginalAlias(...), ); return $__joinPoint->__invoke($this); } @@ -39,7 +39,7 @@ public function normalMethod(): int [ Interceptor::before(The::advice('advisor.Test\ns1\TestClassWithOverride->normalMethod')), ], - $this->__aop__normalMethod(...), + $this->normalMethodOriginalAlias(...), ); return $__joinPoint->__invoke($this); } diff --git a/tests/Instrument/Transformer/_files/php83-override-woven.php b/tests/Instrument/Transformer/_files/php83-override-woven.php index 9f0af489..6db47c32 100644 --- a/tests/Instrument/Transformer/_files/php83-override-woven.php +++ b/tests/Instrument/Transformer/_files/php83-override-woven.php @@ -5,9 +5,9 @@ /** * PHP 8.3 — class with #[\Override] on an intercepted method. * WeavingTransformer must strip the attribute from the generated trait so that - * the proxy's __aop__overriddenMethod alias does not trigger a fatal error. + * the proxy's overriddenMethodOriginalAlias alias does not trigger a fatal error. */ -trait TestClassWithOverride__AopProxied +trait TestClassWithOverrideOriginalTrait { public function overriddenMethod(): string { diff --git a/tests/Instrument/Transformer/_files/php83-override.php b/tests/Instrument/Transformer/_files/php83-override.php index 3bb2682a..fb9a7d31 100644 --- a/tests/Instrument/Transformer/_files/php83-override.php +++ b/tests/Instrument/Transformer/_files/php83-override.php @@ -5,7 +5,7 @@ /** * PHP 8.3 — class with #[\Override] on an intercepted method. * WeavingTransformer must strip the attribute from the generated trait so that - * the proxy's __aop__overriddenMethod alias does not trigger a fatal error. + * the proxy's overriddenMethodOriginalAlias alias does not trigger a fatal error. */ class TestClassWithOverride { diff --git a/tests/Proxy/ClassProxyGeneratorTest.php b/tests/Proxy/ClassProxyGeneratorTest.php index 19c042ea..93d0655e 100644 --- a/tests/Proxy/ClassProxyGeneratorTest.php +++ b/tests/Proxy/ClassProxyGeneratorTest.php @@ -55,7 +55,7 @@ public function testGenerateProxyMethod(string $className, string $methodName): // Proxy uses a trait alias for each intercepted method $this->assertStringContainsString( - "__aop__{$methodName}", + "{$methodName}OriginalAlias", $proxyFileContent, 'Proxy must contain trait alias for intercepted method', ); @@ -301,7 +301,7 @@ public function appendValue(int $value): void /** * Tests that private instance and static methods are intercepted correctly: * - The proxy overrides them with the same `private` visibility - * - The trait-use block aliases each as `private __aop__` + * - The trait-use block aliases each as `private OriginalAlias` * - The method body delegates to the join-point chain * * This is a new capability in the trait-based engine; the old extend-based engine @@ -325,8 +325,8 @@ public function testGenerateInterceptsPrivateMethods(): void $proxyFileContent = "generate(); // Trait alias must exist for each private method - $this->assertStringContainsString('__aop__privateMethod', $proxyFileContent); - $this->assertStringContainsString('__aop__staticSelfPrivate', $proxyFileContent); + $this->assertStringContainsString('privateMethodOriginalAlias', $proxyFileContent); + $this->assertStringContainsString('staticSelfPrivateOriginalAlias', $proxyFileContent); // Proxy methods must keep private visibility $this->assertStringContainsString('private function privateMethod(', $proxyFileContent); @@ -369,7 +369,7 @@ public function testGenerateWithIntroductionOnlyAlwaysIncludesOriginalTrait(): v * methods correctly intercepted in the generated proxy. * * When WeavingTransformer converts a class to a trait, the `use SomeTrait` statement moves - * into the `__AopProxied` trait body. The proxy class itself only uses `__AopProxied`, so + * into the `OriginalTrait` body. The proxy class itself only uses that trait, so * it is unaware of the original trait — but it must still alias and override every method * regardless of whether it came from a used trait or was directly declared. * @@ -387,12 +387,12 @@ public function testGenerateProxyForClassUsingTraitMethods(): void ], ]; - $generator = new ClassProxyGenerator($reflectionClass, 'ClassWithMixedSources__AopProxied', $classAdvices); + $generator = new ClassProxyGenerator($reflectionClass, 'ClassWithMixedSourcesOriginalTrait', $classAdvices); $proxyFileContent = "generate(); // Both trait-defined and own methods must have trait aliases - $this->assertStringContainsString('__aop__publicMethod', $proxyFileContent); - $this->assertStringContainsString('__aop__ownPublicMethod', $proxyFileContent); + $this->assertStringContainsString('publicMethodOriginalAlias', $proxyFileContent); + $this->assertStringContainsString('ownPublicMethodOriginalAlias', $proxyFileContent); // Both must delegate to the join-point chain $this->assertStringContainsString("InterceptorInjector::forMethod(", $proxyFileContent); @@ -417,18 +417,18 @@ public function testGenerateProxyForInheritedMethodDoesNotCreateTraitAlias(): vo ], ]; - $generator = new ClassProxyGenerator($reflectionClass, 'FirstStatic__AopProxied', $classAdvices); + $generator = new ClassProxyGenerator($reflectionClass, 'FirstStaticOriginalTrait', $classAdvices); $proxyFileContent = "generate(); $this->assertStringNotContainsString( - 'FirstStatic__AopProxied::publicMethod as private __aop__publicMethod', + 'FirstStaticOriginalTrait::publicMethod as private publicMethodOriginalAlias', $proxyFileContent, ); $this->assertStringContainsString( "InterceptorInjector::forMethod(", $proxyFileContent, ); - // Inherited instance method must use parent:: first-class callable (no __aop__ alias available) + // Inherited instance method must use parent:: first-class callable (no `OriginalAlias` available) $this->assertStringContainsString( "parent::publicMethod(...)", $proxyFileContent, @@ -453,12 +453,12 @@ public function testGenerateProxyForInheritedStaticMethodUsesParentCallable(): v ], ]; - $generator = new ClassProxyGenerator($reflectionClass, 'FirstStatic__AopProxied', $classAdvices); + $generator = new ClassProxyGenerator($reflectionClass, 'FirstStaticOriginalTrait', $classAdvices); $proxyFileContent = "generate(); // No trait alias for inherited static method $this->assertStringNotContainsString( - '__aop__staticSelfPublic', + 'staticSelfPublicOriginalAlias', $proxyFileContent, 'Inherited static method must not produce a trait alias', ); @@ -539,14 +539,14 @@ public function testTraitAdoptionUsesShortNameWhenSameNamespace(): void ]; // Trait in the same namespace as the proxy (Go\Stubs) - $traitFqcn = 'Go\\Stubs\\First__AopProxied'; + $traitFqcn = 'Go\\Stubs\\FirstOriginalTrait'; $generator = new ClassProxyGenerator($reflectionClass, $traitFqcn, $classAdvices); $output = "generate(); // Must use the short (unqualified) trait name - $this->assertStringContainsString('use First__AopProxied {', $output); - $this->assertStringContainsString('First__AopProxied::publicMethod as private __aop__publicMethod', $output); - $this->assertStringNotContainsString('\\Go\\Stubs\\First__AopProxied', $output); + $this->assertStringContainsString('use FirstOriginalTrait {', $output); + $this->assertStringContainsString('FirstOriginalTrait::publicMethod as private publicMethodOriginalAlias', $output); + $this->assertStringNotContainsString('\\Go\\Stubs\\FirstOriginalTrait', $output); } /** @@ -565,14 +565,14 @@ public function testTraitAdoptionUsesFqcnWhenDifferentNamespace(): void ]; // Trait in a different namespace from the proxy (proxy is in Go\Stubs) - $traitFqcn = 'Other\\Namespace\\First__AopProxied'; + $traitFqcn = 'Other\\Namespace\\FirstOriginalTrait'; $generator = new ClassProxyGenerator($reflectionClass, $traitFqcn, $classAdvices); $output = "generate(); // Must use the FQCN for the trait name - $this->assertStringContainsString('use \\Other\\Namespace\\First__AopProxied {', $output); - $this->assertStringContainsString('\\Other\\Namespace\\First__AopProxied::publicMethod as private __aop__publicMethod', $output); - $this->assertStringNotContainsString('use First__AopProxied {', $output); + $this->assertStringContainsString('use \\Other\\Namespace\\FirstOriginalTrait {', $output); + $this->assertStringContainsString('\\Other\\Namespace\\FirstOriginalTrait::publicMethod as private publicMethodOriginalAlias', $output); + $this->assertStringNotContainsString('use FirstOriginalTrait {', $output); } private static function testAdvice(): GeneratedInterceptor diff --git a/tests/Proxy/EnumProxyGeneratorTest.php b/tests/Proxy/EnumProxyGeneratorTest.php index 502303d7..3bc20a5c 100644 --- a/tests/Proxy/EnumProxyGeneratorTest.php +++ b/tests/Proxy/EnumProxyGeneratorTest.php @@ -32,7 +32,7 @@ class EnumProxyGeneratorTest extends TestCase /** * A proxy for an intercepted instance method on an enum must: * - declare an enum (not a class or trait) - * - alias the intercepted method as private __aop__ + * - alias the intercepted method as private OriginalAlias * - override the method with a per-method static joinpoint dispatch body * - call InterceptorInjector for per-method joinpoint resolution * - dispatch via __invoke($this, ...) for instance methods @@ -40,7 +40,7 @@ class EnumProxyGeneratorTest extends TestCase public function testGenerateProxyEnumMethod(): void { $reflectionClass = new ReflectionClass(StubBackedEnum::class); - $traitName = 'Go\\Stubs\\StubBackedEnum__AopProxied'; + $traitName = 'Go\\Stubs\\StubBackedEnumOriginalTrait'; $classAdvices = [ 'method' => [ 'label' => [self::testAdvice('advisor.StubBackedEnum->label')], @@ -59,8 +59,8 @@ public function testGenerateProxyEnumMethod(): void $this->assertStringContainsString('\Go\Aop\Proxy', $output); // Trait use block with alias must be present - $this->assertStringContainsString('StubBackedEnum__AopProxied', $output); - $this->assertStringContainsString('__aop__label', $output); + $this->assertStringContainsString('StubBackedEnumOriginalTrait', $output); + $this->assertStringContainsString('labelOriginalAlias', $output); // Per-method static joinpoint caching $this->assertStringContainsString('static $__joinPoint', $output); @@ -79,7 +79,7 @@ public function testGenerateProxyEnumMethod(): void public function testGenerateProxyEnumWithStaticMethod(): void { $reflectionClass = new ReflectionClass(StubBackedEnum::class); - $traitName = 'Go\\Stubs\\StubBackedEnum__AopProxied'; + $traitName = 'Go\\Stubs\\StubBackedEnumOriginalTrait'; $classAdvices = [ 'static' => [ 'fromLabel' => [self::testAdvice('advisor.StubBackedEnum->fromLabel')], @@ -89,7 +89,7 @@ public function testGenerateProxyEnumWithStaticMethod(): void $generator = new EnumProxyGenerator($reflectionClass, $traitName, $classAdvices); $output = "generate(); - $this->assertStringContainsString('__aop__fromLabel', $output); + $this->assertStringContainsString('fromLabelOriginalAlias', $output); $this->assertStringContainsString("'fromLabel'", $output); // Static dispatch: static::class as the first argument @@ -108,7 +108,7 @@ public function testGeneratePreservesEnumCases(): void 'method' => ['label' => [self::testAdvice('advisor')]], ]; - $generator = new EnumProxyGenerator($reflectionClass, 'Go\\Stubs\\StubBackedEnum__AopProxied', $classAdvices); + $generator = new EnumProxyGenerator($reflectionClass, 'Go\\Stubs\\StubBackedEnumOriginalTrait', $classAdvices); $output = "generate(); $this->assertStringContainsString("case Active = 'active'", $output); @@ -132,7 +132,7 @@ public function testGeneratePreservesConstantExpressionCaseValues(): void $generator = new EnumProxyGenerator( $reflectionClass, - 'Go\\Stubs\\StubConstExprBackedEnum__AopProxied', + 'Go\\Stubs\\StubConstExprBackedEnumOriginalTrait', $classAdvices, ); $output = "generate(); @@ -155,7 +155,7 @@ public function testGenerateBackedEnumPreservesType(): void 'method' => ['label' => [self::testAdvice('advisor')]], ]; - $generator = new EnumProxyGenerator($reflectionClass, 'Go\\Stubs\\StubBackedEnum__AopProxied', $classAdvices); + $generator = new EnumProxyGenerator($reflectionClass, 'Go\\Stubs\\StubBackedEnumOriginalTrait', $classAdvices); $output = "generate(); // The backed type must appear in the enum declaration @@ -173,7 +173,7 @@ public function testGenerateDoesNotEmitLegacyJoinPointMechanism(): void 'method' => ['label' => [self::testAdvice('advisor')]], ]; - $generator = new EnumProxyGenerator($reflectionClass, 'Go\\Stubs\\StubBackedEnum__AopProxied', $classAdvices); + $generator = new EnumProxyGenerator($reflectionClass, 'Go\\Stubs\\StubBackedEnumOriginalTrait', $classAdvices); $output = $generator->generate(); $this->assertStringNotContainsString('injectJoinPoints', $output); @@ -196,12 +196,12 @@ public function testGenerateDoesNotIncludeBuiltinEnumInterfaces(): void 'method' => ['label' => [self::testAdvice('advisor')]], ]; - $generator = new EnumProxyGenerator($reflectionClass, 'Go\\Stubs\\StubBackedEnum__AopProxied', $classAdvices); + $generator = new EnumProxyGenerator($reflectionClass, 'Go\\Stubs\\StubBackedEnumOriginalTrait', $classAdvices); $output = "generate(); // Extract the implements clause from the enum declaration line and check it directly. // We cannot do a plain assertStringNotContains('BackedEnum') because 'BackedEnum' is - // also a substring of the stub class name 'StubBackedEnum__AopProxied'. + // also a substring of the stub class name 'StubBackedEnumOriginalTrait'. preg_match('/^enum\s+\w+\s*(?::\s*\w+\s*)?implements\s+([^{]+)/m', $output, $matches); $implementsClause = $matches[1] ?? ''; @@ -224,14 +224,14 @@ public function testTraitAdoptionUsesShortNameWhenSameNamespace(): void ]; // Trait in the same namespace as the proxy enum (Go\Stubs) - $traitFqcn = 'Go\\Stubs\\StubBackedEnum__AopProxied'; + $traitFqcn = 'Go\\Stubs\\StubBackedEnumOriginalTrait'; $generator = new EnumProxyGenerator($reflectionClass, $traitFqcn, $classAdvices); $output = "generate(); // Must use the short (unqualified) trait name - $this->assertStringContainsString('use StubBackedEnum__AopProxied {', $output); - $this->assertStringContainsString('StubBackedEnum__AopProxied::label as private __aop__label', $output); - $this->assertStringNotContainsString('\\Go\\Stubs\\StubBackedEnum__AopProxied', $output); + $this->assertStringContainsString('use StubBackedEnumOriginalTrait {', $output); + $this->assertStringContainsString('StubBackedEnumOriginalTrait::label as private labelOriginalAlias', $output); + $this->assertStringNotContainsString('\\Go\\Stubs\\StubBackedEnumOriginalTrait', $output); } /** @@ -248,14 +248,14 @@ public function testTraitAdoptionUsesFqcnWhenDifferentNamespace(): void ]; // Trait in a different namespace from the proxy enum (proxy is in Go\Stubs) - $traitFqcn = 'Other\\Namespace\\StubBackedEnum__AopProxied'; + $traitFqcn = 'Other\\Namespace\\StubBackedEnumOriginalTrait'; $generator = new EnumProxyGenerator($reflectionClass, $traitFqcn, $classAdvices); $output = "generate(); // Must use the FQCN for the trait name - $this->assertStringContainsString('use \\Other\\Namespace\\StubBackedEnum__AopProxied {', $output); - $this->assertStringContainsString('\\Other\\Namespace\\StubBackedEnum__AopProxied::label as private __aop__label', $output); - $this->assertStringNotContainsString('use StubBackedEnum__AopProxied {', $output); + $this->assertStringContainsString('use \\Other\\Namespace\\StubBackedEnumOriginalTrait {', $output); + $this->assertStringContainsString('\\Other\\Namespace\\StubBackedEnumOriginalTrait::label as private labelOriginalAlias', $output); + $this->assertStringNotContainsString('use StubBackedEnumOriginalTrait {', $output); } /** @@ -276,14 +276,14 @@ public function testGenerateFiltersOutBuiltinEnumMethods(): void ], ]; - $generator = new EnumProxyGenerator($reflectionClass, 'Go\\Stubs\\StubBackedEnum__AopProxied', $classAdvices); + $generator = new EnumProxyGenerator($reflectionClass, 'Go\\Stubs\\StubBackedEnumOriginalTrait', $classAdvices); $output = "generate(); // label is intercepted; built-ins are not - $this->assertStringContainsString('__aop__label', $output); - $this->assertStringNotContainsString('__aop__cases', $output); - $this->assertStringNotContainsString('__aop__from', $output); - $this->assertStringNotContainsString('__aop__tryFrom', $output); + $this->assertStringContainsString('labelOriginalAlias', $output); + $this->assertStringNotContainsString('casesOriginalAlias', $output); + $this->assertStringNotContainsString('fromOriginalAlias', $output); + $this->assertStringNotContainsString('tryFromOriginalAlias', $output); } private static function testAdvice(string $advisorId): GeneratedInterceptor diff --git a/tests/Proxy/Generator/ClassGeneratorTest.php b/tests/Proxy/Generator/ClassGeneratorTest.php index 634777d6..8ade2a60 100644 --- a/tests/Proxy/Generator/ClassGeneratorTest.php +++ b/tests/Proxy/Generator/ClassGeneratorTest.php @@ -95,12 +95,12 @@ public function testExtendsExplicitlyRootedGlobalParentStaysFullyQualified(): vo public function testUsesExplicitlyRootedGlobalTraitStaysFullyQualified(): void { $gen = new ClassGenerator('MyClass', 'My\Namespace', [], null); - $gen->addTraits(['\GlobalHelperTrait', 'MyClass__AopProxied']); + $gen->addTraits(['\GlobalHelperTrait', 'MyClassOriginalTrait']); $output = $gen->generate(); $this->assertStringContainsString('\GlobalHelperTrait', $output); // Deliberate short names keep referring to the class' own namespace - $this->assertStringContainsString('MyClass__AopProxied', $output); - $this->assertStringNotContainsString('\MyClass__AopProxied', $output); + $this->assertStringContainsString('MyClassOriginalTrait', $output); + $this->assertStringNotContainsString('\MyClassOriginalTrait', $output); } public function testWithMethod(): void diff --git a/tests/Proxy/Part/InterceptedConstructorGeneratorTest.php b/tests/Proxy/Part/InterceptedConstructorGeneratorTest.php index 312d2bb4..c6d3c649 100644 --- a/tests/Proxy/Part/InterceptedConstructorGeneratorTest.php +++ b/tests/Proxy/Part/InterceptedConstructorGeneratorTest.php @@ -86,8 +86,8 @@ public static function dataGenerator(): array /** * When the constructor belongs to the class being proxied (trait-based engine), it is placed in the - * trait body and aliased as __aop____construct. The generated constructor must call - * $this->__aop____construct() rather than parent::__construct(), which would fail because the new + * trait body and aliased as __constructOriginalAlias. The generated constructor must call + * $this->__constructOriginalAlias() rather than parent::__construct(), which would fail because the new * proxy class has no parent. */ public function testGenerateWithConstructorInTrait(): void @@ -102,9 +102,9 @@ public function testGenerateWithConstructorInTrait(): void $generatedCode = $generator->generate(); $this->assertStringContainsString( - '$this->__aop____construct(', + '$this->__constructOriginalAlias(', $generatedCode, - 'When constructorIsInTrait=true, must call $this->__aop____construct() instead of parent::__construct()', + 'When constructorIsInTrait=true, must call $this->__constructOriginalAlias() instead of parent::__construct()', ); $this->assertStringNotContainsString( 'parent::__construct', diff --git a/tests/Proxy/TraitProxyGeneratorTest.php b/tests/Proxy/TraitProxyGeneratorTest.php index 462d2d8d..d7742e0a 100644 --- a/tests/Proxy/TraitProxyGeneratorTest.php +++ b/tests/Proxy/TraitProxyGeneratorTest.php @@ -25,8 +25,8 @@ * * Unlike ClassProxyGenerator which generates a proxy *class*, TraitProxyGenerator * generates a child *trait* that: - * - uses the renamed original trait (Foo__AopProxied) - * - aliases each intercepted method as private __aop__ + * - uses the renamed original trait (FooOriginalTrait) + * - aliases each intercepted method as private OriginalAlias * - overrides each intercepted method with a per-method static joinpoint dispatch */ class TraitProxyGeneratorTest extends TestCase @@ -34,7 +34,7 @@ class TraitProxyGeneratorTest extends TestCase /** * A trait proxy for an intercepted instance method must: * - declare a trait (not a class) - * - alias the intercepted method as private __aop__ + * - alias the intercepted method as private OriginalAlias * - override the method with a per-method static $__joinPoint body * - call InterceptorInjector (not ClassProxyGenerator::injectJoinPoints) * - dispatch via __invoke($this, ...) for instance methods @@ -50,7 +50,7 @@ public function testGenerateTraitWithInterceptedInstanceMethod(): void $generator = new TraitProxyGenerator( $reflectionTrait, - 'Go\\Stubs\\TraitAliasProxied__AopProxied', + 'Go\\Stubs\\TraitAliasProxiedOriginalTrait', $traitAdvices, ); @@ -61,8 +61,8 @@ public function testGenerateTraitWithInterceptedInstanceMethod(): void $this->assertStringNotContainsString('class TraitAliasProxied', $output); // Parent trait and private alias must appear in the use block - $this->assertStringContainsString('TraitAliasProxied__AopProxied', $output); - $this->assertStringContainsString('__aop__publicMethod', $output); + $this->assertStringContainsString('TraitAliasProxiedOriginalTrait', $output); + $this->assertStringContainsString('publicMethodOriginalAlias', $output); // Method body must use per-method static joinpoint caching $this->assertStringContainsString('static $__joinPoint', $output); @@ -89,14 +89,14 @@ public function testGenerateTraitWithInterceptedStaticMethod(): void $generator = new TraitProxyGenerator( $reflectionTrait, - 'Go\\Stubs\\TraitAliasProxied__AopProxied', + 'Go\\Stubs\\TraitAliasProxiedOriginalTrait', $traitAdvices, ); $output = "generate(); $this->assertStringContainsString('trait TraitAliasProxied', $output); - $this->assertStringContainsString('__aop__staticPublicMethod', $output); + $this->assertStringContainsString('staticPublicMethodOriginalAlias', $output); $this->assertStringContainsString("'staticPublicMethod'", $output); @@ -123,15 +123,15 @@ public function testGenerateTraitWithMultipleInterceptedMethods(): void $generator = new TraitProxyGenerator( $reflectionTrait, - 'Go\\Stubs\\TraitAliasProxied__AopProxied', + 'Go\\Stubs\\TraitAliasProxiedOriginalTrait', $traitAdvices, ); $output = "generate(); - $this->assertStringContainsString('__aop__publicMethod', $output); - $this->assertStringContainsString('__aop__protectedMethod', $output); - $this->assertStringContainsString('__aop__staticPublicMethod', $output); + $this->assertStringContainsString('publicMethodOriginalAlias', $output); + $this->assertStringContainsString('protectedMethodOriginalAlias', $output); + $this->assertStringContainsString('staticPublicMethodOriginalAlias', $output); // Three separate injector calls (one per intercepted method) $this->assertSame(2, substr_count($output, 'InterceptorInjector::forMethod')); @@ -154,7 +154,7 @@ public function testGenerateDoesNotEmitLegacyJoinPointMechanism(): void $generator = new TraitProxyGenerator( $reflectionTrait, - 'Go\\Stubs\\TraitAliasProxied__AopProxied', + 'Go\\Stubs\\TraitAliasProxiedOriginalTrait', $traitAdvices, ); @@ -178,7 +178,7 @@ public function testMethodBodyUsesPerMethodStaticCaching(): void $generator = new TraitProxyGenerator( $reflectionTrait, - 'Go\\Stubs\\TraitAliasProxied__AopProxied', + 'Go\\Stubs\\TraitAliasProxiedOriginalTrait', $traitAdvices, ); @@ -203,7 +203,7 @@ public function testGenerateTraitWithInterceptedProperty(): void $generator = new TraitProxyGenerator( $reflectionTrait, - 'Go\\Stubs\\TraitAliasProxied__AopProxied', + 'Go\\Stubs\\TraitAliasProxiedOriginalTrait', $traitAdvices, ); @@ -228,7 +228,7 @@ public function testGenerateTraitWithClassTypedPropertyUsesFullyQualifiedTypeInF $generator = new TraitProxyGenerator( $reflectionTrait, - 'Go\\Stubs\\TraitWithClassTypedProperty__AopProxied', + 'Go\\Stubs\\TraitWithClassTypedPropertyOriginalTrait', $traitAdvices, ); @@ -254,14 +254,14 @@ public function testTraitAdoptionUsesShortNameWhenSameNamespace(): void ]; // Parent trait in the same namespace as the proxy trait (Go\Stubs) - $parentTraitFqcn = 'Go\\Stubs\\TraitAliasProxied__AopProxied'; + $parentTraitFqcn = 'Go\\Stubs\\TraitAliasProxiedOriginalTrait'; $generator = new TraitProxyGenerator($reflectionTrait, $parentTraitFqcn, $traitAdvices); $output = "generate(); // Must use the short (unqualified) parent trait name - $this->assertStringContainsString('use TraitAliasProxied__AopProxied {', $output); - $this->assertStringContainsString('TraitAliasProxied__AopProxied::publicMethod as private __aop__publicMethod', $output); - $this->assertStringNotContainsString('\\Go\\Stubs\\TraitAliasProxied__AopProxied', $output); + $this->assertStringContainsString('use TraitAliasProxiedOriginalTrait {', $output); + $this->assertStringContainsString('TraitAliasProxiedOriginalTrait::publicMethod as private publicMethodOriginalAlias', $output); + $this->assertStringNotContainsString('\\Go\\Stubs\\TraitAliasProxiedOriginalTrait', $output); } /** @@ -278,14 +278,14 @@ public function testTraitAdoptionUsesFqcnWhenDifferentNamespace(): void ]; // Parent trait in a different namespace from the proxy trait (proxy is in Go\Stubs) - $parentTraitFqcn = 'Other\\Namespace\\TraitAliasProxied__AopProxied'; + $parentTraitFqcn = 'Other\\Namespace\\TraitAliasProxiedOriginalTrait'; $generator = new TraitProxyGenerator($reflectionTrait, $parentTraitFqcn, $traitAdvices); $output = "generate(); // Must use the FQCN for the parent trait name - $this->assertStringContainsString('use \\Other\\Namespace\\TraitAliasProxied__AopProxied {', $output); - $this->assertStringContainsString('\\Other\\Namespace\\TraitAliasProxied__AopProxied::publicMethod as private __aop__publicMethod', $output); - $this->assertStringNotContainsString('use TraitAliasProxied__AopProxied {', $output); + $this->assertStringContainsString('use \\Other\\Namespace\\TraitAliasProxiedOriginalTrait {', $output); + $this->assertStringContainsString('\\Other\\Namespace\\TraitAliasProxiedOriginalTrait::publicMethod as private publicMethodOriginalAlias', $output); + $this->assertStringNotContainsString('use TraitAliasProxiedOriginalTrait {', $output); } private static function testAdvice(string $advisorId): GeneratedInterceptor diff --git a/tests/Stubs/TraitAliasProxy.php b/tests/Stubs/TraitAliasProxy.php index b34e2357..66a6cb44 100644 --- a/tests/Stubs/TraitAliasProxy.php +++ b/tests/Stubs/TraitAliasProxy.php @@ -15,27 +15,27 @@ /** * Simulates the AOP proxy class generated by ClassProxyGenerator: * - Uses the original-body trait (TraitAliasProxied) - * - Aliases each intercepted method as private __aop__ + * - Aliases each intercepted method as private OriginalAlias * - Overrides the public-facing methods with a sentinel (-1) so tests can * confirm that the invocation goes through the alias, not the override. */ class TraitAliasProxy { use TraitAliasProxied { - TraitAliasProxied::publicMethod as private __aop__publicMethod; - TraitAliasProxied::getObjectId as private __aop__getObjectId; - TraitAliasProxied::protectedMethod as private __aop__protectedMethod; - TraitAliasProxied::privateMethod as private __aop__privateMethod; - TraitAliasProxied::variadicArgsTest as private __aop__variadicArgsTest; - TraitAliasProxied::passByReference as private __aop__passByReference; - TraitAliasProxied::staticPassByReference as private __aop__staticPassByReference; - TraitAliasProxied::staticPublicMethod as private __aop__staticPublicMethod; - TraitAliasProxied::staticVariadicArgsTest as private __aop__staticVariadicArgsTest; + TraitAliasProxied::publicMethod as private publicMethodOriginalAlias; + TraitAliasProxied::getObjectId as private getObjectIdOriginalAlias; + TraitAliasProxied::protectedMethod as private protectedMethodOriginalAlias; + TraitAliasProxied::privateMethod as private privateMethodOriginalAlias; + TraitAliasProxied::variadicArgsTest as private variadicArgsTestOriginalAlias; + TraitAliasProxied::passByReference as private passByReferenceOriginalAlias; + TraitAliasProxied::staticPassByReference as private staticPassByReferenceOriginalAlias; + TraitAliasProxied::staticPublicMethod as private staticPublicMethodOriginalAlias; + TraitAliasProxied::staticVariadicArgsTest as private staticVariadicArgsTestOriginalAlias; } /** * Overridden by the proxy — returns a sentinel so tests can prove the invocation - * routes through __aop__publicMethod (the trait alias) and not this override. + * routes through publicMethodOriginalAlias (the trait alias) and not this override. */ public function publicMethod(): int { @@ -45,57 +45,57 @@ public function publicMethod(): int /** @see publicMethod */ public static function staticPublicMethod(): string { - return self::__aop__staticPublicMethod(); + return self::staticPublicMethodOriginalAlias(); } /** - * Creates a first-class callable to the private __aop__getObjectId alias. + * Creates a first-class callable to the private getObjectIdOriginalAlias alias. * Used by tests to verify that the static singleton joinpoint correctly rebinds * $this to each new caller instance via Closure::call(). */ public function createGetObjectIdCallable(): \Closure { - return $this->__aop__getObjectId(...); + return $this->getObjectIdOriginalAlias(...); } /** - * Creates a first-class callable to the private __aop__staticPublicMethod alias. + * Creates a first-class callable to the private staticPublicMethodOriginalAlias alias. * Used by tests to verify that the static singleton joinpoint correctly handles LSB * via forward_static_call(). */ public static function createStaticPublicMethodCallable(): \Closure { - return self::__aop__staticPublicMethod(...); + return self::staticPublicMethodOriginalAlias(...); } /** - * Returns a first-class callable to the private __aop__ alias for the given instance method. + * Returns a first-class callable to the private `OriginalAlias` for the given instance method. * Used by unit tests to provide the required callable argument to invocation constructors. */ public function getCallableFor(string $method): \Closure { return match ($method) { - 'publicMethod' => $this->__aop__publicMethod(...), - 'getObjectId' => $this->__aop__getObjectId(...), - 'protectedMethod' => $this->__aop__protectedMethod(...), - 'privateMethod' => $this->__aop__privateMethod(...), - 'variadicArgsTest' => $this->__aop__variadicArgsTest(...), - 'passByReference' => $this->__aop__passByReference(...), - default => throw new \InvalidArgumentException("No __aop__ alias for '$method'"), + 'publicMethod' => $this->publicMethodOriginalAlias(...), + 'getObjectId' => $this->getObjectIdOriginalAlias(...), + 'protectedMethod' => $this->protectedMethodOriginalAlias(...), + 'privateMethod' => $this->privateMethodOriginalAlias(...), + 'variadicArgsTest' => $this->variadicArgsTestOriginalAlias(...), + 'passByReference' => $this->passByReferenceOriginalAlias(...), + default => throw new \InvalidArgumentException("No OriginalAlias for '$method'"), }; } /** - * Returns a first-class callable to the private __aop__ alias for the given static method. + * Returns a first-class callable to the private `OriginalAlias` for the given static method. * Used by unit tests to provide the required callable argument to invocation constructors. */ public static function getStaticCallableFor(string $method): \Closure { return match ($method) { - 'staticPassByReference' => self::__aop__staticPassByReference(...), - 'staticPublicMethod' => self::__aop__staticPublicMethod(...), - 'staticVariadicArgsTest' => self::__aop__staticVariadicArgsTest(...), - default => throw new \InvalidArgumentException("No static __aop__ alias for '$method'"), + 'staticPassByReference' => self::staticPassByReferenceOriginalAlias(...), + 'staticPublicMethod' => self::staticPublicMethodOriginalAlias(...), + 'staticVariadicArgsTest' => self::staticVariadicArgsTestOriginalAlias(...), + default => throw new \InvalidArgumentException("No static OriginalAlias for '$method'"), }; } }