x2nx / webman-aop
Webman AOP (Aspect-Oriented Programming) plugin for webman framework
Installs: 23
Dependents: 0
Suggesters: 0
Security: 0
Stars: 4
Watchers: 0
Forks: 1
Open Issues: 0
pkg:composer/x2nx/webman-aop
Requires
- php: >=8.1
- workerman/webman-framework: ^2.0
README
特性
- ✅ 标准 AOP 切面类型:支持 Before、After、Around、AfterReturning、AfterThrowing
- ✅ 运行时代理:高性能的动态代理实现,无需生成文件
- ✅ 高可用:完善的错误处理和日志记录
- ✅ 符合 Webman 规范:使用 Bootstrap 接口和中间件机制
- ✅ PHP 8 Attributes:使用原生属性标记切面
- ✅ 优先级支持:多个切面按优先级执行
- ✅ 类级别和方法级别:支持两种级别的切面
- ✅ 抽象基类:提供 AbstractAspect 基类,简化切面开发
安装
通过 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 -> 原方法 }
创建自定义切面
方式一:实现 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 { // 环绕通知:可以控制目标方法的执行 // 可以修改参数、返回值,或决定是否执行目标方法 return $proceed(); } public function afterReturning(JoinPoint $joinPoint, mixed $result): void { // 返回后通知:在目标方法正常返回后执行 } public function afterThrowing(JoinPoint $joinPoint, \Throwable $exception): void { // 异常后通知:在目标方法抛出异常后执行 } }
方式二:继承 AbstractAspect 基类(推荐)
<?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)); } }
AOP 通知类型说明
Before(前置通知)
在目标方法执行之前执行,可以:
- 记录日志
- 参数验证
- 权限检查
- 缓存检查
public function before(JoinPoint $joinPoint): void { // 在目标方法执行前执行 Log::info('Before method execution'); }
After(后置通知)
在目标方法执行之后执行(无论是否抛出异常),可以:
- 清理资源
- 记录执行时间
- 释放锁
public function after(JoinPoint $joinPoint): void { // 无论是否抛出异常,都会执行 Log::info('After method execution'); }
Around(环绕通知)
可以完全控制目标方法的执行,可以:
- 修改参数
- 修改返回值
- 决定是否执行目标方法
- 实现缓存、事务等
public function around(JoinPoint $joinPoint, callable $proceed): mixed { // 前置逻辑 $this->beforeLogic(); // 执行目标方法(可以修改参数) $result = $proceed(); // 后置逻辑(可以修改返回值) return $this->afterLogic($result); }
AfterReturning(返回后通知)
在目标方法正常返回后执行(不抛出异常时),可以:
- 记录返回值
- 处理返回结果
- 发送通知
public function afterReturning(JoinPoint $joinPoint, mixed $result): void { // 只在正常返回时执行 Log::info('Method returned', ['result' => $result]); }
AfterThrowing(异常后通知)
在目标方法抛出异常后执行,可以:
- 记录异常
- 错误处理
- 发送告警
public function afterThrowing(JoinPoint $joinPoint, \Throwable $exception): void { // 只在抛出异常时执行 Log::error('Method threw exception', [ 'exception' => get_class($exception), 'message' => $exception->getMessage() ]); }
通知执行顺序
当多个切面应用到同一个方法时,执行顺序如下:
- 所有切面的 Before 通知(按优先级顺序)
- 所有切面的 Around 通知(按优先级顺序,形成链式调用)
- 目标方法执行
- 所有切面的 AfterReturning 通知(如果正常返回)
- 所有切面的 AfterThrowing 通知(如果抛出异常)
- 所有切面的 After 通知(无论是否抛出异常)
JoinPoint API
// 获取目标对象 $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; } } }
许可证
MIT