对laravel中核心概念的理解


一. 请求周期(Lifecycle)

laravel的生命周期从public/index.php文件开始,public文件夹主要存放了web前端资源文件及配置文件,index.php作为请求的入口,主要对laravel应用的初始化和对请求生命周期的管理。在index.php中,主要实现了对第三方依赖的加载,生成服务器容器及处理请求。

1.加载composer依赖

1
require __DIR__.'/../vendor/autoload.php';

同js的包管理工具npm一样,通过composer加载vendor目录下的第三方依赖库。

2.生成服务容器,app实例。通过服务提供者向容器注册核心组件(HttpKernel,ConsoleKernel,ExceptionHandler)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// public/index.php
$app = require_once __DIR__.'/../bootstrap/app.php';
// bootstrap/app.php
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);

创建服务器容器,通过服务提供器方法绑定http的接口与实现,Console的接口与实现,Debug的接口与实现。

3.处理请求

1
2
3
4
5
6
7
8
9
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
$response->send();
$kernel->terminate($request, $response);

通过make方法实例化Illuminate\Contracts\Http\Kernel,通过handle方法处理请求,生成并发送响应,通过terminate结束请求。


二. 服务器容器(Service Container)

服务器容器是存放类的实例的容器,在laravel运行过程中,自动加载需要用到的类对象,注册到容器中,通过容器管理其生命周期,在业务逻辑的实现过程中可以直接通过容器获取使用,无需关心其生成。把对类的创建和管理权的转移称为控制反转,通过控制反转减少对第三的类的控制权并统一交由容器管理。例:

1
2
3
4
5
6
7
8
9
10
11
class A
{
protected $b;
public __construct()
{
$this->b = new B();
}
}

控制反转(IoC)后,将类B的控制权交由服务器容器app。

1
2
3
4
5
6
7
8
9
class A
{
protected $b;
public __construct()
{
$this->app->make('\B');
}
}

laravel中服务器容器(app)通过bind方法把需要绑定的类的实例注册到容器中,使用时,通过make方法解析。绑定需要服务提供方法,操作一般在ServiceProviders中的register方法中,最基本的绑定是容器的bind方法,它接受一个类名和一个参数为容器实例的闭包来获取实例

1
2
3
$this->app->bind('\B', function ($app) {
return new B();
});

另外,singleton方法与bind方法类似,不同的是singleton方法将类或接口绑定到只能解析一次的容器中,即单例。
在使用时,通过app的make方法解析,

1
2
3
4
$api = $this->app->make('\B');
// 如果类的依赖项不能通过容器去解析,那你可以通过将它们作为关联数组传递到 makeWith 方法来注入它们。
$api = $this->app->makeWith('\B' ['id' => 1]);


三. 服务提供者(Service Providers)

服务提供是laravel中十分重要的概念,在laravel生命周期和服务容器的实现中都有体现,也是实现Facade和合约(Contracts)的方法。
所有服务提供器都会继承 Illuminate\Support\ServiceProvider 类。大多数服务提供器都包含 register 和 boot 方法。在 register 方法中,只需要绑定类到服务容器中,在 root 方法中做视图相关的操作。

1.Service Providers绑定接口与实现

在Repositories文件夹下新建一个接口与实现类:

接口类:

1
2
3
4
5
6
7
8
namespace App\Repositories;
interface HouseInterface
{
public function selectAll();
public function findOne($id);
}

实现类:

1
2
3
4
5
6
7
8
9
10
11
12
namespace App\Repositories;
class House implements HouseInterface
{
public function selectAll() {
return ['all' => [1, 2, 3, 4, 5]];
}
public function findOne($id) {
return $id;
}
}

然后,在Provider中新建一个继承ServiceProvider的类HouseServiceProvider,在register方法中绑定接口与实现,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class HouseServiceProvider extends ServiceProvider
{
public function boot() { }
public function register()
{
$this->app->bind(
'App\Repositories\HouseRepositoryInterface',
'App\Repositories\DbHouseRepository'
);
}
}

laravel中所有的Service Provider需要在config/app.php中providers数组中配置服务

1
2
3
4
5
'providers' => [
// 省略
App\Providers\HouseServiceProvider::class,
// 省略
]

这样就通过Server Provider实现了一个Contract,通过参数注入调用,

1
2
3
4
5
6
7
8
9
10
11
12
13
namespace App\Http\ViewComposers;
use App\Repositories\HouseInterface;
class ProfileComposer
{
protected $house;
public function __construct(HouseInterface $house)
{
$this->house = $house;
}
}

2.Service Providers实现视图合成器

新建类ComposerServiceProvider,继承ServiceProvider,在boot,调用View的composer方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace App\Providers;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider
{
public function boot()
{
// 使用基于类的 composer...
View::composer(
'profile', 'App\Http\ViewComposers\ProfileComposer'
);
}
}

在config/app.php中providers数组中配置服务,

1
2
3
4
5
'providers' => [
// 省略
App\Providers\ComposerServiceProvider::class,
// 省略
]

注册了视图合成器,每次渲染 profile 视图时都会执行 ProfileComposer@compose 方法。定义视图合成器类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
namespace App\Http\ViewComposers;
use Illuminate\View\View;
use App\Repositories\HouseInterface;
class ProfileComposer
{
protected $house;
public function __construct(HouseInterface $house)
{
$this->house = $house;
}
/**
* 将数据绑定到视图。
*
* @param View $view
* @return void
*/
public function compose(View $view)
{
$view->with('count', $this->house->findOne(2));
}
}

需要注意的是,Service Provider中register方法先与boot方法执行。


四. Facades

Facades 是容器中类的静态代理,通过静态方法调用存放在容器中对象方法,表现形式为

1
2
3
4
5
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});

代码中Cache继承了Facade类,重写getFacadeAccessor方法

1
2
3
4
5
6
7
class Cache extends Facade
{
protected static function getFacadeAccessor()
{
return 'cache';
}
}

在laravel中,Illuminate\Support\Facades\Facade类的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}
return $instance->$method(...$args);
}
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
protected static function getFacadeAccessor()
{
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) {
return $name;
}
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
return static::$resolvedInstance[$name] = static::$app[$name];
}

facades通过php魔术方法__callStatic实现。

五. Contracts

laravel中,使用Contracts 定义了接口,降低耦合性。通过Service Provider绑定接口到不同的实现。