Boost PHP Performance with Webman AOP Plugin – Runtime Proxy, No Files

This article introduces a high‑performance, high‑availability AOP plugin for the Webman framework that works on PHP 8, explains its features, installation, configuration, usage examples, custom aspect creation methods, notification types, execution order, JoinPoint API, and provides complete code samples for caching and transaction aspects.

Open Source Tech Hub
Open Source Tech Hub
Open Source Tech Hub
Boost PHP Performance with Webman AOP Plugin – Runtime Proxy, No Files

Overview

This high‑performance, high‑availability AOP (Aspect‑Oriented Programming) plugin is built for the Webman framework, runs on PHP 8, uses runtime proxy without generating files, and is released under the MIT license.

Features

Standard AOP types : supports Before, After, Around, AfterReturning, AfterThrowing.

Runtime proxy : high‑performance dynamic proxy implementation, no file generation.

High availability : comprehensive error handling and logging.

Webman compliance : uses Bootstrap interface and middleware mechanism.

PHP 8 attributes : native attribute syntax for marking aspects.

Priority support : multiple aspects execute according to priority.

Class‑level and method‑level aspects : both levels are supported.

Abstract base class : provides AbstractAspect to simplify aspect development.

Installation

composer require x2nx/webman-aop

Configuration

Edit config/plugin/x2nx/webman-aop/app.php:

<?php
return [
    // Enable AOP
    'enable' => true,

    // Directories to scan
    'scan_dirs' => [
        app_path(),
    ],

    // Directories to exclude
    'exclude_dirs' => [
        'vendor',
        'runtime',
        'config',
        'public',
    ],
];

Usage

Method‑level aspect

<?php
namespace app\controller\v1;
use X2nx\WebmanAop\Attribute\Aspect;
use X2nx\WebmanAop\Aspect\LogAspect;

class IndexController {
    #[Aspect(LogAspect::class)]
    public function index() {
        // method logic
    }
}

Class‑level aspect

<?php
namespace app\controller\v1;
use X2nx\WebmanAop\Attribute\Aspect;
use X2nx\WebmanAop\Aspect\LogAspect;

#[Aspect(LogAspect::class)]
class IndexController {
    public function index() {}
    public function detail() {}
}

Multiple aspects with priority

<?php
use X2nx\WebmanAop\Attribute\Aspect;
use app\aspect\CacheAspect;

#[Aspect(LogAspect::class, [], 100)] // priority 100, executed later
#[Aspect(CacheAspect::class, [], 50)] // priority 50, executed earlier
class IndexController {
    // CacheAspect runs before LogAspect
}

Creating Custom Aspects

Option 1 – Implement AspectInterface

<?php
namespace app\aspect;
use X2nx\WebmanAop\Contract\AspectInterface;
use X2nx\WebmanAop\JoinPoint;

class CacheAspect implements AspectInterface {
    public function before(JoinPoint $joinPoint): void { /* ... */ }
    public function after(JoinPoint $joinPoint): void { /* ... */ }
    public function around(JoinPoint $joinPoint, callable $proceed): mixed { /* ... */ }
    public function afterReturning(JoinPoint $joinPoint, mixed $result): void { /* ... */ }
    public function afterThrowing(JoinPoint $joinPoint, \Throwable $exception): void { /* ... */ }
}

Option 2 – Extend AbstractAspect (recommended)

<?php
namespace app\aspect;
use X2nx\WebmanAop\Contract\AbstractAspect;
use X2nx\WebmanAop\JoinPoint;
use support\Cache;

class CacheAspect extends AbstractAspect {
    public function before(JoinPoint $joinPoint): void {
        // check cache
        $cacheKey = $this->getCacheKey($joinPoint);
        if ($cached = Cache::get($cacheKey)) {
            $joinPoint->setReturnValue($cached);
            $joinPoint->setData('from_cache', true);
        }
    }

    public function around(JoinPoint $joinPoint, callable $proceed): mixed {
        if ($joinPoint->getData('from_cache')) {
            return $joinPoint->getReturnValue();
        }
        $result = $proceed();
        $cacheKey = $this->getCacheKey($joinPoint);
        Cache::set($cacheKey, $result, 3600);
        return $result;
    }

    protected function getCacheKey(JoinPoint $joinPoint): string {
        $method = $joinPoint->getFullMethodName();
        $args = $joinPoint->getArgs();
        return 'aop_cache:' . md5($method . serialize($args));
    }
}

AOP Notification Types

Before

Executed before the target method; typical uses include logging, parameter validation, permission checks, and cache checks.

public function before(JoinPoint $joinPoint): void {
    Log::info('Before method execution');
}

After

Executed after the target method regardless of outcome; typical uses include resource cleanup, timing, and lock release.

public function after(JoinPoint $joinPoint): void {
    Log::info('After method execution');
}

Around

Wraps the target method, allowing full control over execution, parameter modification, return value alteration, or even skipping the method.

public function around(JoinPoint $joinPoint, callable $proceed): mixed {
    $this->beforeLogic();
    $result = $proceed();
    return $this->afterLogic($result);
}

AfterReturning

Runs only when the method returns normally; useful for logging return values, processing results, or sending notifications.

public function afterReturning(JoinPoint $joinPoint, mixed $result): void {
    Log::info('Method returned', ['result' => $result]);
}

AfterThrowing

Runs only when the method throws an exception; useful for error logging, handling, or alerting.

public function afterThrowing(JoinPoint $joinPoint, \Throwable $exception): void {
    Log::error('Method threw exception', [
        'exception' => get_class($exception),
        'message'   => $exception->getMessage(),
    ]);
}

Notification Execution Order

All aspects' Before notifications (ordered by priority).

All aspects' Around notifications (ordered by priority, forming a chain).

The target method execution.

All aspects' AfterReturning notifications (if the method returned normally).

All aspects' AfterThrowing notifications (if an exception was thrown).

All aspects' After notifications (always executed).

JoinPoint API

// Get the target object
$target = $joinPoint->getTarget();

// Class and method names
$className = $joinPoint->getClassName();
$methodName = $joinPoint->getMethodName();
$fullMethodName = $joinPoint->getFullMethodName();

// Arguments
$args = $joinPoint->getArgs();
$joinPoint->setArgs($newArgs);
$arg = $joinPoint->getArg(0);
$joinPoint->setArg(0, $value);

// Execute original method
$result = $joinPoint->proceed();

// Return value and exception
$returnValue = $joinPoint->getReturnValue();
$exception   = $joinPoint->getException();

// Data storage for communication between aspects
$joinPoint->setData('key', $value);
$value = $joinPoint->getData('key', $default);
$allData = $joinPoint->getAllData();

Full Examples

Cache Aspect

<?php
namespace app\aspect;
use X2nx\WebmanAop\Contract\AbstractAspect;
use X2nx\WebmanAop\JoinPoint;
use support\Cache;

class CacheAspect extends AbstractAspect {
    public function before(JoinPoint $joinPoint): void {
        $cacheKey = $this->getCacheKey($joinPoint);
        if ($cached = Cache::get($cacheKey)) {
            $joinPoint->setReturnValue($cached);
            $joinPoint->setData('from_cache', true);
        }
    }

    public function around(JoinPoint $joinPoint, callable $proceed): mixed {
        if ($joinPoint->getData('from_cache')) {
            return $joinPoint->getReturnValue();
        }
        $result = $proceed();
        $cacheKey = $this->getCacheKey($joinPoint);
        Cache::set($cacheKey, $result, 3600);
        return $result;
    }

    protected function getCacheKey(JoinPoint $joinPoint): string {
        return 'aop_cache:' . md5($joinPoint->getFullMethodName() . serialize($joinPoint->getArgs()));
    }
}

Transaction Aspect

<?php
namespace app\aspect;
use X2nx\WebmanAop\Contract\AbstractAspect;
use X2nx\WebmanAop\JoinPoint;
use support\Db;

class TransactionAspect extends AbstractAspect {
    public function around(JoinPoint $joinPoint, callable $proceed): mixed {
        Db::beginTransaction();
        try {
            $result = $proceed();
            Db::commit();
            return $result;
        } catch (\Throwable $e) {
            Db::rollBack();
            throw $e;
        }
    }
}
AOPbackend developmentPHPAspect Oriented ProgrammingWebmanRuntime Proxy
Open Source Tech Hub
Written by

Open Source Tech Hub

Sharing cutting-edge internet technologies and practical AI resources.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.