回首页

laravel 启动流程分析

入口文件

public/index.php

$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);//取出绑定的中间件

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);//把请求放进中间件进行处理
$response->send();//请求发送
$kernel->terminate($request, $response);//终止程序

$app Illuminate\Foundation\Application 实例化的时候做了 三件事

 public function __construct($basePath = null)
    {
        if ($basePath) {
            $this->setBasePath($basePath);
        }

        $this->registerBaseBindings();//绑定app实例即自身到容器中

        $this->registerBaseServiceProviders();//注册服务提供者

        $this->registerCoreContainerAliases();
    }

绑定app实例至容器中

protected function registerBaseBindings()
    {
        static::setInstance($this);

        $this->instance('app', $this);//绑定app实例即自身到容器中

        $this->instance(Container::class, $this);//绑定app实例即自身到容器中

        $this->instance(PackageManifest::class, new PackageManifest(
            new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
        ));//处理安装包相关的,自动发现包
    }

注册基础服务提供者

protected function registerBaseServiceProviders()
    {
        $this->register(new EventServiceProvider($this));//注册服务提供者

        $this->register(new LogServiceProvider($this));

        $this->register(new RoutingServiceProvider($this));
    }
public function register($provider, $options = [], $force = false)
    {
        if (($registered = $this->getProvider($provider)) && ! $force) {//如果已注册则返回
            return $registered;
        }

        // If the given "provider" is a string, we will resolve it, passing in the
        // application instance automatically for the developer. This is simply
        // a more convenient way of specifying your service provider classes.
        if (is_string($provider)) {//是字符串,解析为对象
            $provider = $this->resolveProvider($provider);
        }

        if (method_exists($provider, 'register')) {//方法存在则执行
            $provider->register();
        }

        $this->markAsRegistered($provider);//标记已经注册

        // If the application has already booted, we will call this boot method on
        // the provider class so it has an opportunity to do its boot logic and
        // will be ready for any usage by this developer's application logic.
        if ($this->booted) {
            $this->bootProvider($provider);
        }

        return $provider;
    }

$app 实例化之后 绑定了三个类

处理请求

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

Illuminate\Http\Request

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

     return static::createFromBase(SymfonyRequest::createFromGlobals());//重写SymfonyRequest请求
 }

然后交给中间件处理 App\Http\Kernel处理 ,其父类Illuminate\Foundation\Http\Kernel构造函数使用了依赖注入

//依赖注入 $router `Illuminate\Routing\Router` 到达路由之前或之后,经过这些中间件,有前置中间件和后置中间件之分
 public function __construct(Application $app, Router $router)
    {
        $this->app = $app;
        $this->router = $router;

        $router->middlewarePriority = $this->middlewarePriority;

        foreach ($this->middlewareGroups as $key => $middleware) {//App\Http\Kerne类中 设置的路由组中间件
            $router->middlewareGroup($key, $middleware);
        }

        foreach ($this->routeMiddleware as $key => $middleware) {//App\Http\Kerne类中 设置的路由中间件
            $router->aliasMiddleware($key, $middleware);
        }
    }

处理请求方法

Illuminate\Foundation\Http\Kernel $request 请求实例 主要看这个方法$response = $this->sendRequestThroughRouter($request);

public function handle($request)
    {
        try {
            $request->enableHttpMethodParameterOverride();

            $response = $this->sendRequestThroughRouter($request);//处理请求
        } catch (Exception $e) {
            $this->reportException($e);

            $response = $this->renderException($request, $e);//交给 App\Exceptions\Handler 错误处理
        } catch (Throwable $e) {
            $this->reportException($e = new FatalThrowableError($e));

            $response = $this->renderException($request, $e);
        }

        $this->app['events']->dispatch(
            new Events\RequestHandled($request, $response)
        );//处理完成之后,触发事件 ### 注册基础服务提供者时注册的事件服务提供者

        return $response;
    }
 protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);//绑定请求实例至容器中

        Facade::clearResolvedInstance('request');

        $this->bootstrap();//启动app

        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }

Illuminate\Foundation\Http\Kernel

public function bootstrap()
    {
        if (! $this->app->hasBeenBootstrapped()) {
            $this->app->bootstrapWith($this->bootstrappers());
          //调用了app实例的boostrapWith方法
        }
    }

$this->bootstrappers()返回的是 $bootstrappers 属性

 protected $bootstrappers = [
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,//加载env配置文件
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,//加载config文件夹下的配置文件
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class,//设置错误处理相关函数
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,//注册alias
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class,//注册服务提供者
        \Illuminate\Foundation\Bootstrap\BootProviders::class,//启动服务提供者
    ];

最后Illuminate\Foundation\Application 启动

    public function bootstrapWith(array $bootstrappers)
    {
        $this->hasBeenBootstrapped = true;

        foreach ($bootstrappers as $bootstrapper) {
            $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);

            $this->make($bootstrapper)->bootstrap($this);//取出启动

            $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
        }
    }

ok app启动起来了,现在到真正处理请求的时候 了 Illuminate\Foundation\Http\Kernel sendRequestThroughRouter方法的最后一行

 return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());

(new Pipeline($this->app))设置app实例至管道中 可以通过$app拿到任何你想拿到的东西 (new Pipeline($this->app))->send($request)把要过滤的请求设置进去 (new Pipeline($this->app)) ->send($request) ->through(\$this->app->shouldSkipMiddleware() ? [] : $this->middleware) 把需要通过的中间件设置进去 最重要的是这个

->then($this->dispatchToRouter()) Illuminate\Pipeline\Pipeline

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

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

$destination

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

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

$this->carry()

protected function carry()
    {
        return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                if (is_callable($pipe)) {
                    // If the pipe is an instance of a Closure, we will just call it directly but
                    // otherwise we'll resolve the pipes out of the container and call it with
                    // the appropriate method and arguments, returning the results back out.
                    return $pipe($passable, $stack);
                } elseif (! is_object($pipe)) {
                    list($name, $parameters) = $this->parsePipeString($pipe);

                    // If the pipe is a string we will parse the string and resolve the class out
                    // of the dependency injection container. We can then build a callable and
                    // execute the pipe function giving in the parameters that are required.
                    $pipe = $this->getContainer()->make($name);

                    $parameters = array_merge([$passable, $stack], $parameters);
                } else {
                    // If the pipe is already an object we'll just make a callable and pass it to
                    // the pipe as-is. There is no need to do any extra parsing and formatting
                    // since the object we're given was already a fully instantiated object.
                    $parameters = [$passable, $stack];
                }

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

array_reduce 运行到最后返回的是

            function ($passable) use ($stack, $pipe) {
                if (is_callable($pipe)) {
                    // If the pipe is an instance of a Closure, we will just call it directly but
                    // otherwise we'll resolve the pipes out of the container and call it with
                    // the appropriate method and arguments, returning the results back out.
                    return $pipe($passable, $stack);
                } elseif (! is_object($pipe)) {
                    list($name, $parameters) = $this->parsePipeString($pipe);

                    // If the pipe is a string we will parse the string and resolve the class out
                    // of the dependency injection container. We can then build a callable and
                    // execute the pipe function giving in the parameters that are required.
                    $pipe = $this->getContainer()->make($name);

                    $parameters = array_merge([$passable, $stack], $parameters);
                } else {
                    // If the pipe is already an object we'll just make a callable and pass it to
                    // the pipe as-is. There is no need to do any extra parsing and formatting
                    // since the object we're given was already a fully instantiated object.
                    $parameters = [$passable, $stack];
                }

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

然后运行 $pipeline($this->passable); 是从数组的最后一个元素开始运行的,所以用array_reverse()反转下数组的位置,这样第一个元素放在了最后一个第一运行

中间件格式

class xxx
{

    public function handle($request, Closure $next, $guard = null)
    {
       //todo something

        return $next($request);
    }
}

$next 其实就是上面那个返回的闭包

$pipe->{$this->method}(...$parameters) 运行中间件的handle方法

$parameters 是$parameters = [$passable, $stack]; $passable是请求$request请求的值 $stack 是下一中间件的闭包

假如通过了所有中间件,最后运行,array_reduce的初始值 Illuminate\Pipeline\Pipeline $this->prepareDestination($destination)

 function ($passable) use ($destination) {
            return $destination($passable);
        };

$destination

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

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

成功的通过了中间件,进入了控制器 然后

//todo something 你自己的业务逻辑