slimphp中間件調用流程的理解


slimphp是一款微型php框架,主要是處理http請求,並調用合適的程序處理,並返回一個http響應。

它遵循php的psr7規范,可以很方便的集成其它遵循psr7規范的php組建。

當讀到中間件時,官網給出了,如下所示的圖

 

試驗如下:

$mw1 = function ($request, $response, $next) {
    echo('middleware 1  start <br>');
    $response = $next($request, $response);
    echo('middleware 1  end <br>');
    return $response;
};


$mw2 = function ($request, $response, $next) {
    echo('middleware 2  start <br>');
    $response = $next($request, $response);
    echo('middleware 2  end <br>');
    return $response;
};


$app->get('/mw', function ($request, $response, $args) {
    echo(' Hello <br>');
    return $response;
})->add($mw1)->add($mw2);

輸出為:

middleware 2 start 
middleware 1 start 
middleware 1 end 
middleware 2 end 
Hello 

 
不難看出實際是
1、調用$mw2 輸出 middleware 2 start  
2、調用$mw2里的$next即$mw1 輸出middleware 1 start 
3、$mw1再調用$next,而此時沒有中間件了,直接輸出了middleware 1 end  
4、由於$mw2還沒return,還在調用棧里,接着輸出middleware 2 end
5、對於這個hello我覺得甚是奇怪,按照官網文檔,應當在中間輸出才對。

於是開始調試跟蹤,發現原來如此:

在vendor\slim\slim\Slim\MiddlewareAwareTrait.php里有如下代碼:

protected function addMiddleware(callable $callable)
{
    if ($this->middlewareLock) {
        throw new RuntimeException('Middleware can’t be added once the stack is dequeuing');
    }

    if (is_null($this->stack)) {
        $this->seedMiddlewareStack();
    }
    $next = $this->stack->top();
    $this->stack[] = function (ServerRequestInterface $req, ResponseInterface $res) use ($callable, $next) {
        $result = call_user_func($callable, $req, $res, $next);
        if ($result instanceof ResponseInterface === false) {
            throw new UnexpectedValueException(
                'Middleware must return instance of \Psr\Http\Message\ResponseInterface'
            );
        }

        return $result;
    };

    return $this;
}

 

 $next 即參數為ServerRequestInterface $req, ResponseInterface $res的閉包,而$callable即我們的中間件。

中間件都添加到堆棧$this->stack[]上了,$next則是棧頂,而$this->seedMiddlewareStack();則把路由中間件第一個壓棧了。

這就是官網調用順序的流程了。

 

然而Hello 為何最后輸出則還是費解,於是繼續調試。

在vendor\slim\slim\Slim\Route.php里發現了痕跡:

__invoke函數中

        if ($this->outputBuffering === false) {
            $newResponse = $handler($this->callable, $request, $response, $this->arguments);
        } else {
            try {
                ob_start();
                $newResponse = $handler($this->callable, $request, $response, $this->arguments);
                $output = ob_get_clean();
            } catch (Exception $e) {
                ob_end_clean();
                throw $e;
            }
        }

關鍵是$output = ob_get_clean();

我們的echo輸出被路由中間件攔截了,並放入了$response->getBody()->write($output);

        if (!empty($output) && $response->getBody()->isWritable()) {
            if ($this->outputBuffering === 'prepend') {
                // prepend output buffer content
                $body = new Http\Body(fopen('php://temp', 'r+'));
                $body->write($output . $response->getBody());
                $response = $response->withBody($body);
            } elseif ($this->outputBuffering === 'append') {
                // append output buffer content
                $response->getBody()->write($output);
            }
        }

在$response返回時才會輸出,所以上面的Hello 是最后輸出的。

那為啥$mw1、$mw2沒有這個問題呢,因為我們的中間件就是直接輸出,並不像路由中間件這么處理。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM