Laravel5工作流程分析

初学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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,665评论 19 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 179,243评论 25 708
  • core package 概要:Core是所有其他包的基础包.它提供了大部分功能包括metadata,templa...
    LOVE小狼阅读 2,879评论 0 3
  • 与摩拜单车创始人胡玮炜的自行车情愫相比,我觉得自行车就是我的死敌!它根本不被我驾驭,我不让我驯服,它不是我的好朋友...
    银珠珠阅读 361评论 0 0
  • 文:ShakespeareSky(莎士比亚斯基) 第三节 运动会 11月3日召开一年一度的秋季运动会,这运动会开得...
    ShakespeareSky阅读 650评论 0 0

友情链接更多精彩内容