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 实现新闻标签分类与无刷新分页
Dec 18 PHP
Php Image Resize图片大小调整的函数代码
Jan 17 PHP
php中将时间差转换为字符串提示的实现代码
Aug 08 PHP
php数组函数序列之array_intersect() 返回两个或多个数组的交集数组
Nov 10 PHP
解析func_num_args与func_get_args函数的使用
Jun 24 PHP
PHP模板引擎Smarty的缓存使用总结
Apr 24 PHP
php常用表单验证类用法实例
Jun 18 PHP
PHP的Socket网络编程入门指引
Aug 11 PHP
php实现在新浪云中使用imagick生成缩略图并上传的方法
Sep 26 PHP
PHP实现对xml的增删改查操作案例分析
May 19 PHP
CakePHP框架Model关联对象用法分析
Aug 04 PHP
基于php中echo用逗号和用点号的区别详解
Jan 23 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
实用函数7
2007/11/08 PHP
php empty()与isset()区别的详细介绍
2013/06/17 PHP
yii2.0之GridView自定义按钮和链接用法
2014/12/15 PHP
PHP生成短网址的思路以及实现方法的详解
2019/03/25 PHP
探索Emberjs制作一个简单的Todo应用
2012/11/07 Javascript
jquery实现点击文字可编辑并修改保存至数据库
2014/04/15 Javascript
用html+css+js实现的一个简单的图片切换特效
2014/05/28 Javascript
jquery网页回到顶部效果(图标渐隐,自写)
2014/06/16 Javascript
JavaScript获取网页、浏览器、屏幕高度和宽度汇总
2014/12/18 Javascript
AngularJS入门教程之AngularJS 模板
2016/08/18 Javascript
用jQuery.ajaxSetup实现对请求和响应数据的过滤
2016/12/20 Javascript
jQuery实现用户输入自动完成功能
2017/02/13 Javascript
jQuery插件HighCharts绘制2D金字塔图效果示例【附demo源码下载】
2017/03/09 Javascript
JavaScript定义函数_动力节点Java学院整理
2017/06/27 Javascript
vue中各组件之间传递数据的方法示例
2017/07/27 Javascript
JS实现带动画的回到顶部效果
2017/12/28 Javascript
微信小程序非swiper组件实现的自定义伪3D轮播图效果示例
2018/12/11 Javascript
解决vue props传Array/Object类型值,子组件报错的情况
2020/11/07 Javascript
[50:34]VGJ.T vs Fnatic 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
python生成器generator用法实例分析
2015/06/04 Python
python线程中同步锁详解
2018/04/27 Python
详解Python用户登录接口的方法
2019/04/17 Python
PYcharm 激活方法(推荐)
2020/03/23 Python
Python基于pip实现离线打包过程详解
2020/05/15 Python
利用pipenv和pyenv管理多个相互独立的Python虚拟开发环境
2020/11/01 Python
利用Python实现学生信息管理系统的完整实例
2020/12/30 Python
用CSS3实现瀑布流布局的示例代码
2017/11/10 HTML / CSS
phonegap常用事件总结(必看篇)
2017/03/31 HTML / CSS
马来西亚最大的电器网站:Senheng
2017/10/13 全球购物
C#里面如何判断一个Object是否是某种类型(如Boolean)?
2016/02/10 面试题
全陪导游欢迎词
2014/01/17 职场文书
2014年学习委员工作总结
2014/11/14 职场文书
2014年技术部工作总结
2014/12/12 职场文书
悬空寺导游词
2015/02/05 职场文书
琅琊山导游词
2015/02/05 职场文书
golang内置函数len的小技巧
2021/07/25 Golang