Lumen 源码阅读记录 ---- 路由 (2)

@liubb  October 23, 2018

接上节。

$app->router->group([
    'namespace' => 'App\Http\Controllers',
], function ($router) {
    require __DIR__.'/../routes/web.php';
});

$app->router 上节讲到是 Router.php

public function group(array $attributes, \Closure $callback)
{
    if (isset($attributes['middleware']) && is_string($attributes['middleware'])) {
        $attributes['middleware'] = explode('|', $attributes['middleware']);
    }

    $this->updateGroupStack($attributes);

    call_user_func($callback, $this);

    array_pop($this->groupStack);
}

看到

call_user_func($callback, $this);

即引入 web.php 路由文件,接下来调用 get post等方法,将路由绑定在 $this->routes 变量

接下来看到

$app->run();

此处有个知识点,app是 Application.php类,不存在run方法,从何来?使用了语法糖,RoutesRequests有run方法

use Concerns\RoutesRequests,
Concerns\RegistersExceptionHandlers;

接下来看到 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);
    }
}

先看

$response = $this->dispatch($request);
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));
    }
}

看向 $this->parseIncomingRequest($request) 这里返回的是 对 $_GET $_POST 之类的封装,不赘述。

protected function parseIncomingRequest($request)
{
    if (! $request) {
        $request = Request::capture();
    }

    $this->instance(Request::class, $this->prepareRequest($request));

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

回到上步,看向 sendThroughPipeline(),此处的 middleware是空,直接 执行$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();
}

执行then()

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)
    );
 }
 //此处返回的是 $this->routes的数组 判断本次请求是否在配置的路由内,这里只考虑在的情况
 $this->router->getRoutes()[$method.$pathInfo]

进入

 $this->handleFoundRoute([true, $this->router->getRoutes()[$method.$pathInfo]['action'], []]);
 protected function handleFoundRoute($routeInfo)
 {
    $this->currentRoute = $routeInfo;

    $this['request']->setRouteResolver(function () {
        return $this->currentRoute;
    });

    $action = $routeInfo[1];

    // Pipe through route middleware...
    if (isset($action['middleware'])) {
        $middleware = $this->gatherMiddlewareClassNames($action['middleware']);

        return $this->prepareResponse($this->sendThroughPipeline($middleware, function () {
            return $this->callActionOnArrayBasedRoute($this['request']->route());
        }));
    }

    return $this->prepareResponse(
        $this->callActionOnArrayBasedRoute($routeInfo)
    );
}

这里有个知识点,$this['request']是什么?
Application implements ArrayAccess
继承ArrayAccess,相当于访问 offsetGet('request')

public function offsetGet($key)
{
    return $this->make($key);
}

接下来看到

if (isset($action['middleware'])) {
    //返回中间件,不展开讲解
    $middleware = $this->gatherMiddlewareClassNames($action['middleware']);

    return $this->prepareResponse($this->sendThroughPipeline($middleware, function () {
        return $this->callActionOnArrayBasedRoute($this['request']->route());
    }));
}

看到

$this->sendThroughPipeline($middleware, function () {
    return $this->callActionOnArrayBasedRoute($this['request']->route());
})
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 send($passable)
{
    $this->passable = $passable;

    return $this;
}
public function through($pipes)
{
    $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);
}

这里的then方法是重点,大意就是将循环 $this->prepareDestination($destination) 和 array_reverse($this->pipes) 放到 $this->carry() 里,也就是将请求放在所有中间件中过一遍。

最后执行

function () {
    return $this->callActionOnArrayBasedRoute($this['request']->route());
}

下面就是实例化具体的类了,不展开讲。
有个细节,怎么讲Request $request 注入到每个方法里,这里在调用方法前,有个make的细节,于是每个方法里就可以通过$request来获取参数了

protected static function callClass($container, $target, array $parameters = [], $defaultMethod = null)
{
    $segments = explode('@', $target);

    // We will assume an @ sign is used to delimit the class name from the method
    // name. We will split on this @ sign and then build a callable array that
    // we can pass right back into the "call" method for dependency binding.
    $method = count($segments) == 2
                    ? $segments[1] : $defaultMethod;

    if (is_null($method)) {
        throw new InvalidArgumentException('Method not provided.');
    }

    return static::call(
        $container, [$container->make($segments[0]), $method], $parameters
    );
}


添加新评论