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 相关文章推荐
简单实用的.net DataTable导出Execl
Oct 28 PHP
php调用C代码的实现方法
Mar 11 PHP
Codeigniter中禁止A Database Error Occurred错误提示的方法
Jun 12 PHP
PHP代码实现表单数据验证类
Jul 28 PHP
深入浅析用PHP实现MVC
Mar 02 PHP
Laravel实现构造函数自动依赖注入的方法
Mar 16 PHP
源码分析 Laravel 重复执行同一个队列任务的原因
Dec 25 PHP
PHP有序表查找之二分查找(折半查找)算法示例
Feb 09 PHP
laravel项目利用twemproxy部署redis集群的完整步骤
May 11 PHP
PHP封装的分页类与简单用法示例
Feb 25 PHP
PHP+Redis开发的书签案例实战详解
Jul 09 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
VFP与其他应用程序的集成
2006/10/09 PHP
Win9x/ME下Apache+PHP安装配置
2006/10/09 PHP
在命令行下运行PHP脚本[带参数]的方法
2010/01/22 PHP
PHP实现读取一个1G的文件大小
2013/08/24 PHP
浅谈php中的访问修饰符private、protected、public的作用范围
2016/11/20 PHP
使用纯javascript实现经典扫雷游戏
2015/04/23 Javascript
JavaScript中几种排序算法的简单实现
2015/07/29 Javascript
jquery Easyui快速开发总结
2015/08/20 Javascript
jquery实现的Accordion折叠面板效果代码
2015/09/02 Javascript
requireJS模块化实现返回顶部功能的方法详解
2017/10/16 Javascript
3种vue路由传参的基本模式
2018/02/22 Javascript
详解vue文件中使用echarts.js的两种方式
2018/10/18 Javascript
详解基于electron制作一个node压缩图片的桌面应用
2019/01/29 Javascript
vue v-for循环重复数据无法添加问题解决方法【加track-by='索引'】
2019/03/15 Javascript
angular 表单验证器验证的同时限制输入的实现
2019/04/11 Javascript
ES6入门教程之let、const的使用方法
2019/04/13 Javascript
CKeditor富文本编辑器使用技巧之添加自定义插件的方法
2019/06/14 Javascript
Vue+Element UI 树形控件整合下拉功能菜单(tree + dropdown +input)
2020/08/28 Javascript
[00:35]DOTA2上海特级锦标赛 VP战队宣传片
2016/03/04 DOTA
[01:11:15]VGJ.S vs Secret 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
python机器学习实战之K均值聚类
2017/12/20 Python
python3爬取各类天气信息
2018/02/24 Python
PYTHON基础-时间日期处理小结
2018/05/05 Python
python使用Flask操作mysql实现登录功能
2018/05/14 Python
解决python 未发现数据源名称并且未指定默认驱动程序的问题
2018/12/07 Python
python自动发送测试报告邮件功能的实现
2019/01/22 Python
pyqt5使用按钮进行界面的跳转方法
2019/06/19 Python
利用python list完成最简单的DB连接池方法
2019/08/09 Python
pytorch逐元素比较tensor大小实例
2020/01/03 Python
耐克奥地利官网:Nike奥地利
2019/08/16 全球购物
煤矿班组长的职责
2013/12/25 职场文书
欢迎新生标语
2014/10/06 职场文书
欢迎家长标语
2014/10/08 职场文书
公民授权委托书
2014/10/15 职场文书
SQL Server2019数据库之简单子查询的具有方法
2021/04/27 SQL Server
Tomcat项目启动失败的原因和解决办法
2022/04/20 Servers