|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +**Go! AOP Framework** — an Aspect-Oriented Programming (AOP) framework for PHP 8.4+. It intercepts PHP class/method/function execution transparently by transforming source code at load time via a custom PHP stream wrapper, without requiring PECL extensions, annotations at runtime, or eval. |
| 8 | + |
| 9 | +Package: `goaop/framework` | Namespace root: `Go\` | PHP: `^8.4.0` |
| 10 | + |
| 11 | +## Commands |
| 12 | + |
| 13 | +```bash |
| 14 | +# Install dependencies |
| 15 | +composer install |
| 16 | + |
| 17 | +# Run full test suite |
| 18 | +./vendor/bin/phpunit |
| 19 | + |
| 20 | +# Run a single test file |
| 21 | +./vendor/bin/phpunit tests/Go/Core/ContainerTest.php |
| 22 | + |
| 23 | +# Run a single test method |
| 24 | +./vendor/bin/phpunit --filter testMethodName tests/Go/Core/ContainerTest.php |
| 25 | + |
| 26 | +# Static analysis (level 4, src/ only) |
| 27 | +./vendor/bin/phpstan analyze |
| 28 | + |
| 29 | +# CLI debugging tools |
| 30 | +./bin/aspect debug:advisors [class] |
| 31 | +./bin/aspect debug:pointcuts [expression] |
| 32 | +``` |
| 33 | + |
| 34 | +## Architecture |
| 35 | + |
| 36 | +The framework works by intercepting PHP's class loading pipeline. When a class is loaded, the stream wrapper transforms its source code to inject interception hooks, then stores the result in a cache directory. The transformed class contains calls into the advisor chain for each matched join point. |
| 37 | + |
| 38 | +### Initialization flow |
| 39 | + |
| 40 | +1. **`AspectKernel::init()`** (`src/Core/AspectKernel.php`) — singleton, registers stream wrapper, builds transformer chain, calls `configureAop()` where users register aspects |
| 41 | +2. **`SourceTransformingLoader::register()`** (`src/Instrument/ClassLoading/SourceTransformingLoader.php`) — PHP stream wrapper that intercepts `include`/`require` via the `go-aop-php://` protocol |
| 42 | +3. **`AopComposerLoader::init()`** (`src/Instrument/ClassLoading/AopComposerLoader.php`) — hooks into Composer's autoloader to redirect loads through the stream wrapper |
| 43 | +4. **`CachingTransformer`** — outer transformer that manages cache; on cache miss, invokes the inner transformers and writes the result |
| 44 | + |
| 45 | +### Transformer chain (inner, registered in `AspectKernel::registerTransformers()`) |
| 46 | + |
| 47 | +Applied in order for each loaded file: |
| 48 | +- `ConstructorExecutionTransformer` — transforms `new` expressions (when `INTERCEPT_INITIALIZATIONS` feature enabled) |
| 49 | +- `FilterInjectorTransformer` — wraps `include`/`require` (when `INTERCEPT_INCLUDES` enabled) |
| 50 | +- `SelfValueTransformer` — rewrites `self::` to use the concrete proxy class |
| 51 | +- `WeavingTransformer` — main transformer; uses `AdviceMatcher` to find applicable advices and `CachedAspectLoader` for aspect metadata, then delegates to proxy generators |
| 52 | +- `MagicConstantTransformer` — rewrites `__FILE__`/`__DIR__` so they resolve to the original file, not the cached proxy |
| 53 | + |
| 54 | +Each transformer returns `TransformerResultEnum`: `RESULT_TRANSFORMED`, `RESULT_ABSTAIN`, or `RESULT_ABORTED`. |
| 55 | + |
| 56 | +### Proxy generation (`src/Proxy/`) |
| 57 | + |
| 58 | +- `ClassProxyGenerator` — generates a proxy subclass with overridden interceptable methods |
| 59 | +- `FunctionProxyGenerator` — generates function wrappers |
| 60 | +- `TraitProxyGenerator` — generates trait proxies |
| 61 | +- `src/Proxy/Part/` — individual code-generation components (method lists, parameter lists, joinpoint property injection) |
| 62 | + |
| 63 | +### AOP core (`src/Aop/`) |
| 64 | + |
| 65 | +- `src/Aop/Intercept/` — interfaces: `Joinpoint`, `Invocation`, `MethodInvocation`, `ConstructorInvocation`, `FunctionInvocation`, `FieldAccess` |
| 66 | +- `src/Aop/Framework/` — concrete invocation implementations used at runtime by proxies; `AbstractMethodInvocation`, `DynamicClosureMethodInvocation`, `StaticClosureMethodInvocation`, `ClassFieldAccess`, etc. |
| 67 | +- `src/Aop/Pointcut/` — LALR pointcut grammar (`PointcutGrammar`, `PointcutParser`, `PointcutLexer`, `PointcutParseTable`) and pointcut combinators (`AndPointcut`, `OrPointcut`, `NotPointcut`, `NamePointcut`, `AttributePointcut`, etc.) |
| 68 | +- `src/Lang/Attribute/` — PHP 8 attributes for declaring aspects and advice: `#[Aspect]`, `#[Before]`, `#[After]`, `#[Around]`, `#[AfterThrowing]`, `#[Pointcut]`, `#[DeclareError]`, `#[DeclareParents]` |
| 69 | +- `src/Aop/Features.php` — bitmask enum for optional features (`INTERCEPT_FUNCTIONS`, `INTERCEPT_INITIALIZATIONS`, `INTERCEPT_INCLUDES`) |
| 70 | + |
| 71 | +### Container and aspect loading (`src/Core/`) |
| 72 | + |
| 73 | +- `Container.php` — DI container with `add()` (by class-string or key), `getService()`, `addLazyService()` (Closure), and automatic tagging by interface |
| 74 | +- `AspectLoader` / `CachedAspectLoader` — scan aspect classes for pointcut/advice attributes and produce `Advisor` instances |
| 75 | +- `AttributeAspectLoaderExtension` — handles PHP 8 attribute-based aspect definitions |
| 76 | +- `AdviceMatcher` — given a class reflector, returns the set of applicable advisors keyed by join point |
| 77 | + |
| 78 | +### Bridge |
| 79 | + |
| 80 | +`src/Bridge/Doctrine/MetadataLoadInterceptor.php` — workaround for Doctrine ORM entity weaving (Doctrine loads metadata before the kernel can intercept classes). |
| 81 | + |
| 82 | +## Test conventions |
| 83 | + |
| 84 | +- Tests mirror the `src/` structure under `tests/Go/` |
| 85 | +- Functional/integration tests live in `tests/Go/Functional/` |
| 86 | +- Test fixtures (stub classes for weaving) live in `tests/Go/Stubs/` and `tests/Fixtures/project/src/` (autoloaded as `Go\Tests\TestProject\`) |
| 87 | +- PHPUnit 11, bootstrap is `vendor/autoload.php` (no separate test bootstrap) |
| 88 | +- PHPStan baseline is `phpstan-baseline.php` — add new accepted errors there rather than inline suppression when appropriate |
0 commit comments