laravel Pipeline 原理的詳細解析

laravel 中間件使用了 Pipeline

  • vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php
 return (new Pipeline($this->app))
      ->send($request)
      ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
      ->then($this->dispatchToRouter());
  • then 函數
public function then(Closure $destination)
{
    $pipeline = array_reduce(
            array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
        );

    return $pipeline($this->passable);
}
  • carry 函數
protected function carry()
{
    return function ($stack, $pipe) {
        return function ($passable) use ($stack, $pipe) {
            if (is_callable($pipe)) {
                return $pipe($passable, $stack);
            } elseif (! is_object($pipe)) {
                list($name, $parameters) = $this->parsePipeString($pipe);
                $pipe = $this->getContainer()->make($name);
                $parameters = array_merge([$passable, $stack], $parameters);
            } else {
                $parameters = [$passable, $stack];
            }
            return method_exists($pipe, $this->method)
                            ? $pipe->{$this->method}(...$parameters)
                            : $pipe(...$parameters);
        };
    };
}

讓我們去掉分支條件,創建一個簡單例子

定義管道執行函數

function reducePipe($pipesFn, $initFn) {
    return array_reduce($pipesFn, function($carry, $pipe) {
        return function($data) use($carry, $pipe) {
            return $pipe($data, $carry);
        };
    }, $initFn);
}

定義3個管道函數 $pipes

$pipes = [];
foreach(range(1, 3) as $row) {
    $pipes[] = function($data, $next) use($row) {
        echo 'pipe-before' . $row . PHP_EOL;
        $data->num += $row;
        $data = $next($data);
        echo 'pipe-hehind' . $row . PHP_EOL;
        return $data;
    };
}

定義初始化函數 $init

$init = function($data) {
    echo 'init start' . PHP_EOL;
    var_dump($data);
    echo 'init end' . PHP_EOL;
    return 'init';
};

定義初始化數據 $data

$data = new StdClass;
$data->num = 1;

執行函數

$reduce = reducePipe($pipes, $init); // 獲取 array_reduce 后的函數
$result = $reduce($data);
var_dump($result); // 結果和每個$pipe的 return 有關,傳遞關系 init -> h1 -> h2 -> h3
  • 執行結果
pipe-before3
pipe-before2
pipe-before1
init start
object(stdClass)#5 (1) {
  ["num"]=>
  int(7)
}
init end
pipe-hehind1
pipe-hehind2
pipe-hehind3
string(4) "init"

ps: 所以為了得到正確的執行順序,需要 array_reverse($pipes) 翻轉數組

解析 array_reduce 的執行過程

洋蔥模型 before3->before2->before1->init->behind1->hehind2->hebind3

  • $reduce
function($data) use($carry, $pipe) {
      return $pipe($data, $carry);
};
  • $pipe
 function($data, $next) use($row) {
        echo 'pipe-before' . $row . PHP_EOL;
        $data->num += $row;
        $data = $next($data);
        echo 'pipe-hehind' . $row . PHP_EOL;
        return $data;
  };

reduce1

$reduce1 = function($data) {
    $row = 1;
    echo 'pipe-before' . $row . PHP_EOL;
    $data->num += $row;

    // $next($data) $next == $init
    echo 'init start' . PHP_EOL;
    var_dump($data);
    echo 'init end' . PHP_EOL;
    $data = 'init';

    echo 'pipe-hehind' . $row . PHP_EOL;
    return $data;
};

reduce2

$reduce2 = function($data) use($reduce1) {
    $row = 2;
    echo 'pipe-before' . $row . PHP_EOL;
    $data->num += $row;

    // $next($data) $next == $reduce1
    $data = $reduce1($data);

    echo 'pipe-hehind' . $row . PHP_EOL;
    return $data;
};

reduce3

$reduce3 = function($data) use($reduce2) {
    $row = 3;
    echo 'pipe-before' . $row . PHP_EOL;
    $data->num += $row;

    // $next($data) $next == $reduce2
    $data = $reduce2($data);

    echo 'pipe-hehind' . $row . PHP_EOL;
    return $data;
};

執行 reduce3

var_dump($reduce3($data));

執行結果

與之前的相同

pipe-before3
pipe-before2
pipe-before1
init start
object(stdClass)#5 (1) {
  ["num"]=>
  int(7)
}
init end
pipe-hehind1
pipe-hehind2
pipe-hehind3
string(4) "init"
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容