AOP 面向切面程式設計

yueguang發表於2022-01-18

面向切面程式設計(AOP),也可以成為契約程式設計。好處和作用有很多。比如降低模組之間的耦合度,進行程式碼擦除減少重複程式碼。
在工作過程中,因為要對一部分方法,進行日誌環繞處理。如果用代理模式的話,顯得過於臃腫。所以,想到了Java的AOP。由於沒有找到合適的包,所以自己就手擼了一個。由於比較簡單,所以也懶得打包了,目前只實現了簡單的函式代理。(流程圖如下)

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');
    }
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章