205 laravel 中间件实现

本文通过实例详细解析了Laravel框架中的管道机制实现原理,并对比分析了不同闭包使用方式的效果,介绍了IlluminateFoundationHttpKernel类的核心处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

laravel实现了管道机制, 即上一个中间件的输出是下一个中间件的输入,是对装饰器模式的成功应用。

闭包

有必要讲下闭包

1.闭包做参数传递

先来看一段代码

class A {
    function go($next){
        echo "step a".'<br/>';
        return $next;
    }
}

class B{
    function go($next){
        echo "step b".'<br/>';
        return $next;
    }
}

class C{
    function go($next){
        echo "step c".'<br/>';
        return $next;
    }
}

$fn();

(new C)->go(
    (new B)->go(
        (new A())->go($fn)
    )
);

运行一下,结果如下

获取请求
step a
step b
step c

2.优化一下

<?php
$fn = function(){
    echo "获取请求".'<br/>';
};

class A {
    function go($next){
        echo "step a".'<br/>';
        $next();
    }
}

class B{
    function go($next){
        echo "step b".'<br/>';
        $next();
    }
}

class C{
    function go($next){
        echo "step c".'<br/>';
        $next();
    }
}

function getSlices($next,$step){
    // var_dump($next);
    // var_dump($step);
    return function()use($next,$step){
        return (new $step)->go($next);
    };
}

$steps = [
    'A','B','C'
];

call_user_func(
    array_reduce($steps,'getSlices',$fn)
);

运行一下 返回了

step c
step b
step a
获取请求

结果正好倒过来了

3.分析下结果

第一步
$result_1 = function(){
    return function()use($fn,"A"){
        return (new "A")->go($fn);
    };
}
第二步
$result_2 = function(){
    return function()use($result_1 ,"B"){
        return (new "B")->go($result_1 );
    };
}
第三步
$result_3 = function(){
    return function()use($result_2,"C"){
        return (new C)->go($result_2);
    };
}
最后
call_user_func($result_3);

最外层是(new C)->go($result_2),所以先输出了step c,然后才是内层的step bstep a.

Illuminate\Foundation\Http\Kernel

1.handle()

前面做了一些 bootstrap 的工作,核心是sendRequestThroughRouter()

public function handle($request)
{
    try {
        $request->enableHttpMethodParameterOverride();

        $response = $this->sendRequestThroughRouter($request);
    } catch (Exception $e) {
       //省略异常处理
    }

    $this->app['events']->fire('kernel.handled', [$request, $response]);

    return $response;
}

2.sendRequestThroughRouter()

核心是最后一句

return (new Pipeline($this->app))
         ->send($request)
         ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
         ->then($this->dispatchToRouter());

Illuminate\Pipeline\Pipeline

1.send()

接受http请求实例

public function send($passable){
    $this->passable = $passable;
    return $this;
}

2.through

设置需要处理的中间件

public function through($pipes){
    $this->pipes = is_array($pipes) ? $pipes : func_get_args();

    return $this;
}

3.then()

public function then(Closure $destination){
    $firstSlice = $this->getInitialSlice($destination);

    $pipes = array_reverse($this->pipes);

    return call_user_func(
        array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable
    );
}

重点是getSlice()

4.getSlice()

是不是有点眼熟,但是这里套了两层闭包,上面的例子里明明只套了一层闭包,这是为什么呢?

protected function getSlice(){
    return function ($stack, $pipe) {
        return function ($passable) use ($stack, $pipe) {
            if ($pipe instanceof Closure) {
                return call_user_func($pipe, $passable, $stack);
            } else {
                list($name, $parameters) = $this->parsePipeString($pipe);

                return call_user_func_array([$this->container->make($name), $this->method],array_merge([$passable, $stack], $parameters));
            }
        };
    };
}

我们注意到,在最上面的例子中,array_reduce()第二个参数使用了函数名字符串作为参数,而这里使用的是匿名函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值