这是一个基于 Webman 框架的高性能、高可用 AOP(面向切面编程)插件,支持运行时代理,无需生成文件。项目采用 MIT 许可,适用于 PHP 8 环境。
插件地址:https://www.workerman.net/plugin/208
通过 Composer 安装:
composer require x2nx/webman-aop
配置文件:config/plugin/x2nx/webman-aop/app.php
<?php
return [
// 是否启用 AOP
'enable' => true,
// 扫描目录
'scan_dirs' => [
app_path(),
],
// 排除目录
'exclude_dirs' => [
'vendor',
'runtime',
'config',
'public',
],
];
<?php
namespace app\controller\v1\customer;
use X2nx\WebmanAop\Attribute\Aspect;
use X2nx\WebmanAop\Aspect\LogAspect;
class IndexController
{
#[Aspect(LogAspect::class)]
public function index()
{
// 方法逻辑
}
}
<?php
namespace app\controller\v1\customer;
use X2nx\WebmanAop\Attribute\Aspect;
use X2nx\WebmanAop\Aspect\LogAspect;
#[Aspect(LogAspect::class)]
class IndexController
{
// 所有公共方法都会应用 LogAspect
public function index() {}
public function detail() {}
}
<?php
use X2nx\WebmanAop\Attribute\Aspect;
use X2nx\WebmanAop\Aspect\LogAspect;
use app\aspect\CacheAspect;
#[Aspect(LogAspect::class, [], 100)] // 优先级 100,后执行
#[Aspect(CacheAspect::class, [], 50)] // 优先级 50,先执行
class IndexController
{
// 切面执行顺序:CacheAspect -> LogAspect -> 原方法
}
<?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
{
// 环绕通知:可以控制目标方法的执行
// 可以修改参数、返回值,或决定是否执行目标方法
return $proceed();
}
public function afterReturning(JoinPoint $joinPoint, mixed $result): void
{
// 返回后通知:在目标方法正常返回后执行
}
public function afterThrowing(JoinPoint $joinPoint, \Throwable $exception): void
{
// 异常后通知:在目标方法抛出异常后执行
}
}
<?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
{
$method = $joinPoint->getFullMethodName();
$args = $joinPoint->getArgs();
return 'aop_cache:' . md5($method . serialize($args));
}
}
在目标方法执行之前执行,可以:
public function before(JoinPoint $joinPoint): void
{
// 在目标方法执行前执行
Log::info('Before method execution');
}
在目标方法执行之后执行(无论是否抛出异常),可以:
public function after(JoinPoint $joinPoint): void
{
// 无论是否抛出异常,都会执行
Log::info('After method execution');
}
可以完全控制目标方法的执行,可以:
public function around(JoinPoint $joinPoint, callable $proceed): mixed
{
// 前置逻辑
$this->beforeLogic();
// 执行目标方法(可以修改参数)
$result = $proceed();
// 后置逻辑(可以修改返回值)
return $this->afterLogic($result);
}
在目标方法正常返回后执行(不抛出异常时),可以:
public function afterReturning(JoinPoint $joinPoint, mixed $result): void
{
// 只在正常返回时执行
Log::info('Method returned', ['result' => $result]);
}
在目标方法抛出异常后执行,可以:
public function afterThrowing(JoinPoint $joinPoint, \Throwable $exception): void
{
// 只在抛出异常时执行
Log::error('Method threw exception', [
'exception' => get_class($exception),
'message' => $exception->getMessage()
]);
}
当多个切面应用到同一个方法时,执行顺序如下:
// 获取目标对象
$target = $joinPoint->getTarget();
// 获取类名和方法名
$className = $joinPoint->getClassName();
$methodName = $joinPoint->getMethodName();
$fullMethodName = $joinPoint->getFullMethodName();
// 获取和设置参数
$args = $joinPoint->getArgs();
$joinPoint->setArgs($newArgs);
$arg = $joinPoint->getArg(0);
$joinPoint->setArg(0, $value);
// 执行原方法
$result = $joinPoint->proceed();
// 获取返回值和异常
$returnValue = $joinPoint->getReturnValue();
$exception = $joinPoint->getException();
// 数据存储(用于切面之间传递数据)
$joinPoint->setData('key', $value);
$value = $joinPoint->getData('key', $default);
$allData = $joinPoint->getAllData();
<?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()));
}
}
<?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;
}
}
}
插件地址404
还在审核