Laravel 中間件

Laravel 中間件

代碼展現

protected function sendRequestThroughRouter($request)
{
    # $this->app->instance('request', $request);

    # Facade::clearResolvedInstance('request');

    # $this->bootstrap();

    return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());      // $this->dispatchToRouter(),後期繼續
}

new \Illuminate\Routing\Pipeline($this->app):
public function __construct(Container $container = null)
{
    $this->container = $container;
}
public function send($passable)
{
    $this->passable = $passable;

    return $this;
}
public function through($pipes)
{
    ####################################################################################
    #    $this->middleware = [                                                         #
    #        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,    #
    #        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,           #
    #        \App\Http\Middleware\TrimStrings::class,                                  #
    #        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,  #
    #    ];                                                                            #
    ####################################################################################
    $this->pipes = is_array($pipes) ? $pipes : func_get_args();

    return $this;
}
// 中間件的本質
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()
{
    // 接受匿名函數$stack參數和$pipe參數,返回匿名函數,再將此匿名函數做爲第一個參數$stack迭代傳入。
    return function ($stack, $pipe) {
        return function ($passable) use ($stack, $pipe) {
            if ($pipe instanceof Closure) {
                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];
            }
            // 中間件做用是提供了一種方便的機制來過濾進入應用的 HTTP 請求
            // $this->method默認爲handle,可經過via方法進行設置
            return $pipe->{$this->method}(...$parameters);
        };
    };
}
protected function parsePipeString($pipe)
{
    // 不帶參數的pipe(class)或帶參數(參數以,分割)的pipe(class:param1,param2,param3...)
    list($name, $parameters) = array_pad(explode(':', $pipe, 2), 2, []);

    if (is_string($parameters)) {
        $parameters = explode(',', $parameters);
    }
    // $parameters爲[]數組或[param1,param2,param3...]
    return [$name, $parameters];
}
protected function prepareDestination(Closure $destination)
{
    return function ($passable) use ($destination) {
        return $destination($passable);
    };
}

前置條件bootstrap

  1. array_reduce接受三個參數,第一個參數接收數組,第二個參數函數名(也能夠是匿名函數,函數有兩個參數,分別表明$result和$item),第三個參數(可選),該參數將被當成是數組中的第一個值來處理,或者若是數組爲空的話就做爲最終返回值。數組

  2. 匿名函數也叫閉包函數(closures),容許臨時建立一個沒有指定名稱的函數,經過 Closure 類來實現的。閉包

  3. 當對閉包函數進行賦值時,PHP 便會自動將此種表達式轉換成內置類 Closure 的對象對象在進行賦值。app

  4. 當閉包函數使用到 use 使用外部數據時,會在 Closure 對象生成一個 static 屬性數組進行存放。函數

  5. 當閉包函數使用到參數時,會在 Closure 對象生成一個 parameter 屬性數組進行存放。this

public function then(Closure $destination)
    {
        $pipeline = array_reduce(
            array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
        );
        return $pipeline($this->passable);
    }

生成最終匿名函數的過程:spa

array_reduce執行第一次時獲得以下簡化的匿名函數返回,將會繼續做爲第一個參數進行迭代:        
        object(Closure)#id (1) {
          ["static"]=>
          array(2) {
            ["stack"]=>
            object(Closure)#1 (0) { // $this->prepareDestination($destination)
            }
            ["pipe"]=>
            string(15) "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull"
          }
        }
    第二次:
        object(Closure)#id (1) {
          ["static"]=>
          array(2) {
            ["stack"]=>
            object(Closure)#id (1) {
              ["static"]=>
              array(2) {
                ["stack"]=>
                object(Closure)#1 (0) { // $this->prepareDestination($destination)
                }
                ["pipe"]=>
                string(15) "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull"
              }
            }
            ["pipe"]=>
            string(15) "App\Http\Middleware\TrimStrings"
          }
        }
    第三次:
        object(Closure)#id (1) {
          ["static"]=>
          array(2) {
            ["stack"]=>
            object(Closure)#id (1) {
              ["static"]=>
              array(2) {
                ["stack"]=>
                object(Closure)#id (1) {
                  ["static"]=>
                  array(2) {
                    ["stack"]=>
                    object(Closure)#1 (0) { // $this->prepareDestination($destination)
                    }
                    ["pipe"]=>
                    string(15) "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull"
                  }
                }
                ["pipe"]=>
                string(15) "App\Http\Middleware\TrimStrings"
              }
            }
            ["pipe"]=>
            string(15) "Illuminate\Foundation\Http\Middleware\ValidatePostSize"
          }
        }

依次類推,最終獲得一個匿名函數以下(接受一個參數,此匿名函數內部使用上面遞歸形式的$pipe和$stack)。code

function ($passable) {
        if ($pipe instanceof Closure) {
            return $pipe($passable, $stack);
        } elseif (! is_object($pipe)) {
            list($name, $parameters) = $this->parsePipeString($pipe);

            $pipe = $this->getContainer()->make($name); // 實例化middleware

            $parameters = array_merge([$passable, $stack], $parameters);
        } else {
            $parameters = [$passable, $stack];
        }
        return $pipe->{$this->method}(...$parameters);  // 語法糖模式,由於middleware參數無關緊要
    };

最終匿名函數的調用過程(從最外層開始,這就是前面爲何要array_reverse,一層一層往裏撥,總體上的處理是:$pipe如果匿名函數,直接調用。如果字符串,則解析成對應的類和參數,make類,再組成參數數組。最後調用$pipe->handle)router

最外層($pipe=Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode):
    (new \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode)->handle($passable, $stack)
    執行完前置操做後,調用$stack($passable),繼續進行下一層

    下一層($pipe=Illuminate\Foundation\Http\Middleware\ValidatePostSize):
    (new \Illuminate\Foundation\Http\Middleware\ValidatePostSize)->handle($passable, $stack)
    執行完前置操做後,調用$stack($passable),繼續進行下一層

    **以此類推,當每層中間件的前置任務所有完成,即遞歸執行到最裏面一層(路由分發,解析請求,返回響
    應),再由最內層一層一層往回走,執行每層中間件的後置任務。至此,返回本次請求的響應。**

最內層(即路由分發,解析請求,返回響應)的操做代碼展現,後續分析:即執行$destination($passable)匿名函數,位於下面方法中。中間件

protected function prepareDestination(Closure $destination)
    {
        return function ($passable) use ($destination) {
            return $destination($passable);
        };
    }
    $destination:
    protected function dispatchToRouter()
    {
        return function ($request) {
            $this->app->instance('request', $request);

            return $this->router->dispatch($request);
        };
    }

說明: 全部的後置操做,都是執行到最內層,遞歸往回走時纔會執行逐層執行。

相關文章
相關標籤/搜索