.
This commit is contained in:
commit
a5ce423afe
30 changed files with 1807 additions and 0 deletions
191
README.md
Normal file
191
README.md
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
# PHP AOP - Proof of Concept
|
||||
|
||||
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
|
||||
|
||||
Configure the AspectLoader using the builder pattern in `src/bootstrap.php`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Fnzr\Aop\AspectLoader;
|
||||
use Fnzr\Aop\Aspect;
|
||||
|
||||
(new AspectLoader())
|
||||
->withAspects([Aspect::class]) // Which aspect attributes to process
|
||||
->withClasses([ // Which classes to weave (avoids vendor code)
|
||||
'Example\Calculator',
|
||||
'Example\ComplexExample',
|
||||
])
|
||||
->register();
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### 1. Mark methods with aspect attributes
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Example;
|
||||
|
||||
use Fnzr\Aop\Aspect;
|
||||
|
||||
class Calculator
|
||||
{
|
||||
#[Aspect]
|
||||
public function add(int $a, int $b): int
|
||||
{
|
||||
echo "Adding {$a} + {$b}\n";
|
||||
return $a + $b;
|
||||
}
|
||||
|
||||
#[Aspect]
|
||||
protected function divide(float $a, float $b): float
|
||||
{
|
||||
echo "Dividing {$a} / {$b}\n";
|
||||
return $a / $b;
|
||||
}
|
||||
|
||||
// This method will NOT have aspect behavior
|
||||
public function subtract(int $a, int $b): int
|
||||
{
|
||||
return $a - $b;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Use the class normally
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
use Example\Calculator;
|
||||
|
||||
$calc = new Calculator();
|
||||
|
||||
// This will output:
|
||||
// before
|
||||
// Adding 5 + 3
|
||||
// after
|
||||
$result = $calc->add(5, 3);
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
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)
|
||||
|
||||
## Limitations (POC)
|
||||
|
||||
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
|
||||
|
||||
## References
|
||||
|
||||
- [okapi-web/php-aop](https://github.com/okapi-web/php-aop)
|
||||
- [goaop/framework](https://github.com/goaop/framework)
|
||||
Loading…
Add table
Add a link
Reference in a new issue