Laravel5工作流程分析

2018-05-01  本文已影响0人  bluemap

初学Laravel难免对项目组织结构、工作流程不太了解,本文以尽量简单的方式向大家做大概介绍。

组织结构说明

成功创建Laravel后,项目组织结构如下图所示。

组织结构

运行效果

合理配置web服务器后运行,正常运行效果如下。

运行效果

启动流程分析

Laravel是如何展示上面网页的呢,下面我们来慢慢揭晓。
下图为我简单整理的启动流程序列图,估计不太清晰,建议大家下载原图查看。


laravel工作流程
备注:为了图片简单,上面序列图中,大部分函数调用的返回箭头我是省略掉了,相信略有基础的人应该都可以看懂。

一、创建App对象

在Laravel中一个app对象就是一个Container(服务容器,概念不清晰的推荐阅读科普好文官方介绍),这就意味着app可以绑定各类Provider(服务提供者)
public/index.php是程序入口,public/index.php中通过如下代码创建一个app对象。

$app = require_once __DIR__.'/../bootstrap/app.php';

进入bootstrap/app.php文件,我们看到如下代码,该代码创建了一个Application对象实例。

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

进入Application源文件,我们可以看到这样的类定义:

class Application extends Container implements ApplicationContract, HttpKernelInterface

可以看出来Application就是一个Container,同时Application也继承了ApplicationContract及HttpKernelInterface接口。
再深入一点,我们可以看到ApplicationContract主要定义了一组获取项目路径、环境信息、Provider(服务提供者)的接口,及程序启动相关的boot()、booted()等接口。而HttpKernelInterface主要定义了http相关的事件处理接口handle()。

二、绑定服务

我们继续阅读bootstrap/app.php文件,可以看到如下代码

//注册一个App\Http\Kernel服务Class,这个类主要用于http相关事件处理
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
//注册一个App\Console\Kernel服务Class,这里我还不太清楚该类的作用
$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);
//注册异常处理Handler,没深入去研究,看代码基本上是接受到异常后还是交给父类处理
$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

上面代码中很关键的一个点是注册App\Http\Kernel,从上面流程图你可以大致感觉到这个类的重要性。

如果你困惑为什么注册的函数名叫singleton,建议你看看函数内部实现,其实内部就是调用的Container->bind()方法,只是对外换了个名字而已。

通常,绑定并不会实例化对象,绑定可以简单理解为通过一个字典将interface跟具体的class(或匿名函数Closure)关联起来,其中字典的key就是interface的名字,字典的value就是class名字,理解这一点非常重要,因为这是Container实现di(dependency injection依赖注入)和ioc(Inversion Of Control控制反转)很重要的一环。

三、实例化各类服务及路由处理

回到public/index.php,我们看到如下代码。

//实例化一个kernel对象。
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

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

上面代码中app->make才是真正将App\Http\Kernel对象实例化。怎么实例化的呢?前面我们有讲到,绑定就是将interface名跟与之对应的class存入一个字典中,而make就是通过interface名找到对应的class名,并用反射机制创建对应的class实例。
kernel->handle()传入一个request并返回一个response对象。别看这里只有一行代码,实际上做的事情非常之多,该函数内部调用的核心代码如下:

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

        Facade::clearResolvedInstance('request');

        $this->bootstrap();

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

其中bootstrap()接口会调用app相关方法来实例化LoadConfiguration、BootProviders、RegisterProviders等对象。
app对象通过读取config/app.php配置文件来设置App\Providers\RouteServiceProvider、
App\Providers\AppServiceProvider等。

   'providers' => [
        //是的,为了简洁,这里我删除了一些系统默认provider,大家可以去查阅源码
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,

    ],

获取完配置信息之后app会初始化一个App\Providers\RouteServiceProvider对象,并调用其boot()方法,boot()最终会设置好程序的路由映射关系。
RouteServiceProvider是很重要的一个类,我们看下路由映射关系代码。

    protected function mapWebRoutes()
    {
        Route::middleware('web')
             ->namespace($this->namespace)
             ->group(base_path('routes/web.php'));
    }

    /**
     * Define the "api" routes for the application.
     *
     * These routes are typically stateless.
     *
     * @return void
     */
    protected function mapApiRoutes()
    {
        Route::prefix('api')
             ->middleware('api')
             ->namespace($this->namespace)
             ->group(base_path('routes/api.php'));
    }

通过代码我们发现,web接口对应的路由配置文件是routes/web.php。
打开routes/web.php,我们可以看到如下代码。

Route::get('/', function () {
    return view('welcome');
});

从代码我们大致能够猜测,当用户访问网站首页的时候将返回一个'welcome'页面,而这个页面实际上就是resources/views/welcome.blade.php。我们可以debug一下view(),其内部有一组规则将'welcome'映射到welcome.blade.php文件。
设置好路由之后kernel对象会调用dispatchToRouter(),并最终生成一个response对象,response对象里面包含welcome.blade.php返回的网页数据。

输出内容

在回到public/index.php,下面的事情就变得简单。

//将页面内容输出到调用者(这里指浏览器)
$response->send();
//扫尾工作
$kernel->terminate($request, $response);

以上是Laravel工作流程的分析,如果感觉还是不太理解,建议认真阅读Laravel中文手册
同时也推荐大家看看如下推荐文章,不同的作者有不同的介绍方式,个人感悟也可能不一样。
https://www.jianshu.com/p/509a8dd5654e
https://www.jianshu.com/p/63a3d76e7aca

上一篇下一篇

猜你喜欢

热点阅读