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 相关文章推荐
2.PHP入门
Oct 09 PHP
php中的登陆login
Jan 18 PHP
不错的PHP学习之php4与php5之间会穿梭一点点感悟
May 03 PHP
php中用文本文件做数据库的实现方法
Mar 27 PHP
php Undefined index的问题
Jun 01 PHP
发款php蜘蛛统计插件只要有mysql就可用
Oct 12 PHP
php导入csv文件碰到乱码问题的解决方法
Feb 10 PHP
php计算两个日期相差天数的方法
Mar 14 PHP
php通过array_unshift函数添加多个变量到数组前端的方法
Mar 18 PHP
thinkPHP中多维数组的遍历方法
Jan 09 PHP
Yii视图CGridView列表用法实例分析
Jul 12 PHP
PHP常见的几种攻击方式实例小结
Apr 29 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
文件上传的实现
2006/10/09 PHP
PHP JSON 数据解析代码
2010/05/26 PHP
PHP 反向排序和随机排序代码
2010/06/30 PHP
php去除字符串换行符示例分享
2014/02/13 PHP
高质量PHP代码的50个实用技巧必备(下)
2016/01/22 PHP
PHP析构函数destruct与垃圾回收机制的讲解
2019/03/22 PHP
laravel框架使用阿里云短信发送消息操作示例
2020/02/15 PHP
$.format,jquery.format 使用说明
2011/07/13 Javascript
javaScript让文本框内的最后一个文字的后面获得焦点实现代码
2013/01/06 Javascript
jquery统计复选框选中示例
2013/11/05 Javascript
浅谈JavaScript字符集
2014/05/22 Javascript
兼容各大浏览器的JavaScript阻止事件冒泡代码
2015/07/09 Javascript
JS简单实现点击复制链接的方法
2016/08/03 Javascript
利用JS做网页特效_大图轮播(实例讲解)
2017/08/09 Javascript
微信小程序实现tab切换效果
2017/11/21 Javascript
jQuery实现遍历XML节点和属性的方法示例
2018/04/29 jQuery
解决vuex数据异步造成初始化的时候没值报错问题
2019/11/13 Javascript
[49:40]2018DOTA2亚洲邀请赛小组赛 A组加赛 TNC vs Newbee
2018/04/03 DOTA
python数据结构之图的实现方法
2015/07/08 Python
简单实现python爬虫功能
2015/12/31 Python
PyQt5实现QLineEdit添加clicked信号的方法
2019/06/25 Python
python文档字符串(函数使用说明)使用详解
2019/07/30 Python
python生成特定分布数的实例
2019/12/05 Python
解决reload(sys)后print失效的问题
2020/04/25 Python
matlab、python中矩阵的互相导入导出方式
2020/06/01 Python
Python如何使用input函数获取输入
2020/08/06 Python
python批量合成bilibili的m4s缓存文件为MP4格式 ver2.5
2020/12/01 Python
html5菜单折纸效果
2014/04/22 HTML / CSS
BookOutlet加拿大:在网上书店购买廉价折扣图书和小说
2018/10/05 全球购物
Hashtable 添加内容的方式有哪几种,有什么区别?
2012/04/08 面试题
企业为何需要商业计划书
2013/12/26 职场文书
车辆工程专业求职信
2014/04/28 职场文书
我的教育故事演讲稿
2014/05/04 职场文书
办公室禁烟通知
2015/04/23 职场文书
教师文明餐桌光盘行动倡议书
2015/04/28 职场文书
vue使用element-ui按需引入
2022/05/20 Vue.js