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 相关文章推荐
建立文件交换功能的脚本(一)
Oct 09 PHP
php中处理模拟rewrite 效果
Dec 09 PHP
如何解决CI框架的Disallowed Key Characters错误提示
Jul 05 PHP
ajax返回值中有回车换行、空格的解决方法分享
Oct 24 PHP
php生成缩略图示例代码分享(使用gd库实现)
Jan 20 PHP
Yii2实现ajax上传图片插件用法
Apr 28 PHP
php fread读取文件注意事项
Sep 24 PHP
PHP页面跳转实现延时跳转的方法
Dec 10 PHP
基于php编程规范(详解)
Aug 17 PHP
php设计模式之正面模式实例分析【星际争霸游戏案例】
Mar 24 PHP
php查看一个变量的占用内存的实例代码
Mar 29 PHP
如何用PHP实现多线程编程
May 26 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最常用的ini函数分析 针对PHP.ini配置文件
2010/04/22 PHP
javascript for循环从入门到偏门(效率优化+奇特用法)
2012/08/01 Javascript
JS动态修改图片的URL(src)的方法
2015/04/01 Javascript
javascript创建对象、对象继承的实用方式详解
2016/03/08 Javascript
Bootstrap自定义文件上传下载样式
2016/05/26 Javascript
jQuery AJAX timeout 超时问题详解
2016/06/21 Javascript
Bootstrap3 模态框使用实例
2017/02/22 Javascript
vue.js声明式渲染和条件与循环基础知识
2017/07/31 Javascript
React Native 使用Fetch发送网络请求的示例代码
2017/12/02 Javascript
Javascript执行上下文顺序的深入讲解
2020/11/04 Javascript
11个Javascript小技巧帮你提升代码质量(小结)
2020/12/28 Javascript
[41:20]2014 DOTA2华西杯精英邀请赛 5 24 NewBee VS DK
2014/05/26 DOTA
Python编写Windows Service服务程序
2018/01/04 Python
Python视频爬虫实现下载头条视频功能示例
2018/05/07 Python
matplotlib subplots 调整子图间矩的实例
2018/05/25 Python
Python使用itertools模块实现排列组合功能示例
2018/07/02 Python
用Python将结果保存为xlsx的方法
2019/01/28 Python
解决py2exe打包后,总是多显示一个DOS黑色窗口的问题
2019/06/21 Python
Python编程快速上手——选择性拷贝操作案例分析
2020/02/28 Python
python如何随机生成高强度密码
2020/08/19 Python
意大利火车票和铁路通行证专家:ItaliaRail
2019/01/22 全球购物
英国旅行箱包和行李箱购物网站:Travel Luggage & Cabin Bags
2019/08/26 全球购物
俄罗斯家居用品购物网站:Евродом
2020/11/21 全球购物
请写出 float x 与"零值"比较的 if 语句
2016/01/04 面试题
企业宣传标语
2014/06/09 职场文书
禁烟标语大全
2014/06/11 职场文书
学校教研活动总结
2014/07/02 职场文书
安全责任书模板
2014/07/22 职场文书
2015年导购员工作总结
2015/04/25 职场文书
2015年节能减排工作总结
2015/05/14 职场文书
甲午风云观后感
2015/06/02 职场文书
先进个人主要事迹范文
2015/11/04 职场文书
导游词之河北滦平金山岭长城
2019/10/16 职场文书
详解redis在微服务领域的贡献
2021/10/16 Redis
攻击最高的10只幽灵系神奇宝贝,坚盾剑怪排第一,第五最为可怕
2022/03/18 日漫
Golang日志包的使用
2022/04/20 Golang