1. 程式人生 > >PHP進階5 裝飾者模式在Laravel中中介軟體的應用

PHP進階5 裝飾者模式在Laravel中中介軟體的應用

                    不積跬步無以至千里不積小流無以成江海


裝飾者模式是在開放-關閉原則下實現動態新增或者減少功能的一種方式,類似於洋蔥,分很多層,每一層都有一定的功能,可以隨時新增和修改這些層,官方將這些層稱之為中介軟體Middleware。

前置知識

1、mixed array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] )
    array_reduce() 將回調函式 callback 迭代地作用到 array 陣列中的每一個單元中,從而將陣列簡化為單一的值
        array   輸入的 array
        mixed callback ( mixed $carry , mixed $item )
            carry    攜帶上次迭代裡的值; 如果本次迭代是第一次,那麼這個值是 initial。
            item     攜帶了本次迭代的值。
        initial   如果指定了可選引數 initial,該引數將在處理開始前使用,或者當處理結束,陣列為空時的最後一個結果。
$a = [1, 2, 3, 4, 5];

function sum($carry, $item)
{
    $carry += $item;
    echo $carry . '<br>';     //  1 3 6 10 15
    return $carry;
}

var_dump(array_reduce($a, 'sum'));  //  int(15)

function product($carry, $item)
{
    $carry *= $item;
    echo $carry . '<br>';   //  10 20 60 240 1200
    return $carry;
}

var_dump(array_reduce($a, 'product',10));   //  int(1200)

$x = [];

var_dump(array_reduce($x, "sum", "No data to reduce"));

2、call_user_func

    把第一個引數作為回撥函式呼叫 mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] ) 第一個引數 callback 是被呼叫的回撥函式,其餘引數是回撥函式的引數

3、裝飾者模式

<?php

interface Decorator
{
    public function display();
}

class XiaoFang implements Decorator
{

    private $name;

    public function __construct($name)
    {
        $this->name = $name;
    }

    public function display()
    {
        echo "我是" . $this->name . "我出門了!!!" . '<br>';
    }

}

class Finery implements Decorator
{

    private $component;

    public function __construct(Decorator $compent)
    {
        $this->component = $compent;
    }

    public function display()
    {
        $this->component->display();
    }
}

class Shoes extends Finery
{

    public function display()
    {
        echo '穿上鞋子' . '<br>';
        parent::display();
    }
}

class Skirt extends Finery
{

    public function display()
    {
        echo "穿上裙子" . '<br>';
        parent::display();
    }
}

class Fire extends Finery
{

    public function display()
    {
        echo '出門前先整理頭髮' . '<br>';
        parent::display();
        echo '出門後再整理一下頭髮' . '<br>';
    }
}

$xiaofang = new XiaoFang('小芳');
$shoes = new Shoes($xiaofang);
$skirt = new Skirt($shoes);
$fire = new Fire($skirt);
$fire->display();

/**
 * 1、出門前先整理頭髮
 * 2、呼叫$skirt的diaplay()方法:穿上裙子
 * 3、呼叫$shoes的display()方法:穿上鞋子
 * 4、呼叫$xiaofang的diaplay()方法:我是小芳,我出門了
 * 5、出門後再整理一下頭髮
 */

簡化版的中介軟體啟動順序

<?php

interface Step
{
    public static function go(Closure $next);
}

class FirstStep implements Step
{
    public static function go(Closure $next)
    {
        echo "開啟session,獲取資料" . '<br>';
        $next();
        echo "儲存資料,關閉session" . '<br>';
    }
}

function goFun($step, $className)
{
    return function () use ($step, $className) {
        return $className::go($step);
    };
}

function then()
{
    $steps = ["FirstStep"];

    $prepare = function () {
        echo "請求向路由器傳遞,返回響應" . '<br>';
    };

    /**
     * 第一個引數是要處理的陣列
     * 第二個引數是處理函式名稱或回撥函式
     * 第三個引數為可選引數,為初始化引數,將被當作陣列中的第一個值來處理,如果陣列為空則作為返回值
     * 這裡第三個引數傳遞一個回撥函式,該函式用於將請求向路由器繼續傳遞,返回響應
     * 第一個引數為陣列,該陣列記錄了外層功能的類名
     * goFun函式作為處理陣列的回撥函式
     * array_reduce()最終返回的是一個回撥函式,即$go
     * $go = function() {
     *     return $FirstStep::go(function() {
     *         echo "請求向路由器傳遞,返回響應" . '<br>';
     * });
     * }
     */
    $go = array_reduce($steps, "goFun", $prepare);

    $go();
}

then();

升級版的中介軟體依次執行順序

<?php

interface Middleware
{
    public static function handle(Closure $next);
}

class VerifyCsrfToken implements Middleware
{
    public static function handle(Closure $next)
    {
        echo "驗證Csrf-Token" . '<br>';
        $next();
    }
}

class ShareErrorsFromSession implements Middleware
{
    public static function handle(Closure $next)
    {
        echo "如果session中有'error'變數,則共享他" . '<br>';
        $next();
    }
}

class StartSession implements Middleware
{
    public static function handle(Closure $next)
    {
        echo "開啟session" . '<br>';
        $next();
        echo "儲存資料,關閉session" . '<br>';
    }
}

class AddQueuedCookieToResponse implements Middleware
{
    public static function handle(Closure $next)
    {
        $next();
        echo "新增下一次請求需要的cookie" . '<br>';
    }
}

class EncryptCookie implements Middleware
{
    public static function handle(Closure $next)
    {
        echo "對輸入請求的cookie進行解密" . '<br>';
        $next();
        echo "對輸出響應的cookie進行加密" . '<br>';
    }
}

class CheckFormaintenanceMode implements Middleware
{
    public static function handle(Closure $next)
    {
        echo "確定當前程式是否處於維護狀態" . '<br>';
        $next();
    }
}

function getSlice()
{
    return function ($stack, $pipe) {
        return function () use ($stack, $pipe) {
            return $pipe::handle($stack);
        };
    };
}

function then()
{
    $pipes = [
        "CheckFormaintenanceMode",
        "EncryptCookie",
        "AddQueuedCookieToResponse",
        "StartSession",
        "ShareErrorsFromSession",
        "VerifyCsrfToken"
    ];

    $firstSlice = function () {
        echo "請求向路由器傳遞,返回響應" . "<br>";
    };

    $pipes = array_reverse($pipes);

    call_user_func(
        array_reduce($pipes, getSlice(), $firstSlice)
    );
}

then();

VerifyCsrfToken::handle(
    function () {
        echo "請求向路由器傳遞,返回響應" . "<br>";
    }
);

/**************************邏輯分析*****************************

ShareErrorsFromSession::handle(
    VerifyCsrfToken::handle(
        function () {
            echo "請求向路由器傳遞,返回響應" . "<br>";
        }
    )
);

StartSession::handle(
    ShareErrorsFromSession::handle(
        VerifyCsrfToken::handle(
            function () {
                echo "請求向路由器傳遞,返回響應" . "<br>";
            }
        )
    )
);
AddQueuedCookieToResponse::handle(
    StartSession::handle(
        ShareErrorsFromSession::handle(
            VerifyCsrfToken::handle(
                function () {
                    echo "請求向路由器傳遞,返回響應" . "<br>";
                }
            )
        )
    )
);
EncryptCookie::handle(
    AddQueuedCookieToResponse::handle(
        StartSession::handle(
            ShareErrorsFromSession::handle(
                VerifyCsrfToken::handle(
                    function () {
                        echo "請求向路由器傳遞,返回響應" . "<br>";
                    }
                )
            )
        )
    )
);

CheckFormaintenanceMode::handle(    //  確定當前程式是否處於維護狀態
    EncryptCookie::handle(          //  對輸入請求的cookie進行解密
        AddQueuedCookieToResponse::handle(
            StartSession::handle(   //  開啟session,獲取資料
                ShareErrorsFromSession::handle( //  如果session中有error變數,則共享他
                    VerifyCsrfToken::handle(    //  驗證csrf-token
                        function () {
                            echo "請求向路由器傳遞,返回響應" . "<br>";  //  請求向路由器傳遞,返回響應
                        }
                    )
                )
            )   //  儲存資料,關閉session
        )   //  新增下一次請求需要的cookie
    )
    //  對輸出響應的cookie進行解密
);

 * ****************************************************************/