下面我将为您详细讲解“PHP AOP教程案例”的完整攻略。
什么是AOP
面向切面编程(Aspect-Oriented Programming, AOP)是一种编程思想,它解决了面向对象编程中的一些横向关注点问题。 AOP 的一个核心功能便是拦截、修改某个对象的某个方法。PHP 的 AOP 有很多库可以使用,这里介绍的是 goaop/aop
。
安装
使用 composer
安装:
composer require goaop/framework
使用方式
定义切面
切面就是拦截修改某个类的某个方法。我们通过 Annotation
的方式来定义一个切面。例如:
use Go\Aop\Aspect;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\Around;
class LoggerAspect implements Aspect
{
/**
* @param MethodInvocation $invocation Invocation
*
* @Around("@execution(MyProject\Annotation\Loggable)")
*/
public function aroundMethodExecution(MethodInvocation $invocation)
{
$method = $invocation->getMethod()->name;
echo "Before {$method}\n";
$result = $invocation->proceed();
echo "After {$method}\n";
return $result;
}
}
- 类实现了
Go\Aop\Aspect
接口; - 利用
Go\Lang\Annotation\Around
注解来定义切面的类型以及需要修改的方法; @execution(MyProject\Annotation\Loggable)
表示被@Loggable
注解的方法将会被拦截修改。
这个切面的作用是在被 @Loggable
注解的方法调用前后分别打印出 "Before" 和 "After" 字符串。
使用切面
利用前文定义的 LoggerAspect
切面,修改 MyController
中的 testAction
方法:
use MyProject\Annotation\Loggable;
class MyController extends Controller
{
/**
* @Loggable
*/
public function testAction()
{
return 'Test';
}
}
在 testAction
方法上添加 @Loggable
注解后,方法被拦截,进入 LoggerAspect
的 aroundMethodExecution
方法中,打印出对应的日志。
Test:
Before testAction
After testAction
以上就是使用 PHP AOP 的基本流程。
案例演示
这里再举一个常见的案例来说明 AOP 的作用。
问题
假设我们有这样一个场景:我们要在老项目中新增一个接口,需要让这个接口返回的 json 数据,完全按照别的项目返回的 json 数据格式来操作。
但是考虑实际情况,整个老项目中有很多地方都调用到了这个接口,如果我们要在所有这些调用点中修改代码,那就是一件非常麻烦的事情。
那么,那怎样才能让这个接口被调用的时候,能够自动转换 json 数据格式呢?
解决方案
使用 AOP 解决这个问题可以分为以下两个步骤:
- 定义转换器:将老项目中的 json 数据转为新的 json 数据;
- 编写切面:在老项目中的那个接口方法调用之前,把这个方法返回的 json 数据转换。
第一步:定义转换器
我们需要先定义一个转换器类,实现一个 convert
方法,将老项目中的 json 数据转成新的 json 数据:
class JsonConverter
{
public function convert($oldJson)
{
// 这里是转换逻辑,比如:
$arr = json_decode($oldJson, true);
return json_encode($arr, JSON_PRETTY_PRINT);
}
}
这里我们假设定义了一个 JsonConverter
类来完成 json 转换,实际开发中完全可以根据业务进行相应修改。
第二步:编写切面
在老项目中需要使用 AOP 来切入到接口被调用的方法中,并且在转换 json 数据后保存到返回结果中,这里我们需要编写如下的切面:
class JsonAspect implements Aspect
{
/**
* @var JsonConverter $jsonConverter
*/
private $jsonConverter;
public function __construct(JsonConverter $jsonConverter)
{
$this->jsonConverter = $jsonConverter;
}
/**
* @param MethodInvocation $invocation Invocation
*
* @Around("@execution(MyProject\Annotation\JsonSerializable)")
*/
public function aroundMethodExecution(MethodInvocation $invocation)
{
$result = $invocation->proceed();
// 如果返回的结果是字符串,表示是 json,那么我们就需要处理
if (is_string($result)) {
$newJson = $this->jsonConverter->convert($result);
return $newJson;
}
return $result;
}
}
这里定义了一个名为 JsonAspect
的切面,它的作用是在 @JsonSerializable
注解被使用的接口方法上拦截,获取方法返回结果并将其转换为新的 json 格式。
其中 JsonConverter
是我们在第一步中定义的类,它的实例被传递给了 JsonAspect
切面的构造函数。
示例
现在让我们来看一下一个具体的示例,来模拟刚刚提到的那个场景。
- 在老项目中定义一个接口,并且在注解中使用
@JsonSerializable
标记:
class MyController
{
/**
* @JsonSerializable
*/
public function myOldApi()
{
return '{"name":"Alice"}';
}
}
- 在切面中实现对这个接口方法返回结果进行转换:
$jsonConverter = new JsonConverter();
$loggerAspect = new JsonAspect($jsonConverter);
$loader = include __DIR__ . '/../vendor/autoload.php';
$annotationLoader = new AnnotationLoader($loader);
$aspectContainer = new AspectContainer();
$aspectContainer->registerAspect($loggerAspect);
$kernel = AspectKernel::getInstance();
$kernel->init([
'debug' => true,
'cacheDir' => __DIR__ . '/cache',
'includePaths' => [
__DIR__ . '/../src',
__DIR__ . '/../vendor/goaop/framework/src',
],
]);
$kernel->setLoader($annotationLoader);
$kernel->setAspectContainer($aspectContainer);
$controller = new MyController();
$result = $controller->myOldApi();
echo $result;
在上面的代码中,我们初始化 JsonAspect
切面的时候需要传递 JsonConverter
类的实例。然后我们使用 AOP 给 MyController
中的 myOldApi
方法加上了一个切面。当这个方法被调用时,实际返回的结果将会在这个切面中被转换。
以上就是使用 AOP 实现自动修改过滤 json 格式的攻略,希望对您有所帮助。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:PHP AOP教程案例 - Python技术站