What is the decorator pattern
The decorator pattern allows adding new functionality to an existing object without changing its structure.
Simple code implementation:
interface Decorate {
function getInfo();
}
/**
* Class DecoateA
* 初始化一个装饰对象
*/
class DecoateA implements Decorate{
/**
* 实现接口
*/
public function getInfo() {
echo __CLASS__.PHP_EOL;
}
}
/**
* Class DecoateB
* 装饰DecoateA 的装饰类
*/
class DecoateB implements Decorate{
/**
* DecoateB constructor.
* @param Decorate $dec
* 构造器 传过来其他装饰类
*/
public function __construct(Decorate $dec) {
$this->dec = $dec;
}
/**
* 实现接口
*/
public function getInfo() {
echo __CLASS__.PHP_EOL;
$this->dec->getInfo();
}
}
// 通过不断的创建装饰对象来添加功能
$obj = new DecoateA();// 初始一个对象
$obj = new DecoateB($obj);// 装饰对象
$obj->getInfo();
// DecoateB
// DecoateA
Decorators and Laravel middleware
Decorator's idea of continuously adding functions to existing objects is consistent with the idea of framework middleware, which is a major application of the decorator pattern.
A request can be regarded as an existing object. Before the request reaches the controller, it needs to go through various middleware of the Laravel framework. Each middleware "decorates" the request differently, and responds to the client when the request reaches the controller and the execution is completed. At the end, a response must pass through the middleware that just arrived on its way back. Each middleware can "decorate" the response differently, and finally the response reaches the client. This process is a typical "onion model".
Express the Laravel middleware principle in a short code
interface Middleware
{
public static function handle(Closure $next);
}
/**
* 中间件A(可以视为装饰类A)
*/
class MiddlewareA implements Middleware
{
public static function handle(Closure $next)
{
echo __CLASS__." start".PHP_EOL;
$next();
echo __CLASS__." end".PHP_EOL;
}
}
/**
* 中间件B(可以视为装饰类B)
*/
class MiddlewareB implements Middleware
{
public static function handle(Closure $next)
{
echo __CLASS__." start".PHP_EOL;
$next();
echo __CLASS__." end".PHP_EOL;
}
}
/**
* 生成闭包函数
*/
function makeClosureFun($carry, $middlerwareClass)
{
return function () use ($carry, $middlerwareClass) {
return $middlerwareClass::handle($carry);
};
}
/**
* 执行
*/
// Kernel 中的中间件数组
$middlerwareArray = [
'MiddlewareA',
'MiddlewareB'
];
$prepare = function () {
echo "_init_".PHP_EOL;
};
// 组装闭包
$closure = array_reduce($middlerwareArray, 'makeClosureFun', $prepare);
// 执行
call_user_func($closure);
result:
MiddlewareB start
MiddlewareA start
_init_
MiddlewareA end
MiddlewareB end
The output of the above code exactly conforms to the "onion model" of middleware.
Code analysis
How is this achieved? The key to implementing middleware is array_reduce() + closure function.
array_reduce() The first parameter is an array, the second parameter is a callback function, and the third parameter is a function that is triggered when the processing starts or ends.
Array_reduce will pass two parameters to the callback function, carry and item, which are the value returned from the previous iteration and the value of this iteration, respectively.
Assemble the closure
When assembling the closure, the partial code is as follows
function makeClosureFun($carry, $middlerwareClass)
{
return function () use ($carry, $middlerwareClass) {
// 忽略视为黑盒
};
}
// 组装闭包
$closure = array_reduce($middlerwareArray, 'makeClosureFun', $prepare);
makeClosureFun() returns a use two-variable closure function. As for what is done inside the closure function, don't pay attention to it, and treat it as a black box. The two parameters of the closure function use are the return value of the last iteration passed to the closure by array_reduce() and the value of the current iteration.
When makeClosureFun() is executed for the first iteration, a closure function is returned. This closure function is actually a closure class when stored. You can see that the two parameters of use are stored under the static attribute.
object(Closure)#2 (1) {
["static"]=>
array(2) {
["carry"]=>
object(Closure)#1 (0) {
}
["middlerwareClass"]=>
string(11) "MiddlewareA"
}
}
The first iteration of closure function returns, continued as the second iteration of $carry
passed, $middlerwareClass
become a $middlerwareArray
the second element of the array. Iterate in this way, to the final $closure
.
object(Closure)#3 (1) {
["static"]=>
array(2) {
["carry"]=>
object(Closure)#2 (1) {
["static"]=>
array(2) {
["carry"]=>
object(Closure)#1 (0) {
}
["middlerwareClass"]=>
string(11) "MiddlewareA"
}
}
["middlerwareClass"]=>
string(11) "MiddlewareB"
}
}
Execution closure
Use call_user_func() to execute the closure function in the $closure variable.
TODO: How does PHP find the corresponding function code fragment based on the structure in the closure variable? (How does OR call_user_func execute the closure function?)
The first execution of use introduces $carry
and $middlerwareClass
, and executes $middlerwareClass::handle($carry)
. At this time, the $middlerwareClass
is MiddlewareB, that is, executes MiddlewareB::handle($carry)
, and the incoming $carry
is a closure. MiddlewareB class handle () method previously performed MiddlewareB start
output by $next()
performing the closure, i.e. the second performance.
Execute $middlerwareClass::handle($carry)
this time. At this time, the $middlerwareClass
is MiddlewareA, that is, execute MiddlewareA::handle($carry)
, output MiddlewareA start
, execute closure $next()
, PHP finds that the closure is empty, trigger array_reduce() to set $prepare
_init_
, and then output 12987ef3c98747d, and then output the , output MiddlewareA end
, then output MiddlewareB end
.
Reference article
Main reference, special thanks to the author of this article
decorator mode and middleware application under the Laravel framework
Further reading
Pipeline pipeline operation to achieve request middleware filtering (the most detailed explanation)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。