美文网首页
lumen router

lumen router

作者: 小东班吉 | 来源:发表于2019-07-17 18:36 被阅读0次

laravel router

router

app.php中初始化的时候实例化router类给app::router,参数为app对象

/**
 * Bootstrap the router instance.
 *
 * @return void
 */
public function bootstrapRouter()
{
    $this->router = new Router($this);
}


\Laravel\Lumen\Application 引入了\Laravel\Lumen\Concerns\RoutesRequests,在初始化完成后执行里面的\Laravel\Lumen\Concerns\RoutesRequests::run方法,大致分为解析请求,以及发送请求

public function run($request = null)
{
    $response = $this->dispatch($request);

    if ($response instanceof SymfonyResponse) {
        $response->send();
    } else {
        echo (string) $response;
    }

    if (count($this->middleware) > 0) {
        $this->callTerminableMiddleware($response);
    }
}

public function dispatch($request = null)
{
    list($method, $pathInfo) = $this->parseIncomingRequest($request);
    try {
        return $this->sendThroughPipeline($this->middleware, function () use ($method, $pathInfo) {
            if (isset($this->router->getRoutes()[$method.$pathInfo])) {
                return $this->handleFoundRoute([true, $this->router->getRoutes()[$method.$pathInfo]['action'], []]);
            }

            return $this->handleDispatcherResponse(
                $this->createDispatcher()->dispatch($method, $pathInfo)
            );
        });
    } catch (Exception $e) {
        return $this->prepareResponse($this->sendExceptionToHandler($e));
    } catch (Throwable $e) {
        return $this->prepareResponse($this->sendExceptionToHandler($e));
    }
}

解析构造request请求

protected function parseIncomingRequest($request)
{
    if (! $request) {
        $request = Request::capture();
    }
$this->instance(Request::class, $this->prepareRequest($request));

return [$request->getMethod(), '/'.trim($request->getPathInfo(), '/')];
}

\Laravel\Lumen\Concerns\RoutesRequests::dispatch方法传入请求request,request不存在则创建一个(\Laravel\Lumen\Concerns\RoutesRequests::parseIncomingRequest)

public static function capture()
{
    static::enableHttpMethodParameterOverride();

    return static::createFromBase(SymfonyRequest::createFromGlobals());
}

public static function createFromBase(SymfonyRequest $request)
{
    if ($request instanceof static) {
        return $request;
    }
    $content = $request->content;
    $request = (new static)->duplicate(
        $request->query->all(), $request->request->all(), $request->attributes->all(),
        $request->cookies->all(), $request->files->all(), $request->server->all()
    );
    $request->content = $content;
    $request->request = $request->getInputSource();
    return $request;
}
  1. 启用对http method的重写

  2. \Illuminate\Http\Request::createFromBase 根据symfony组件创建一个基础请求

    public static function createFromGlobals()
    {
        $request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER);
    if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
        && \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])
    ) {
        parse_str($request->getContent(), $data);
        $request->request = new ParameterBag($data);
    }
    
    return $request;
    }
    
    1. 参数 SymfonyRequest::createFromGlobals()就是Symfony组件有关请求参数处理的返回,主要处理了php的超全局变量比如get,post数据等,构建一个request对象

      1. \Symfony\Component\HttpFoundation\Request::createRequestFromFactory这里就是创建一个request,可以看到下面实力化当前类,new static()注意(new static 和new self的区别,new static的实例是由调用者决定的),调用initialize方法对对象进行赋值操作

        private static function createRequestFromFactory(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
        {
            if (self::$requestFactory) {
                $request = (self::$requestFactory)($query, $request, $attributes, $cookies, $files, $server, $content);
        
                if (!$request instanceof self) {
                    throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.');
                }
        
                return $request;
            }
        
            return new static($query, $request, $attributes, $cookies, $files, $server, $content);
        }
        
      2. \Symfony\Component\HttpFoundation\ServerBag::getHeaders 从server取出来数据,设置header头,

        1. server里的http_开头的截取http_后边的都以key=>value形式存储起来,其余的['CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true]覆盖

        2. 判断PHP 授权认证PHP_AUTH_USER是否存在存在,则PHP_AUTH_PW和PHP_AUTH_USER存起来,如果没有 PHP_AUTH_USER ,则在 server 中寻找关于 HTTP_AUTHORIZATION、REDIRECT_HTTP_AUTHORIZATION、AUTHORIZATION 的key value建值对,(这里针对apache,php-cgi不会把HTTP Basic Auth 传递给php,作者建议再.htaccess里添加)

          \Symfony\Component\HttpFoundation\ServerBag::getHeaders
          
      3. 如果 请求的方法在['PUT', 'DELETE', 'PATCH']里面并且 from头不是application/x-www-form-urlencoded表单请求,则解析生成的request请求的内容给具体的变量,重新覆盖赋值给$request->request属性

    2. 从symfony实例创建的request对象克隆并重写一些方法,对里面的空的文件对象过滤掉\Illuminate\Http\Request::duplicate

  3. request注册到容器中,这里会在request对象上设置用户auth回调以及当前路由的回调\Laravel\Lumen\Application::prepareRequest,这在下一步发送请求的时候由管道去执行。

发送请求

这里发送请求,\Laravel\Lumen\Concerns\RoutesRequests::sendThroughPipeline这里处理了中间件的情况,中间件是在app初始化的时候绑定上的(\Laravel\Lumen\Concerns\RoutesRequests::middleware) ,通过管道发送对象,这里设置了发送的对象,以及所用的中间件,然后在then方法里统一处理\Illuminate\Pipeline\Pipeline::then()

protected function sendThroughPipeline(array $middleware, Closure $then)
{
    if (count($middleware) > 0 && ! $this->shouldSkipMiddleware()) {
        return (new Pipeline($this))
            ->send($this->make('request'))
            ->through($middleware)
            ->then($then);
    }
    return $then();
}
public function then(Closure $destination)
{
    $pipeline = array_reduce(
        array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
    );

    return $pipeline($this->passable);
}

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];
            }

            $response = method_exists($pipe, $this->method)
                ? $pipe->{$this->method}(...$parameters)
            : $pipe(...$parameters);

            return $response instanceof Responsable
                ? $response->toResponse($this->container->make(Request::class))
                : $response;
        };
    };
}
  1. 这里依赖于PHP的reduce函数,array_reduce函数处理把app初始化绑定的中间件一个一个的处理,这里会返回一个闭包,\Illuminate\Pipeline\Pipeline::carry 这是reduce的匿名函数,每次处理都会返回一个闭包,闭包里面处理了发送本次请求的回调,如果管道流pipe(中间件)是一个匿名函数则直接调用匿名函数,返回结果,如果不是一个对象则解析获取管道类名称以及参数,注入容器,如果是一个对象,合并参数,最后执行中间件handle函数,返回一个response对象。App\Http\Middleware\CORSMiddleware假设这是中间件,则method_exists判断handle在中间中,执行handle,hanlde中next(request),相当于执行上面\Illuminate\Pipeline\Pipeline::prepareDestination这里返回的匿名函数了。

  2. 这里有个不太容易理解的地方,假设有两个中间件,App\Http\Middleware\CORSMiddleware1, App\Http\Middleware\CORSMiddleware2

    1. \Illuminate\Pipeline\Pipeline::prepareDestination函数返回的闭包是第一个stack的值,pipe为 App\Http\Middleware\CORSMiddleware3 中间件事一个字符串 carry第一次返回的暂记为callback1,
    2. 在第二次迭代的时候,callback1作为stack的值,此时pipe的值是 App\Http\Middleware\CORSMiddleware2
      这里闭包是闭包套闭包,重点就在结束的时候统一执行,也能解释了为什么要在上面把数组的顺序反转下,就是因为在最终生成的闭包里保证执行顺序是一开始设定的。
    class CORSMiddleware1
    {
        public function handle($name, Closure $next)
        {
            echo "这是中间件1\n";
            $next($name);
        }
    }
    class CORSMiddleware2
    {
        public function handle($name, Closure $next)
        {
            echo "这是中间件2\n";
            $next($name);
        }
    }
    $prepareDestination = function () {
        return function ($name) {
            echo "这是最后一块的洋葱,那么中间件都执行完了,是不是到我了-- {$name}";
        };
    };
    
    function carry()
    {
        return function ($stack, $pipe) {
            return function ($name) use ($stack, $pipe) {
                return (new $pipe())->handle($name, $stack);
            };
        };
    }
    
    $res = array_reduce(array_reverse([CORSMiddleware1::class, CORSMiddleware2::class]), carry(), $prepareDestination());
    var_dump($res());
    
  3. 上一步中间件处理完成后调用next()最终会执行一开始设置的发送请求设置的回调,寻找路由就是\Laravel\Lumen\Concerns\RoutesRequests::sendThroughPipeline这个方法的第二个参数

    1. 判断路由是否存在,存在则解析找到路由,中间件如果存在,重复上一个步骤,由管道处理中间件,处理完中间件后调用路由的回调函数,准备发送响应\Laravel\Lumen\Concerns\RoutesRequests::callActionOnArrayBasedRoute
    2. 如果路由不存在,则创建一个路由实例,实例不存在则创建一个FastRoute实例,调用addRoute添加进去,重复上面分发路由

相关文章

  • lumen router

    laravel router router app.php中初始化的时候实例化router类给app::route...

  • 小程序Lumen API开发

    Lumen API开发 Laravel和Lumen的区别:Lumen轻量级框架,集合了Laravel的优美语法,支...

  • 学习 Lumen 用户认证 (一)

    好久没写 PHP 代码了,尤其是 Lumen,我是 Lumen 的忠实用户,自从面世开始,我就将 Lumen 作为...

  • luman如何搭建swoole

    1,首先搭建lumen框架,使用composer命令(https://lumen.laravel-china.or...

  • Lumen 实现自定义LOG目录

    问题背景: Lumen 的runtime Log 默认保存在项目目录 storage/logs/lumen.log...

  • lumen 目录整合

    lumen 目录清单 在lumen 基础上添加了 Helpers Models Libs 三个目录 ...

  • Lumen如何生成APP_KEY

    在Lumen控制台运行php artisan key:generate提示: 原因是Lumen本身并不带Larav...

  • lumen +swoole

    lumen搭建 lumen搭建项目 添加dingo 安装扩展 修改配置 app.php 修改.env文件 配置sw...

  • 不用Lumen Auth,迁移使用TP AUTH

    看到lumen auth 要更改几个文件,不是很喜欢。就把Thinkphp上的auth权限验证迁移到lumen上了...

  • pipeline-中间件的实现

    1、 lumen(5.6) 中间件 类型 lumen 中的中间件份为两种(bootstrap/app.php) :...

网友评论

      本文标题:lumen router

      本文链接:https://www.haomeiwen.com/subject/buvclctx.html