From 2c0ba488d8d362380a1e01c7dca6bb1d4a43c0bf Mon Sep 17 00:00:00 2001 From: icefox Date: Mon, 5 Jan 2026 10:40:49 -0300 Subject: [PATCH] update readme --- README.md | 208 ++++++++++++++++--------------------------------- src/Aspect.php | 19 ----- 2 files changed, 67 insertions(+), 160 deletions(-) delete mode 100644 src/Aspect.php diff --git a/README.md b/README.md index 240fd69..55d84a0 100644 --- a/README.md +++ b/README.md @@ -1,189 +1,115 @@ -# PHP AOP - Proof of Concept +# PHP AOP A minimalistic Aspect-Oriented Programming (AOP) implementation for PHP that uses attributes and Composer's autoloading system to weave aspects into classes at runtime. -## Features - -- **Attribute-based AOP**: Use custom attributes to mark methods for aspect weaving -- **Builder Pattern Configuration**: Fluent API for configuring aspects and classes -- **Selective Class Loading**: Only process specified classes, avoiding vendor code -- **Multiple Aspect Support**: Register multiple aspect attributes and control which ones are processed -- **Reflection-based Weaving**: Uses native PHP Reflection for robust type handling -- **Complex Type Support**: Union types, nullable, variadic, by-reference parameters -- **Automatic proxy generation**: Hooks into Composer's class loading to generate proxy classes -- **Before/After execution**: Methods marked with aspects will execute interceptor code -- **Works with public and protected methods**: Full support for method visibility -- **Smart Caching**: Generated proxies cached for performance - -## How It Works - -### Two-Pass Reflection Architecture - -1. **Configuration**: Use builder pattern to specify which aspects and classes to process -2. **Class Loading Hook**: Custom autoloader intercepts class loading for registered classes -3. **Use Statement Detection**: Checks if source file imports registered aspect attributes -4. **Two-Pass Loading**: - - **Pass 1 (String + eval)**: Rename class, load into memory for reflection analysis - - **Pass 2 (Reflection)**: Use native PHP Reflection to inspect method signatures and attributes -5. **Proxy Generation**: Create self-contained proxy file with: - - Original class with renamed identifier - - Proxy class extending original with aspect methods overridden -6. **Caching**: Generated proxies cached to `/tmp/php-aop-cache` for performance - ## Installation ```bash composer install ``` -## Configuration +## Usage -Configure the AspectLoader using the builder pattern in `src/bootstrap.php`: + +### Create an Aspect class: ```php -withAspects([Aspect::class]) // Which aspect attributes to process - ->withClasses([ // Which classes to weave (avoids vendor code) - 'Example\Calculator', - 'Example\ComplexExample', - ]) +#[Attribute(Attribute::TARGET_METHOD)] +class MyAspect +{ + public function before(object|string $target, mixed ...$args): void + { + // $target is the class (for static methods) or object (for instance methods) + // Access all parameters before the wrapped function is called + var_dump($args); + } + + public function after(object|string $target, mixed $result): mixed + { + // Access the returned value with $result + var_dump($result); + // modify the return value, or simply return the unmodified value + return $result; + } +} +``` + +### Register all Aspect classes on application bootstrap: + +``` +``` +$cacheDir = +$useCache = false; + +$weaver = new AspectWeaver( + [MyAspect::class], + // provide a cache directory to avoid reprocessing class + sys_get_temp_dir() . '/cache/php-aop-cache'; + // whether to use cache. Disable in dev, enable in production + false, + // PSR compatible log, for debugging. + new NullLogger(), +); + +// Provide with classes or namespaces will be evaluated for Aspects +// trying to evaluate all vendor classes will result in a bad time +// at minimum, use withNamespaces to filter your application +// classes will be evaluated if they match the list of classes OR belong to the namespace +$loader = AspectBuilder::begin() + // list of class names + ->withClasses([ MyClass::class ]) + // namespace prefix + ->withNamespaces([ "MyApplication\" ]) + ->build($weaver) ->register(); ``` -## Usage - -### 1. Mark methods with aspect attributes +### Create a class and mark a method with your Aspect class: ```php add(5, 3); +/// MyAspect@before will var_dump([5, 3]) +/// MyClass@add will echo "Adding 5 + 3\n" +/// MyAspect@after will var_dump(8) ``` -## Running the Demo - -```bash -php example/demo.php -``` - -Expected output: -``` -=== PHP AOP Demo === - -1. Calling add() with Aspect attribute: -before -Adding 5 + 3 -after -Result: 8 - -2. Calling multiply() with Aspect attribute: -before -Multiplying 4 * 7 -after -Result: 28 - -3. Calling subtract() WITHOUT Aspect attribute: -Subtracting 10 - 3 -Result: 7 - -4. Calling protected divide() method (with Aspect) via public wrapper: -before -Dividing 20 / 4 -after -Result: 5 - -=== Demo Complete === -``` - -## Architecture - -### Components - -- **`Aspect.php`**: The attribute class that marks methods for aspect weaving -- **`AspectWeaver.php`**: Parses source files and generates proxy classes with aspect behavior -- **`AspectLoader.php`**: Hooks into Composer's autoloader to intercept class loading -- **`bootstrap.php`**: Initializes the AspectLoader when the autoloader is included - -### Proxy Generation Strategy +## Proxy Generation Strategy The weaver uses a two-class approach: 1. **Original Class (renamed)**: The original class is included in the proxy file with a modified name (e.g., `__AopOriginal_Calculator`) 2. **Proxy Class**: A new class with the original name that extends the renamed original class and overrides methods marked with `#[Aspect]` -This approach ensures: -- No modification of source files -- Full type safety and IDE support -- Debuggability -- No runtime performance overhead (after initial proxy generation) +## TODO -## Limitations (POC) +Features that may or may not exist in the future -This is a proof of concept with the following limitations: - -- Only supports `before` and `after` behavior (no around, exception handling, etc.) -- Simple regex-based parsing (not a full PHP parser) -- Cache clearing requires manual deletion of `/tmp/php-aop-cache` -- No support for static methods with static variables -- No support for complex method signatures (references, variadic parameters may have issues) - -## Extending - -To extend this POC to support more advanced AOP features: - -1. **Custom Aspects**: Create different attribute classes with parameters -2. **Joinpoint Information**: Pass method name, arguments, and execution context to aspect code -3. **Around Advice**: Allow aspects to control method execution -4. **Exception Handling**: Add support for after-throwing and after-returning advice -5. **Performance**: Use a proper PHP parser (like nikic/php-parser) for more robust code analysis +1. **Around Advice**: Allow aspects to control method execution +2. Demonstrate exception handling +3. Demonstrate Aspect constructor arguments ## References diff --git a/src/Aspect.php b/src/Aspect.php deleted file mode 100644 index 7bb8cc9..0000000 --- a/src/Aspect.php +++ /dev/null @@ -1,19 +0,0 @@ -