No description
Find a file
2025-12-22 17:54:16 -03:00
src . 2025-12-22 17:54:16 -03:00
tests . 2025-12-22 17:54:16 -03:00
.gitignore . 2025-12-22 17:54:16 -03:00
composer.json . 2025-12-22 17:54:16 -03:00
phpunit.xml . 2025-12-22 17:54:16 -03:00
README.md . 2025-12-22 17:54:16 -03:00

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

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 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