zoukankan      html  css  js  c++  java
  • 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我觉得甚是奇怪,按照官网文档,应当在中间输出才对。

    于是开始调试跟踪,发现原来如此:

    在vendorslimslimSlimMiddlewareAwareTrait.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 PsrHttpMessageResponseInterface'
                );
            }
    
            return $result;
        };
    
        return $this;
    }

     $next 即参数为ServerRequestInterface $req, ResponseInterface $res的闭包,而$callable即我们的中间件。

    中间件都添加到堆栈$this->stack[]上了,$next则是栈顶,而$this->seedMiddlewareStack();则把路由中间件第一个压栈了。

    这就是官网调用顺序的流程了。

    然而Hello 为何最后输出则还是费解,于是继续调试。

    在vendorslimslimSlimRoute.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 HttpBody(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没有这个问题呢,因为我们的中间件就是直接输出,并不像路由中间件这么处理。

  • 相关阅读:
    EasyUI Datagrid换页不清出勾选方法
    【HDOJ】4902 Nice boat
    【HDOJ】1688 Sightseeing
    【HDOJ】3584 Cube
    【POJ】2155 Matrix
    【HDOJ】4109 Instrction Arrangement
    【HDOJ】3592 World Exhibition
    【POJ】2117 Electricity
    【HDOJ】4612 Warm up
    【HDOJ】2888 Check Corners
  • 原文地址:https://www.cnblogs.com/xdao/p/slim_middleware_thinking.html
Copyright © 2011-2022 走看看