No description
| src | ||
| tests | ||
| .gitignore | ||
| composer.json | ||
| phpunit.xml | ||
| README.md | ||
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
- Configuration: Use builder pattern to specify which aspects and classes to process
- Class Loading Hook: Custom autoloader intercepts class loading for registered classes
- Use Statement Detection: Checks if source file imports registered aspect attributes
- 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
- Proxy Generation: Create self-contained proxy file with:
- Original class with renamed identifier
- Proxy class extending original with aspect methods overridden
- Caching: Generated proxies cached to
/tmp/php-aop-cachefor performance
Installation
composer install
Configuration
Configure the AspectLoader using the builder pattern in src/bootstrap.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
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
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
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 weavingAspectWeaver.php: Parses source files and generates proxy classes with aspect behaviorAspectLoader.php: Hooks into Composer's autoloader to intercept class loadingbootstrap.php: Initializes the AspectLoader when the autoloader is included
Proxy Generation Strategy
The weaver uses a two-class approach:
- Original Class (renamed): The original class is included in the proxy file with a modified name (e.g.,
__AopOriginal_Calculator) - 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
beforeandafterbehavior (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:
- Custom Aspects: Create different attribute classes with parameters
- Joinpoint Information: Pass method name, arguments, and execution context to aspect code
- Around Advice: Allow aspects to control method execution
- Exception Handling: Add support for after-throwing and after-returning advice
- Performance: Use a proper PHP parser (like nikic/php-parser) for more robust code analysis