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 相关文章推荐
WML,Apache,和 PHP 的介绍
Oct 09 PHP
PHP 检查扩展库或函数是否可用的代码
Apr 06 PHP
php下封装较好的数字分页方法
Nov 23 PHP
PHP学习之数组值的操作
Apr 17 PHP
如何突破PHP程序员的技术瓶颈分析
Jul 17 PHP
PHP图片库imagemagick安装方法
Sep 23 PHP
深入浅析php中sprintf与printf函数的用法及区别
Jan 08 PHP
php实现在线通讯录功能(附源码)
May 13 PHP
PHP实现QQ登录的开原理和实现过程
Feb 04 PHP
PHP fprintf()函数用法讲解
Feb 16 PHP
Laravel5.4框架中视图共享数据的方法详解
Sep 05 PHP
PHP字符串和十六进制如何实现互相转换
Jul 16 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实现简单的语法高亮函数实例分析
2015/04/27 PHP
PHP+AJAX 投票器功能
2017/11/11 PHP
PHP实现redis限制单ip、单用户的访问次数功能示例
2018/06/16 PHP
Laravel框架运行出错提示RuntimeException No application encryption key has been specified.解决方法
2019/04/02 PHP
PHP Swoole异步读取、写入文件操作示例
2019/10/24 PHP
jquery 图片预加载 自动等比例缩放插件
2008/12/25 Javascript
在JavaScript中获取请求的URL参数[正则]
2010/12/25 Javascript
jquery获得option的值和对option进行操作
2013/12/13 Javascript
Javascript Object 对象学习笔记
2014/12/17 Javascript
Angular中的Promise对象($q介绍)
2015/03/03 Javascript
JS使用onerror捕获异常示例
2016/08/03 Javascript
ES6记录异步函数的执行时间详解
2016/08/31 Javascript
JavaScript定义函数的三种实现方法
2017/09/23 Javascript
微信web端后退强制刷新功能的实现代码
2018/03/04 Javascript
微信小程序使用form表单获取输入框数据的实例代码
2018/05/17 Javascript
对vue中methods互相调用的方法详解
2018/08/30 Javascript
layui+jquery支持IE8的表格分页方法
2019/09/28 jQuery
详解vue-flickity的fullScreen功能实现
2020/04/07 Javascript
javascript使用Blob对象实现的下载文件操作示例
2020/04/18 Javascript
使用JavaScript实现网页秒表功能(含开始、暂停、继续、重置功能)
2020/06/05 Javascript
vue组件讲解(is属性的用法)模板标签替换操作
2020/09/04 Javascript
[45:32]Liquid vs LGD 2018国际邀请赛淘汰赛BO3 第二场 8.23
2018/08/24 DOTA
python中redis的安装和使用
2016/12/04 Python
python互斥锁、加锁、同步机制、异步通信知识总结
2018/02/11 Python
pandas读取csv文件,分隔符参数sep的实例
2018/12/12 Python
浅谈Pandas:Series和DataFrame间的算术元素
2018/12/22 Python
Python小进度条显示代码
2019/03/05 Python
在pycharm中文件取消用 pytest模式打开的操作
2020/09/01 Python
公司合作协议书范本
2014/04/18 职场文书
婚礼新人答谢词
2015/01/04 职场文书
护士先进个人总结
2015/02/13 职场文书
社区文明倡议书
2015/04/28 职场文书
golang 实现Location跳转方式
2021/05/02 Golang
springboot利用redis、Redisson处理并发问题的操作
2021/06/18 Java/Android
带你了解CSS基础知识,样式
2021/07/21 HTML / CSS
MySQL 语句执行顺序举例解析
2022/06/05 MySQL