Laravel中Facade的加载过程与原理详解

前言

本文主要给大家介绍了关于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') 方法。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • laravel5创建service provider和facade的方法详解

    本文实例讲述了laravel5创建service provider和facade的方法.分享给大家供大家参考,具体如下: laravel5创建一个facade,可以将某个service注册个门面,这样,使用的时候就不需要麻烦地use 了.文章用一个例子说明怎么创建service provider和 facade. 目标 我希望我创建一个AjaxResponse的facade,这样能直接在controller中这样使用: class MechanicController extends Contr

  • Laravel中注册Facades的步骤详解

    本文讲述了Laravel中注册Facades的步骤.分享给大家供大家参考,具体如下: 在Laravel中将类注册为Fcade可以使用Ioc容器,每次使用这个类的时候只会初始化一次类,类似单例模式,而且可以像使用静态方法调用类的方法,下面是在Laravel中注册Facades的步骤. 1.在项目app目录的Providers/AppServiceProvider.php中的register方法新增方法,代码如下. /** * Register any application services. *

  • Laravel中Facade的加载过程与原理详解

    前言 本文主要给大家介绍了关于Laravel中Facade加载过程与原理的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 简介 Facades(读音:/fəˈsäd/ )为应用程序的 服务容器 中可用的类提供了一个「静态」接口.你不必 use 一大串的命名空间,也不用实例化对象,就能访问对象的具体方法. use Config; class Test { public function index() { return Config::get('app.name');

  • PHP中的自动加载操作实现方法详解

    本文实例讲述了PHP中的自动加载操作实现方法.分享给大家供大家参考,具体如下: what is 自动加载? 或许你已经对自动加载有所了解.简单描述一下:自动加载就是我们在new一个class的时候,不需要手动去写require来导入这个class.php文件,程序自动帮我们加载导入进来.这是php5.1.2(好像是)版本新加入一个功能,他解放了程序员的双手,不需要手动写那么多的require,变得有那么点智能的感觉. 自动加载可以说是现代PHP框架的根基,任何牛逼的框架或者架构都会用到它,它发明

  • Android中Fragment的加载方式与数据通信详解

    一.加载方式 1. 静态加载 1.1 加载步骤 (1) 创建fragment:创建自定义Fragment类继承自Fragment类,同时将自定义Fragment类与Fragment视图绑定(将layout转换成View) View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) inflater用于绑定Fragment的布局文件,同时将该布局转换成View对象并返回:con

  • PHP框架自动加载类文件原理详解

    描述:公司项目PHP用作中间转发层(接收http请求,用 socket跟c++做通信),由于代码没有用到框架,这些东西自然就是之前的人自己写的.最近需要对这个底层进行优化,于是便看了下这部分的代码. 目的:这块代码的主要作用是把主目录下的所有插件类一次性全部加载进来.当使用尚未被定义的类(class)和接口(interface)时自动去加载.通过注册自动加载器,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类. 实现方法:主要用到PHP函数__autoload() 详细: error_

  • Python实现从文件中加载数据的方法详解

    前几篇都是手动录入或随机函数产生的数据.实际有许多类型的文件,以及许多方法,用它们从文件中提取数据来图形化. 比如之前python基础(12)介绍打开文件的方式,可直接读取文件中的数据,扩大了我们的数据来源.下面,将展示几种方法. 我们将使用内置的 csv 模块加载CSV文件 CSV文件是一种特殊的文本文件,文件中的数据以逗号作为分隔符,很适合进行数据的解析.先用excle建立如下表格和数据,另存为csv格式文件,放到代码目录下. 包含在Python标准库中自带CSV 模块,我们只需要impor

  • Android 动态加载 so实现示例详解

    目录 背景 so动态加载介绍 从一个例子出发 so库检索与删除 动态加载so 结束了吗? ELF文件 扩展 总结 背景 对于一个普通的android应用来说,so库的占比通常都是巨高不下的,因为我们无可避免的在开发中遇到各种各样需要用到native的需求,所以so库的动态化可以减少极大的包体积,自从2020腾讯的bugly团队发部关于动态化so的相关文章后,已经过去两年了,相关文章,经过两年的考验,实际上so动态加载也是非常成熟的一项技术了. 但是很遗憾,许多公司都还没有这方面的涉略又或者说不知

  • umi插件开发仿dumi项目加载markdown文件实现详解

    目录 引言 为什么不能直接展示markdown chainWebpack webpack loader 实现过程 新建插件 新建loader 新建测试文档 运行项目 解决文件加载类型错误 webpack ruletype 解决错误 引言 前面章节中我们已经顺利将tsx组件转换为页面展示,但是目前提供的功能和umi的约定式路由功能差不多,接下来我们将实现将markdown文件转换为页面展示. 为什么不能直接展示markdown 我们前面所使用的页面写法都是react组件式写法,umi通过webpa

  • linecache模块加载和缓存文件内容详解

    linecache模块 接触到linecache这个模块是因为前两天读attrs源码的时候看到内部代码引用了这个模块来模拟一个假文件,带着一脸疑问顺便读了一下这个模块的源码,发现其实也就那么回事儿,代码不多,在这总结一下. linecache模块可以读取文件并将文件内容缓存起来,方便后面多次读取.这个模块原本被设计用来读取Python模块的源代码,所以当一个文件名不在指定路径下的时候,模块会通过搜索路径(search path)来尝试读取文件. 接口 linecache模块的__all__参数其

  • vue 项目常用加载器及配置详解

    本文介绍了vue 项目常用加载器及配置详解,分享给大家,具体如下: 1.安装sass: 1.1 由于sass-loader依赖于node-sass,所以在安装sass-loader的同时还需安装node-sass npm install --save-dev node-sass npm install --save-dev sass-loader 1.2 安装完成后修改 <style>标签: <style lang="scss"></style> 2

  • 基于Vue渲染与插件的加载顺序的问题详解

    Vue实践分享(三)在实际项目的开发过程中,经常会遇到页面还没渲染完成而插件就已经开始加载的问题,这样就会导致显示和功能出错. 可以通过Vue中的nextTick来解决 Vue.nextTick(function() { //widget }); 这样就会在页面渲染完成后再执行nextTick内的插件 以上这篇基于Vue渲染与插件的加载顺序的问题详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们. 您可能感兴趣的文章: 浅谈Vue的加载顺序探讨 Vue.js学习教程

随机推荐