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 相关文章推荐
使用无限生命期Session的方法
Oct 09 PHP
针对初学PHP者的疑难问答(2)
Oct 09 PHP
php 中的str_replace 函数总结
Apr 27 PHP
PHP 存储文本换行实现方法
Jan 05 PHP
thinkPHP的Html模板标签使用方法
Nov 13 PHP
php魔术函数__call()用法实例分析
Feb 13 PHP
php插入排序法实现数组排序实例
Feb 16 PHP
Codeigniter控制器controller继承问题实例分析
Jan 19 PHP
PHP中用mysqli面向对象打开连接关闭mysql数据库的方法
Nov 05 PHP
Yii2语言国际化的配置教程
Aug 19 PHP
PHP获取远程http或ftp文件的md5值的方法
Apr 15 PHP
PHP使用PDO 连接与连接管理操作实例分析
Apr 21 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
eWebEditor v3.8 商业完整版 (PHP)
2006/12/06 PHP
php INI配置文件的解析实现分析
2011/01/04 PHP
PHP 面向对象详解
2012/09/13 PHP
CMS中PHP判断系统是否已经安装的方法示例
2014/07/26 PHP
PHP Hash算法:Times33算法代码实例
2015/05/13 PHP
Yii2 assets清除缓存的方法
2016/05/16 PHP
PHP实现深度优先搜索算法(DFS,Depth First Search)详解
2017/09/16 PHP
Laravel 实现Controller向blade前台模板赋值的四种方式小结
2019/10/22 PHP
PHP实现Snowflake生成分布式唯一ID的方法示例
2020/08/30 PHP
showModelDialog弹出文件下载窗口的使用示例
2013/11/19 Javascript
JQuery异步获取返回值中文乱码的解决方法
2015/01/29 Javascript
jQuery的事件委托实例分析
2015/07/15 Javascript
基于jQuery插件jqzoom实现的图片放大镜效果示例
2017/01/23 Javascript
JS基于正则截取替换特定字符之间字符串操作示例
2017/02/03 Javascript
js 获取html5的data属性实现方法
2017/07/28 Javascript
微信小程序获取手机系统信息的方法【附源码下载】
2017/12/07 Javascript
vue vue-Router默认hash模式修改为history需要做的修改详解
2018/09/13 Javascript
详解如何用webpack4从零开始构建react开发环境
2019/01/27 Javascript
JS浅拷贝和深拷贝原理与实现方法分析
2019/02/28 Javascript
JavaScript canvas实现雪花随机动态飘落
2020/02/08 Javascript
JS数组的高级使用方法示例小结
2020/03/14 Javascript
编写一个javascript元循环求值器的方法
2020/04/14 Javascript
在vue中axios设置timeout超时的操作
2020/09/04 Javascript
python对Excel按条件进行内容补充(推荐)
2019/11/24 Python
python 串口读取+存储+输出处理实例
2019/12/26 Python
深入了解如何基于Python读写Kafka
2019/12/31 Python
基于CSS3实现的黑色个性导航菜单效果
2015/09/14 HTML / CSS
Skyscanner台湾:全球知名的旅行比价引擎
2018/07/01 全球购物
哈萨克斯坦最大的时装、鞋子和配饰在线商店:Lamoda.kz
2019/11/19 全球购物
创建精神文明单位实施方案
2014/03/08 职场文书
2014县委书记四风对照检查材料思想汇报
2014/09/21 职场文书
员工聘用合同范本
2015/09/21 职场文书
2016年暑假学生家长评语
2015/12/01 职场文书
2019年预备党员的思想汇报:加深对党的认知
2019/09/25 职场文书
python opencv人脸识别考勤系统的完整源码
2021/04/26 Python
【DOTA2】当街暴打?PSG LGD vs VG - DPC 2022 WINTER TOUR CN
2022/04/02 DOTA