205 laravel 中間件實現

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()第二個參數使用了函數名字符串作爲參數,而這裏使用的是匿名函數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章