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和ACCESS写聊天室(十)
Oct 09 PHP
php预定义常量
Dec 25 PHP
php 提速工具eAccelerator 配置参数详解
May 16 PHP
从康盛产品(discuz)提取出来的模板类
Jun 28 PHP
php学习笔记(三)操作符与控制结构
Aug 06 PHP
浅谈PHP变量作用域以及地址引用问题
Dec 27 PHP
2014年最新推荐的10款 PHP 开发框架
Aug 01 PHP
php实现的简单美国商品税计算函数
Jul 13 PHP
Linux(CentOS)下PHP扩展PDO编译安装的方法
Apr 07 PHP
Joomla语言翻译类Jtext用法分析
May 05 PHP
laravel框架实现去掉URL中index.php的方法
Oct 12 PHP
php操作redis数据库常见方法实例总结
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实现图象锐化代码
2007/06/14 PHP
支持php4、php5的mysql数据库操作类
2008/01/10 PHP
PHP中改变图片的尺寸大小的代码
2011/07/17 PHP
laravel Model 执行事务的实现
2019/10/10 PHP
jquery禁用右键示例
2014/04/28 Javascript
js实现正方形颜色从下往上升的效果
2014/08/04 Javascript
Express实现前端后端通信上传图片之存储数据库(mysql)傻瓜式教程(二)
2015/12/10 Javascript
Angularjs过滤器使用详解
2016/05/25 Javascript
JavaScript数组去重由慢到快由繁到简(优化篇)
2016/08/26 Javascript
ES6生成器用法实例分析
2017/04/10 Javascript
Vue.js弹出模态框组件开发的示例代码
2017/07/26 Javascript
express启用https使用小记
2019/05/21 Javascript
微信小程序-可移动菜单的实现过程详解
2019/06/24 Javascript
JS实现动态无缝轮播
2020/01/11 Javascript
[19:24]DOTA2客户端使用指南 一分钟快速设置轻松超神
2013/09/24 DOTA
python以环状形式组合排列图片并输出的方法
2015/03/17 Python
教你用Python脚本快速为iOS10生成图标和截屏
2016/09/22 Python
window下eclipse安装python插件教程
2017/04/24 Python
Python随机生成均匀分布在三角形内或者任意多边形内的点
2017/12/14 Python
python实现图片筛选程序
2018/10/24 Python
python 遍历列表提取下标和值的实例
2018/12/25 Python
解决python2 绘图title,xlabel,ylabel出现中文乱码的问题
2019/01/29 Python
Python处理mysql特殊字符的问题
2020/03/02 Python
python和js交互调用的方法
2020/06/23 Python
pycharm中使用request和Pytest进行接口测试的方法
2020/07/31 Python
python 自定义异常和主动抛出异常(raise)的操作
2020/12/11 Python
HTML5 video标签(播放器)学习笔记(一):使用入门
2015/04/24 HTML / CSS
a标签下载链接的简单实现
2016/09/13 HTML / CSS
美国最灵活的移动提供商:Tello
2017/07/18 全球购物
女大学生自我鉴定
2013/12/09 职场文书
户外拓展活动方案
2014/02/11 职场文书
QQ空间主人寄语大全
2014/04/12 职场文书
银行贷款委托书范本
2014/10/11 职场文书
个人存款证明书
2014/10/18 职场文书
少年的你:世界上没有如果,要在第一次就勇敢的反抗
2019/11/20 职场文书
MySQL学习之基础操作总结
2022/03/19 MySQL