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中操作ini配置文件的方法
Apr 25 PHP
解析php常用image图像函数集
Jun 24 PHP
php计算几分钟前、几小时前、几天前的几个函数、类分享
Apr 09 PHP
php保存二进制原始数据为图片的程序代码
Oct 14 PHP
10款PHP开源商城系统汇总介绍
Jul 23 PHP
四个常见html网页乱码问题及解决办法
Sep 08 PHP
学习php设计模式 php实现命令模式(command)
Dec 08 PHP
微信第三方登录(原生)demo【必看篇】
May 26 PHP
php获取文章内容第一张图片的方法示例
Jul 03 PHP
PHP 结合 Boostrap 结合 js 实现学生列表删除编辑及搜索功能
May 21 PHP
Laravel框架控制器的middleware中间件用法分析
Sep 30 PHP
Thinkphp 框架扩展之行为扩展原理与实现方法分析
Apr 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
thinkphp实现多语言功能(语言包)
2014/03/04 PHP
PHP中的日期加减方法示例
2014/08/21 PHP
php进程间通讯实例分析
2016/07/11 PHP
详解PHP发送邮件知识点
2018/05/06 PHP
PHP For循环字母A-Z当超过26个字母时输出AA,AB,AC
2020/02/16 PHP
如何通过PHP实现Des加密算法代码实例
2020/05/09 PHP
PHP配置文件php.ini中打开错误报告的设置方法
2015/01/09 PHP
JavaScript学习笔记之JS函数
2015/01/22 Javascript
js实现拖拽效果
2015/02/12 Javascript
完美实现js焦点轮播效果(二)(图片可滚动)
2017/03/07 Javascript
微信小程序 配置顶部导航条标题颜色的实现方法
2017/09/20 Javascript
AngularJS修改model值时,显示内容不变的实例
2018/09/13 Javascript
node链接mongodb数据库的方法详解【阿里云服务器环境ubuntu】
2019/03/07 Javascript
Vue分页插件的前后端配置与使用
2019/10/09 Javascript
Vue优化:常见会导致内存泄漏问题及优化详解
2020/08/04 Javascript
Ant Design Pro 之 ProTable使用操作
2020/10/31 Javascript
JS实现页面侧边栏效果探究
2021/01/08 Javascript
Python multiprocessing.Manager介绍和实例(进程间共享数据)
2014/11/21 Python
Python排序算法之选择排序定义与用法示例
2018/04/29 Python
python进行两个表格对比的方法
2018/06/27 Python
Python批量生成特定尺寸图片及图画任意文字的实例
2019/01/30 Python
Python利用heapq实现一个优先级队列的方法
2019/02/03 Python
python3+django2开发一个简单的人员管理系统过程详解
2019/07/23 Python
python快速排序的实现及运行时间比较
2019/11/22 Python
Python多线程操作之互斥锁、递归锁、信号量、事件实例详解
2020/03/24 Python
当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
2014/09/09 面试题
一些Unix笔试题和面试题
2013/01/22 面试题
某公司部分笔试题
2013/11/05 面试题
口腔医学技术应届生求职信
2013/11/09 职场文书
寄语十八大感言
2014/02/07 职场文书
反邪教标语
2014/06/23 职场文书
机电一体化专业求职信
2014/07/22 职场文书
青年岗位能手事迹材料
2014/12/23 职场文书
小区保洁员岗位职责
2015/04/10 职场文书
火锅店的开业营销方案范本!
2019/07/05 职场文书
详解python网络进程
2021/06/15 Python