PHP進階5 裝飾者模式在Laravel中中介軟體的應用
阿新 • • 發佈:2018-12-22
不積跬步無以至千里不積小流無以成江海
裝飾者模式是在開放-關閉原則下實現動態新增或者減少功能的一種方式,類似於洋蔥,分很多層,每一層都有一定的功能,可以隨時新增和修改這些層,官方將這些層稱之為中介軟體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進行解密
);
* ****************************************************************/