117 lines
2.9 KiB
Markdown
117 lines
2.9 KiB
Markdown
# 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.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
composer install
|
|
```
|
|
|
|
## Usage
|
|
|
|
|
|
### Create an Aspect class:
|
|
|
|
```php
|
|
namespace Tests\Aspects;
|
|
|
|
use Attribute;
|
|
|
|
#[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();
|
|
```
|
|
|
|
### Create a class and mark a method with your Aspect class:
|
|
|
|
```php
|
|
<?php
|
|
|
|
class MyClass
|
|
{
|
|
#[MyAspect]
|
|
public function add(int $a, int $b): int
|
|
{
|
|
echo "Adding {$a} + {$b}\n";
|
|
return $a + $b;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Use the class normally
|
|
|
|
```php
|
|
<?php
|
|
|
|
$object = new MyClass();
|
|
$result = $calc->add(5, 3);
|
|
/// MyAspect@before will var_dump([5, 3])
|
|
/// MyClass@add will echo "Adding 5 + 3\n"
|
|
/// MyAspect@after will var_dump(8)
|
|
```
|
|
|
|
## 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]`
|
|
|
|
## TODO
|
|
|
|
Features that may or may not exist in the future
|
|
|
|
1. **Around Advice**: Allow aspects to control method execution
|
|
2. Demonstrate exception handling
|
|
3. Demonstrate Aspect constructor arguments
|
|
|
|
## References
|
|
|
|
- [okapi-web/php-aop](https://github.com/okapi-web/php-aop)
|
|
- [goaop/framework](https://github.com/goaop/framework)
|