Laravel 路由管道原始碼分析

chonghua_123發表於2018-12-25

前言 只為學習

Illuminate\Routing\Pipeline 分析

1. 引入

use Closure;//php匿名函式類
use Exception;//php異常類
use Throwable;//php錯誤處理類
use Illuminate\Http\Request;//框架請求類
use Illuminate\Contracts\Debug\ExceptionHandler;//框架異常處理介面
use Illuminate\Pipeline\Pipeline as BasePipeline;//框架管道核心類
use Symfony\Component\Debug\Exception\FatalThrowableError;//Symfony錯誤處理類

2. 繼承BasePipeline

class Pipeline extends BasePipeline //繼承BasePipeline

3. 過載父類prepareDestination

 protected function prepareDestination(Closure $destination)
    {
        return function ($passable) use ($destination) {
            try {
                return $destination($passable);
            } catch (Exception $e) {
                return $this->handleException($passable, $e);//丟擲異常
            } catch (Throwable $e) {
                return $this->handleException($passable, new FatalThrowableError($e));
            }
        };
    }

4. 過載父類carry

1、 parent::carry

 protected function carry()
    {
        return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                try {
                    $slice = parent::carry();
                    $callable = $slice($stack, $pipe);
                    return $callable($passable);
                } catch (Exception $e) {
                    return $this->handleException($passable, $e);
                } catch (Throwable $e) {
                    return $this->handleException($passable, new FatalThrowableError($e));
                }
            };
        };
    }

5. 處理異常

1、bound
2、make
3、report
4、render
5、withException

  protected function handleException($passable, Exception $e)
    {
        if (! $this->container->bound(ExceptionHandler::class) ||
            ! $passable instanceof Request) {
            throw $e;
        }

        $handler = $this->container->make(ExceptionHandler::class);

        $handler->report($e);

        $response = $handler->render($passable, $e);

        if (method_exists($response, 'withException')) {
            $response->withException($e);
        }

        return $response;
    }

Illuminate\Pipeline\Pipeline

1. 引入

use Closure;//php匿名函式類
use RuntimeException;//php執行異常類
use Illuminate\Http\Request;//框架請求類
use Illuminate\Contracts\Container\Container;//框架容器介面
use Illuminate\Contracts\Support\Responsable;//框架響應介面
use Illuminate\Contracts\Pipeline\Pipeline as PipelineContract;//框架管道介面

2. 成員變數

    protected $container;//容器
    protected $passable;//正在通過管道的物件
    protected $pipes = [];//管道陣列
    protected $method = 'handle';//呼叫每個管道的方法

3. 建構函式

public function __construct(Container $container = null)
    {
        $this->container = $container;//傳入容器(應用)
    }

4. 設定通過管道傳送的物件

 public function send($passable)
    {
        $this->passable = $passable;//傳入的Illuminate\Http\Request

        return $this;
    }

5. 管道陣列

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

6. 設定呼叫管道的方法

public function via($method)
    {
        $this->method = $method;
        return $this;
    }

7. 使用最終回撥執行管道

1、array_reduce
2、carry(走的是子類過載的carry方法)
3、prepareDestination(走的是子類過載的prepareDestination的方法)

 public function then(Closure $destination)
    {
        $pipeline = array_reduce(
            array_reverse($this->pipes), //翻轉陣列
            $this->carry(),//需要執行的自定義函式
            $this->prepareDestination($destination)//引數 carry裡邊的$stack引數
        );
        return $pipeline($this->passable);//相當於執行dispatchToRouter($request)
    }

8. carry

1、parsePipeString

    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)) {
                    [$name, $parameters] = $this->parsePipeString($pipe);
                    $pipe = $this->getContainer()->make($name);//解析中介軟體
                    $parameters = array_merge([$passable, $stack], $parameters);//合併引數
                } else {
                    $parameters = [$passable, $stack];
                }
                $response = method_exists($pipe, $this->method)//判斷是否存在方法
                                ? $pipe->{$this->method}(...$parameters)//執行
                                : $pipe(...$parameters);//例項
                return $response instanceof Responsable
                            ? $response->toResponse($this->container->make(Request::class))//返回響應體
                            : $response;
            };
        };
    }

9. 得到最後匿名函式

 protected function prepareDestination(Closure $destination)
    {
        return function ($passable) use ($destination) {
            return $destination($passable);//返回匿名函式 
        };
    }

10. 解析傳入的管道字串(中介軟體名稱)

    protected function parsePipeString($pipe)
    {
        [$name, $parameters] = array_pad(explode(':', $pipe, 2), 2, []);

        if (is_string($parameters)) {
            $parameters = explode(',', $parameters);
        }
        return [$name, $parameters];
    }

11. 獲取當前容器

    protected function getContainer()
    {
        if (! $this->container) {
            throw new RuntimeException('A container instance has not been passed to the Pipeline.');
        }
        return $this->container;
    }

相關文章