初学Laravel难免对项目组织结构、工作流程不太了解,本文以尽量简单的方式向大家做大概介绍。
组织结构说明
成功创建Laravel后,项目组织结构如下图所示。
运行效果
合理配置web服务器后运行,正常运行效果如下。
启动流程分析
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