Laravel学习笔记
第一步:了解几个常用名称
代码基于 Laravel-5.8 部分内容给其他版本不一致,但整体逻辑是没有太大差别的。
Service Container、Service Provider、Facades、Contracts。当第一次使用 Laravel 框架的时候被这几个词直接搞蒙圈了,完全不知道是什么意思,尤其是在看完文档之后,真的想说 WTF 。不过不知道也不会太影响使用。(😅有个朋友说过,不管用着在 NB 的框架,依然可以写出屎一样的代码)
Service Container
中文解释:服务容器 (😂 是不是看完这个翻译更不知道是啥了)
我的理解:一个装着各种各样的服务实例的容器。这些服务就是 Laravel 中的各种模块,如 Redis、Route 等等。
实现一个简单的容器:
1 | class Container |
为什么理解 IOC Container 对于理解 Laravel 架构是如此的重要?
因为在 Laravel 中,你所能使用到的 Laravel 的特性和功能几乎全部是由 IOC Container 实现的。
1 | Cache::get('key'); |
上面的 Cache、Route 都是通过把各自类的实现 bind 到 Container 中,然后 Container make 出的一个实例。
那么它们是在哪里进行的 bind 呢?没错就是 Service Provider 中。
Service Provider
在 Laravel 中有两种方式来使用 IOC Container:
- 通过 Service Provider
- 不通过 Service Provider
一般情况都是使用第一种方式。
不通过 Service Provider 来使用 IOC Container
实例:
1 | //web.php |
通过 Service Provider 来使用 IOC Container
原因:我们知道,有时候我们的类、模块会有需要其他类和组件的情况,为了保证初始化阶段不会出现所需要的模块和组件没有注册的情况,laravel 将注册和初始化行为进行拆分,注册的时候就只能注册,初始化的时候就是初始化。拆分后的产物就是现在的 服务提供者。
可以想象这样一个场景,你要绑定 3 个类 A B C 到 IOC Container 中。 A,B,C 都是非常复杂的类。在 bind A 时,引用了一个类 B 的实例,那么想要获得类 B 的实例,就需要 B 已经被 bind,只有这样,我们的 IOC Container 才有能力 make 出一个 B 的实例。 而在 bind B 时,恰好又需要 C 的实例.
如果是这样的逻辑,那么在 bind A B C 时,就必须手动的严格安排 bind 的次序,而且这只是 3 个类的情况,如果有几十个类的话,人工已经无法完成了.
而这时就需要 Service Provider 的作用了。
1 | <?php |
Facades
1 | Route::get('/', |
想必大家在第一使用 Laravel 的时候一定会注意到这行代码。在使用 phpStorm 之类的 IDE 工具是无法跳转到对应的类文件。那么这是为啥呢?
你是无法找到对 Route 类的声明的,是因为使用了别名。别名是 PHP 的一个特性( class_alias 方法 )。
别名在哪里设置呢——app/config/app.php
1 | 'aliases' => [ |
查看 vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php 文件….哎?怎么只有这么点东西,get 呢?
1 | namespace Illuminate\Support\Facades; |
首先要知道 Facade 的作用:使用一个简单的语法,让你从 Laravel 的 IOC Container 中方便的 make 出你想要的对象。
Facade 实现原理(make)
- 首先 Route 继承自
Illuminate\Support\Facades。 ``` 里也没有 get 静态方法。 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
543. 使用了 [__callStatic()](https://www.php.net/manual/zh/language.oop5.overloading.php#object.callstatic) 魔术方法。
4. 在 Route 中调用静态的 get 方法,实际上 Illuminate\Support\Facades 中的 ``` __callStatic ``` 魔术方法被调用。
5. 在 [__callStatic()](https://www.php.net/manual/zh/language.oop5.overloading.php#object.callstatic) 方法中调用了静态的 getFaRoot 方法。
6. 在 ``` getFacadeRoot ``` 方法中调用了静态的 ``` resolveFacadeInstance ``` 方法。
7. ``` resolveFacadeInstance ``` 方法需要传递一个参数,该参数通过静态 ``` getFacadeAccessor ``` 方法获得。
8. ``` getFacadeAccessor``` 该方法在 Route 类中实现了 ``` getFacadeAccessor ```并返回 route。
9. ``` resolveFacadeInstance``` 方法接收 route 字符串参数。首先判断是否为对象,当然不是啦,$name 是字符串。 然后判断该 ``` resolvedInstance ``` 数组中是否存在 route 相关信息,因为我们的程序是第一次运行当然也是没有的。 最后返回 ``` static::$app['route'] ```,同时把结果保存到 ``` resolvedInstance``` 数组中。
10. $app 其实就是前面说的 Application 类的实例对象,这个类是一个 IOC Container,实例化过程在 Laravel 最开始的时候。Facade 初始化的时候也让自己有了一个 $app,这个就是 Application 类的实例化对象。
11. 其实此时 $app 中并没有 'route' 属性,那么为什么可以调用呢?因为 Application 继承了 Container, 而 Container 又继承了 ArrayAccess 类,并实现了 offsetGet 方法。 该方法的内容为 ``` return $this->make($key);``` 这里就很明显了,直接make 出了一个 route 实例。
12. 最终相当于 ```$instance = static::getFacadeRoot(); ``` 与 ``` $instance = $app->make('router'); ``` 是相等的。
```php
//Illuminate\Support\Facades\Facades.php;
//以上内容省略
....
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];
}
//以下内容省略
....
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);
}
Facade 实现原理(bind)
在最开始就了解到,既然要 make,必须首先 bind。而且最好的方式是通过 serviceProvider 来 bind 类。 而且不论是 make 还是 bind 都需要一个 key,用来在容器中保存和查找这个类。上面讲的是使用 route 关键字进行 make 的过程。那么我们可以肯定在之前一定有一个使用 route 进行 bind 的操作。下面就进行 bind 的讲解。
- 首先 ServiceProvider 需要在
config/app.php进行注册,我们在文件中找到对应 route 相关的内容,就是App\Providers\RouteServiceProvider::class,。