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 相关文章推荐
如何用phpmyadmin设置mysql数据库用户的权限
Jan 09 PHP
php重定向的三种方法分享
Feb 22 PHP
php存储过程调用实例代码
Feb 03 PHP
php获取URL中带#号等特殊符号参数的解决方法
Sep 02 PHP
PHP+iFrame实现页面无需刷新的异步文件上传
Sep 16 PHP
php操作(删除,提取,增加)zip文件方法详解
Mar 12 PHP
全面解析PHP操作Memcache基本函数
Jul 14 PHP
php中final关键字用法分析
Dec 07 PHP
PHP面向对象五大原则之接口隔离原则(ISP)详解
Apr 04 PHP
浅谈laravel 5.6 安装 windows上使用composer的安装过程
Oct 18 PHP
通过实例解析PHP数据类型转换方法
Jul 11 PHP
Thinkphp极验滑动验证码实现步骤解析
Nov 24 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
在任意字符集下正常显示网页的方法二(续)
2007/04/01 PHP
php 特殊字符处理函数
2008/09/05 PHP
PHP ajax 分页类代码
2008/11/13 PHP
php多文件上传实现代码
2014/02/20 PHP
在Yii2中使用Pjax导致Yii2内联脚本载入失败的原因分析
2016/03/06 PHP
PHP实现十进制数字与二十六进制字母串相互转换操作示例
2018/08/10 PHP
jquery validate使用攻略 第四步
2010/07/01 Javascript
file模式访问网页时iframe高度自适应解决方案
2013/01/16 Javascript
js中的referrer返回上一页使用介绍
2013/09/26 Javascript
JS格式化数字保留两位小数点示例代码
2013/10/15 Javascript
Javascript控制input输入时间格式的方法
2015/01/28 Javascript
Vue.js双向绑定操作技巧(初级入门)
2016/12/27 Javascript
Bootstrap的popover(弹出框)在append后弹不出(失效)
2017/02/27 Javascript
canvas压缩图片转换成base64格式输出文件流
2017/03/09 Javascript
浅谈FastClick 填坑及源码解析
2018/03/02 Javascript
收集前端面试题之url、href、src
2018/03/22 Javascript
node 命令方式启动修改端口的方法
2018/05/12 Javascript
python2.7 mayavi 安装图文教程(推荐)
2017/06/22 Python
Python3爬虫学习之MySQL数据库存储爬取的信息详解
2018/12/12 Python
python super的使用方法及实例详解
2019/09/25 Python
在OpenCV里使用Camshift算法的实现
2019/11/22 Python
html5 Canvas绘制线条 closePath()实例代码
2012/05/10 HTML / CSS
英国马莎百货官网:Marks & Spencer
2016/07/29 全球购物
日本著名化妆品零售网站:Cosme Land
2019/03/01 全球购物
医学生职业规划范文
2014/01/05 职场文书
寒假思想汇报
2014/01/10 职场文书
清洁工岗位职责
2014/01/29 职场文书
有趣的广告词
2014/03/18 职场文书
协议书的格式
2014/04/23 职场文书
祖国在我心中演讲稿(小学生)
2014/09/23 职场文书
领导班子群众路线与四风问题对照检查材料思想汇报
2014/10/11 职场文书
严以修身专题学习研讨会发言材料
2015/11/09 职场文书
《称赞》教学反思
2016/02/17 职场文书
tp5使用layui实现多个图片上传(带附件选择)的方法实例
2021/11/17 PHP
面试中老生常谈的MySQL问答集锦夯实基础
2022/03/13 MySQL
PostgreSQL基于pgrouting的路径规划处理方法
2022/04/18 PostgreSQL