面向切面编程(AOP),也可以成为契约编程。好处和作用有很多。比如降低模块之间的耦合度,进行代码擦除减少重复代码。
在工作过程中,因为要对一部分方法,进行日志环绕处理。如果用代理模式的话,显得过于臃肿。所以,想到了 Java 的 AOP。由于没有找到合适的包,所以自己就手撸了一个。由于比较简单,所以也懒得打包了,目前只实现了简单的函数代理。(流程图如下)

 

AOP实现类

<?php

namespace App\Http\Intercept\Proxy;
use App\Http\Intercept\Interceptor;
use App\Http\Intercept\InvokeEnhance\Invocation;


/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2021/10/8
 * Time: 15:14
 */
class ProxyBean
{
    private  $target = null; //被代理类
    private  $args = null;// 被代理的构造参数
    private  $interceptor = null; //代理类
    private $ex;


    /**
     * @param $target //被代理类
     * @param $args //被代理的构造参数
     * @param Interceptor $interceptor //代理类
     * @return ProxyBean
     */
    public static function getProxyBean($target,$args,Interceptor $interceptor){
        $proxyBean = new ProxyBean();
        //被代理的类
        $proxyBean->target = $target; //被代理类
        $proxyBean->args = $args; //被代理的构造参数
        $proxyBean->interceptor = $interceptor; //代理类
        return $proxyBean;
    }

    /**
     * 魔术方法 - 调用不存在的方法是,触发代理实现
     * @param $name
     * @param $arguments
     */
    public function __call($name,$arguments) {
        return $this->invoke($this->target,$this->args,$name,$arguments);
    }


    /**
     *代理执行
     * @param $target //被代理类
     * @param $args //被代理类构造参数
     * @param $name //被代理类执行方法
     * @param $arguments //被代理类执行方法-参数
     * @return void|null
     */
    public function invoke($target,$args,$name,$arguments){

        $exceptionFlag = false;
        $retObj = null;

        $invocation = new Invocation($target,$args,$name,$arguments);

        try {
            if ($this->interceptor->before() && $this->interceptor->useAround()){
                $retObj = $this->interceptor->around($invocation);
            }else{
                $retObj = $invocation->proceed();
            }
        }catch (\Exception $ex){
            $exceptionFlag = true;
            $this->ex =$ex;
        }
        $this->interceptor->after();
        if ($exceptionFlag){
            $this->interceptor->afterThrowing($ex);
        }else{
            $this->interceptor->afterReturning();
            return $retObj;
        }
        return null;
    }
}

契约接口

<?php
namespace App\Http\Intercept;
use App\Http\Intercept\InvokeEnhance\Invocation;


/**
 * AOP 约定接口标准
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2021/10/8
 * Time: 14:56
 */
interface Interceptor
{

    //执行前
    public function before() :bool;

    //执行后
    public function after();

    //环绕
    public function around(Invocation $invocation);

    //运行后执行
    public function afterReturning();

    //捕获运行错误
    public function afterThrowing(\Exception $ex);

    //是否启用环绕
    public function useAround() :bool;

}

契约自定义

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2021/10/8
 * Time: 15:29
 */

namespace App\Http\Intercept;
use App\Http\Intercept\InvokeEnhance\Invocation;

class DatabaseWmsLogInterceptor  implements Interceptor
{
    public function before(): bool
    {
        var_dump('before......<br>');
        return true;
    }

    public function after()
    {
        // TODO: Implement after() method.
    }

    public function afterReturning()
    {
        var_dump('afterReturning...<br>');
    }

    public function afterThrowing(\Exception $ex)
    {

        var_dump('afterThrowing...'.$ex->getMessage());
    }

    public function useAround(): bool
    {
        return true;
    }


    public function around(Invocation $invocation)
    {
        echo '<br>================around before...==================<br>';
        var_dump('around before...<br>');
        var_dump($invocation->getMethodargs());
        echo '<br>=================around before...=================<br>';
        $obj = $invocation->proceed();
        echo '<br>================around after...==================<br>';
        var_dump('<br>around after...<br>');
        var_dump($obj);
        echo '<br>================around after...==================<br>';
        return $obj;
    }

}

被代理

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2021/10/8
 * Time: 15:26
 */

namespace App\Services\Test;


use App\Services\Test\InInterface\HelloService;

class HelloServiceImpl implements HelloService
{

    public function sayHello($name)
    {
        if (empty($name)){
            throw new \RuntimeException("parameter is null");
        }
        var_dump('hello:'.$name);
    }

    public function sayGoodBy($name,array $arr)
    {
        var_dump($name.':sayGoodBy:'.json_encode($arr));
        return $arr;
    }

    public static function good($name){
        var_dump($name);
        return $name;
    }
}

实例

    /**
     * aop 约定式编程
     *
     * @param  int  $id
     * @return \Illuminate\View\View
     */
    public function testDatabaseWmsLogInterceptor()
    {
        $proxy = ProxyBean::getProxyBean(HelloServiceImpl::class,[],app(DatabaseWmsLogInterceptor::class));
        //$proxy->sayHello();
        echo '<br>';
        $result = $proxy->sayGoodBy('小狗',['one','two']);
        var_dump($result);
        //return view('testDemo');
    }