Laravel中Facade的加载过程与原理详解


Posted in PHP onSeptember 22, 2017

前言

本文主要给大家介绍了关于Laravel中Facade加载过程与原理的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

简介

Facades(读音:/fəˈsäd/ )为应用程序的 服务容器 中可用的类提供了一个「静态」接口。你不必 use 一大串的命名空间,也不用实例化对象,就能访问对象的具体方法。

use Config;

class Test
{
 public function index()
 {
 return Config::get('app.name');
 }
}

Facade 的启动与注册

Facade 的启动引导是在 Illuminate\Foundation\Bootstrap\RegisterFacades 中注册的。

public function bootstrap(Application $app)
{
 Facade::clearResolvedInstances();
 Facade::setFacadeApplication($app);

 AliasLoader::getInstance(array_merge(
 $app->make('config')->get('app.aliases', []),
 $app->make(PackageManifest::class)->aliases()
 ))->register();
}

默认的别名配置是从 app 配置文件下的 aliases 读取的,PackageManifest 是 laravel 5.5 新增的 包自动发现 规则,这里我们暂时不考虑 PackageManifest 包提供的别名。

其中,array_merge 返回如下格式的数组:

"App" => "Illuminate\Support\Facades\App"
 "Artisan" => "Illuminate\Support\Facades\Artisan"
 "Auth" => "Illuminate\Support\Facades\Auth"
 "Blade" => "Illuminate\Support\Facades\Blade"
 ...

上面代码将通过 AliasLoader 把所有的 facade 注册进自动加载。其核心就是 php 的 spl_autoload_register。

/**
 * Prepend the load method to the auto-loader stack.
 *
 * @return void
 */
 protected function register()
 {
 if (! $this->registered) {
  spl_autoload_register([$this, 'load'], true, true);

  $this->registered = true;
 }
 }

注册完成后,后续所有 use 的类都将通过 load 函数来完成类的自动加载。

注意:这里在定义 spl_autoload_register 时,最后面的参数传的是 true。当该参数是 true 时,spl_autoload_register() 会添加函数到队列之首,而不是队列尾部。(优先通过该函数来完成自动加载)

也就是说,

<?php

use Config;
use App\User;

class Test
{
 public function index()
 {
 Config::get('app.name');
 new User();
 }
}

不管我们 use 的是具体存在的类(App\User)还是别名 (Config),都将最先通过 load 函数来完成自动加载,当该函数返回 false 时,再由其他自动加载函数来完成自动加载(如 composer psr-4)。

在 AliasLoader 的 load 方法中,主要是用了 class_alias 函数来实现的别名自动加载。

public function load($alias)
{
 if (isset($this->aliases[$alias])) {
 return class_alias($this->aliases[$alias], $alias);
 }
}

关于 class_alias 这里帖一个官方的列子:

class foo { }

class_alias('foo', 'bar');

$a = new foo;
$b = new bar;

// the objects are the same
var_dump($a == $b, $a === $b); //true
var_dump($a instanceof $b); //false

// the classes are the same
var_dump($a instanceof foo); //true
var_dump($a instanceof bar); //true

var_dump($b instanceof foo); //true
var_dump($b instanceof bar); //true

Facade 的加载

当我们在使用 Facade 时,如:

<?php

use Config;

class Test
{
 public function index()
 {
 Config::get('app.name');
 }
}

实际上加载的是 Illuminate\Support\Facades\Config 类(因为我们已经注册了 class_alias),相当于:

<?php

use Illuminate\Support\Facades\Config;

class Test
{
 public function index()
 {
  Config::get('app.name');
 }
}

而所有的 Facade 都继承自 Illuminate\Support\Facades\Facade 类,在该基类中定义了一个 __callStatic 方法,已至于我们能够轻松地使用 Facade(不用实列化)。

<?php

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);
}

getFacadeRoot 方法用于获取别名类的具体实列,我们知道,所有的 Facade 类都需要定义一个 getFacadeAccessor 方法。该方法可能的返回值有:

  • String 类型的字符串(如 config, db)
  • String 类型的类字符串 (如 App\Service\SomeService)
  • Object 具体的实列化对象
  • Closure 闭包

如 Config Facade 的 getFacadeAccessor 方法如下:

protected static function getFacadeAccessor()
{
 return 'config';
}

getFacadeRoot 方法将根据 getFacadeAccessor() 的返回值,从容器从取出对应的实列对象。

public static function getFacadeRoot()
{
 $name = static::getFacadeAccessor();
 
 if (is_object($name)) {
  return $name;
 }

 if (isset(static::$resolvedInstance[$name])) {
  return static::$resolvedInstance[$name];
 }

 return static::$resolvedInstance[$name] = static::$app[$name];
}

由于 APP 容器中已经注册过 config 的实列

<?php
//Illuminate\Foundation\Bootstrap/LoadConfiguration

$app->instance('config', $config = new Repository($items));

所以 \Config::get('app.name', 'dafault) 实际访问的是 Repository 实列的 get('app.name', 'default') 方法。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

PHP 相关文章推荐
PHP+jQuery 注册模块的改进(三):更新到Smarty3.1
Oct 14 PHP
PHP实现获取FLV文件的时间
Feb 10 PHP
php插入排序法实现数组排序实例
Feb 16 PHP
日常整理PHP中简单的图形处理(经典)
Oct 26 PHP
深入理解PHP之源码目录结构与功能说明
Jun 01 PHP
注释PHP和html混合代码的小技巧(分享)
Nov 03 PHP
PHP中include()与require()的区别说明
Feb 14 PHP
PHP实现动态创建XML文档的方法
Mar 30 PHP
TP(thinkPHP)框架多层控制器和多级控制器的使用示例
Jun 13 PHP
thinkPHP5使用Rabc实现权限管理
Aug 28 PHP
laravel 实现划分admin和home 模块分组
Oct 15 PHP
PHP使用openssl扩展实现加解密方法示例
Feb 20 PHP
laravel实现分页样式替换示例代码(增加首、尾页)
Sep 22 #PHP
深入理解PHP的远程多会话调试
Sep 21 #PHP
Laravel中日期时间处理包Carbon的简单使用
Sep 21 #PHP
简单实现php上传文件功能
Sep 21 #PHP
Laravel中七个非常有用但很少人知道的Carbon方法
Sep 21 #PHP
如何通过View::first使用Laravel Blade的动态模板详解
Sep 21 #PHP
基于Laravel实现的用户动态模块开发
Sep 21 #PHP
You might like
PHP与SQL注入攻击[三]
2007/04/17 PHP
php 根据url自动生成缩略图并处理高并发问题
2014/01/23 PHP
浅谈thinkphp的nginx配置,以及重写隐藏index.php入口文件方法
2019/10/12 PHP
js 加载并解析XML字符串的代码
2009/12/13 Javascript
jQuery getJSON 处理json数据的代码
2010/07/26 Javascript
jQuery 阴影插件代码分享
2012/01/09 Javascript
javascript 实现 秒杀,团购 倒计时展示的记录 分享
2013/07/12 Javascript
javascript trim函数在IE下不能用的解决方法
2014/09/12 Javascript
javascript实现回车键提交表单方法总结
2015/01/10 Javascript
JS中mouseover和mouseout多次触发问题如何解决
2016/06/06 Javascript
浅谈移动端之js touch事件 手势滑动事件
2016/11/07 Javascript
Windows系统下安装Node.js的步骤图文详解
2016/11/15 Javascript
微信小程序 仿美团分类菜单 swiper分类菜单
2017/04/12 Javascript
Vue中封装input组件的实例详解
2017/10/17 Javascript
详解JS转换数值函数Number()、parseInt()、parseFloat()
2018/08/24 Javascript
JQuery判断radio单选框是否选中并获取值的方法
2019/01/17 jQuery
layui数据表格重载实现往后台传参
2019/11/15 Javascript
[01:36]极致酷炫!TI9典藏宝瓶+撼地者至宝展示
2019/06/11 DOTA
[55:56]NB vs Infamous 2019国际邀请赛淘汰赛 败者组 BO3 第二场 8.22
2019/09/05 DOTA
用Python编写一个简单的Lisp解释器的教程
2015/04/03 Python
python 计算两个日期相差多少个月实例代码
2017/05/24 Python
Python实现嵌套列表去重方法示例
2017/12/28 Python
python opencv 图像尺寸变换方法
2018/04/02 Python
详谈pandas中agg函数和apply函数的区别
2018/04/20 Python
python3去掉string中的标点符号方法
2019/01/22 Python
python利用多种方式来统计词频(单词个数)
2019/05/27 Python
Django实现网页分页功能
2019/10/31 Python
python中dict()的高级用法实现
2019/11/13 Python
详解H5本地储存Web Storage
2017/07/03 HTML / CSS
伊芙丽官方旗舰店:中国淑女一线品牌
2017/12/01 全球购物
Kathmandu美国网站:新西兰户外运动品牌
2019/03/23 全球购物
银行实习生的自我评价
2013/12/09 职场文书
邻里守望志愿服务活动方案
2014/08/15 职场文书
大学生自荐信范文
2015/03/05 职场文书
高中生物教学反思
2016/02/20 职场文书
SQL语句中EXISTS的详细用法大全
2022/06/25 MySQL