2019独角兽企业重金招聘Python工程师标准>>>

Facade 布局是在面向对象编程中经常使用的一种软件设计布局方式。Facade 实际上是一种包括复杂函数库的类,提供了更加简洁易读的接口。Facade 布局还能为一组结构复杂、设计简陋的 API 提供统一、设计周到的 API。

Laravel 框架与该布局的特点相似,也称为 Facades。在本教程中,我们会学习如何在其他框架应用 Laravel 的 “Facades”。在继续学习之前,让我们简单了解一下 Ioc 容器。

首先,我们了解 Laravel 的 facades 内部工作结构。之后再讨论如何将之改造并用于其他环境。

Laravel 中的 Facades

Laravel facade 是一种为容器内部服务提供类似静态接口的类。据其文档描述,Facades 是可触及容器服务底层实现方式的代理。

不过,在 PHP 社区,有关其名称的争论一直不断。一些人坚持修改此名称以避免开发者的困惑,因为其并未完全实现 Facade 布局。如果你也受此名称困扰,大可以为其取个别名。但是,请注意,下文将会用到的 Laravel 框架基类(base class)将会称为 Facade。

How Facades Are implemented in Laravel

Facades 在 Laravel 中如何实现

你可能也知道,容器内的每个服务都有个唯一名称。在 laravel 应用中,可使用 App::make() 方法或 app() 辅助函数从容器中直接获取服务。

<?phpApp::make('some_service')->methodName();

前面已经提过,Laravel 使用 facade 类的好处是让开发者使用服务时更加便捷。使用 facade 类之后,下面的代码就能达到相同的效果:

// ...
someService::methodName();
// ...

在 Laravel 中,所有服务都包含一个 facade 类。这些 facade 类继承自 Illuminate/Support 包中的 Facade 基类。它们只需实现 getFacadeAccessor 方法即可,后者会返回容器内的服务名。

在上面的示例中,someService 代表 facade 类。methodName 其实是容器内原服务的一个方法。如果跳出 Laravel 的语境查看上面的示例,则表示一个名为 someService 的类引出名为 methodName() 的静态方法。但 Laravel 并不是这样实现接口的。在下一节,我们将介绍 Laravel 的 Facade 基类在幕后的运作方式。

Base Facade

Facade 类包含一个名为 $app 的私有属性,其值为服务容器的引用。如果要在 Laravel 之外使用 facades,必须使容器明确使用 setFacadeApplication() 方法。

在 facade 基类内部,__callStatic 魔术方法用于处理实际并不存在的静态方法的调用。如果调用 Laravel facade 类的静态方法, __callStatic 方法便会激活,因为 facade 类并未实现该方法。因此,__callStatic 会从容器获取各自的服务,进而调用之。

以下是 facade 基类中 __callStatic 方法的实现方式:

<?php
// ...
/*** Handle dynamic, static calls to the object.** @param  string  $method* @param  array   $args* @return mixed*/public static function __callStatic($method, $args){$instance = static::getFacadeRoot();switch (count($args)) {case 0:return $instance->$method();case 1:return $instance->$method($args[0]);case 2:return $instance->$method($args[0], $args[1]);case 3:return $instance->$method($args[0], $args[1], $args[2]);case 4:return $instance->$method($args[0], $args[1], $args[2], $args[3]);default:return call_user_func_array([$instance, $method], $args);}}

在上面的方法中,getFacadeRoot() 会从容器获取服务。

Facade 类解析

每个 facade 类均继承自基类。我们只需实现 getFacadeAccessor() 方法,该方法用于返回容器中的服务名。

<?php namespace App\Facades;use Illuminate\Support\Facades\Facade as BaseFacade;class SomeServiceFacade extends BaseFacade {/*** Get the registered name of the component.** @return string*/protected static function getFacadeAccessor() { return 'some.service'; }}

别名

由于 Laravel facades 是 PHP 类,在使用之前我们得导入它们。PHP 支持命名空间与自动导入,因此只要调用全限定名,即可自动载入这些类。PHP 还支持使用 use 指令给类取别名:

use App\Facades\SomeServiceFacadeSomeServiceFacade:SomeMethod();

然而,在需要某个特定的 facade 类时,我们必须在每个脚本文件都写一遍上面的代码。Laravel 在处理 facade 别名时有其独特的方法——别名载入器(alias loader)。

Laravel 如何给 Facades 加别名

所有的别名都保存在 app.php 配置文件的 aliases 数组中,该文件保存在 /config 目录下。

查看该数组,会发现每个别名都与一个全限定类名对应。这意味着我们可以给 facade 类选定任意的名字。

// ..
'aliases' => [// ...'FancyName' => 'App\Facades\SomeServiceFacade',
],

现在,让我们看看 Laravel 如何使用该数组给 facade 类取别名。在引导阶段,Laravel 会使用来自 Illuminate\Foundation 包的 AliasLoader 服务。AliasLoader 以该别名数组为参数,遍历其所有元素,使用 PHP 的 spl_autoload_register 创建一个 __autoload 函数队列。各个 __autoload 函数会用 PHP 的 class_alias 函数为各个 facade 类创建别名。

因此,我们无需像使用 use 指令时那样在使用类前导入之并为其创建别名。当我们试图使用一个不存在的类时,PHP 会检查 __autoload 队列以得到合适的 autoloader。这时,AliasLoader 已经记下所有的 __autoload 函数。各个 autoloader 会选定一个类名并根据别名数组推导出对应的初始类名。最后,它会为其创建别名。请参考下面的方法调用:

<?php// FancyName is resolved to App\Facades\SomeServiceFacade according to the aliases arrayFancyName::someMethod()

在幕后,FancyName 会对应至 App\Facades\SomeServiceFacade

在其他框架使用 Facades

现在,我们已经了解 Laravel 如何处理 facades 与别名,我们可以将 Laravel 的 facade 方法运用到其他环境中。接下来,我们会在 Silex 框架使用 facades。然而,只要遵循同样的理念,你也可以将之用在别的框架。

Silex 拥有继承自 Pimple 的容器。使用 $app 对象即可调用容器内的服务:

<?php
$app['some.service']->someMethod()

有了 facade 类,我们可以为 Silex 服务提供一个类似静态的接口。此外,我们也可以使用 AliasLoader 服务为这些 facades 创建有意义的别名。因此,我们可以重组上面的代码:

<?php
SomeService::someMethod();

必备条件

为了使用 facade 基类,我们要使用 composer 指令安装 Illuminate\Support 包:

composer require illuminate\support

此包还包含其他服务。但目前我们只需要 facade 基类。

创建 Facades

只需继承 Facade 基类并实现 getFacadeAccessor 方法,即可为服务创建 facade。

在本文中,所有 facades 都会保存在 src/Facades 路径下。例如:名为 some.service 的服务,其 facade 类如下:

<?php
namespace App\Facadesuse Illuminate\Support\Facades\Facade;class SomeServiceFacade extends Facade {/*** Get the registered name of the component.** @return string*/protected static function getFacadeAccessor() { return 'some.service'; }}

请注意,此类位于 app\facades 命名空间下。

现在只剩下设定 facade 类的应用容器。如前所述,在静态语境下调用 facade 类的方法,会触发 __callStatic 方法。该方法会用 getFacadeAccessor() 返回的数据识别容器内的服务,并试图获取之。在 Laravel 之外使用 facade 基类时,容器对象并不是自动设定的,需要手动设定。

为此,使用 facade 基类的 setFacadeApplication 方法,可以设定 facade 类的应用容器。

app.php 文件,添加以下代码:

<?php
Illumiante\Support\Facade::setFacadeApplication($app);

这会给继承自 facade 基类的所有 facades 设定容器。

现在,无需直接从容器获取服务,我们可以使用刚刚创建的 facade 类来获取,该类还允许我们调用静态语境下的所有方法。

实现别名

为了给 facade 类创建别名,我们将使用之前介绍过的 AliasLoaderAliasLoader 类由 illuminate\foundation 包提供,可以下载整个包,也可以拷贝部分代码保持为文件。

如果你想拷贝源文件,建议将其保存在 src/Facades 目录下。你可以根据项目的结构为 AliasLoader 类创建命名空间。

在本例中,我们将拷贝代码并将其保存在 app/facades 命名空间下。

创建别名数组

config 目录下创建 aliases.php 文件,并填入 alias-facade 绑定:

<?php
return ['FancyName' => 'App\Facades\SomeService',
];

FancyName 是我们给 App\Facades\SomeService 建立的别名。

注册别名

AliasLoader 是一种单例服务。要创建或得到别名载入器(alias loader)的实例,需调用 getInstance 方法并以别名数组为参数。最后,为了注册这些别名,需调用其 register 方法。

再次打开 app.php 文件,加入以下代码:

<?php// ...$aliases = require __DIR__ . '/../../config/aliases.php';
App\Facades\AliasLoader::getInstance($aliases)->register();

现在,大功告成了!我们可以这样使用该服务:

<?php
FancyName::methodName();

进行包装

一个 Facade 类只需实现 getFacadeAccessor 方法即可,后者会返回容器内的服务名。若要在 Laravel 环境外使用 facade,必须使用 setFacadeApplication() 方法明确设定服务容器。

要引用 facade 类,我们可以使用全限定类名或使用 PHP 的 use 指令导入之。或者,遵循 Laravel 给 facades 创建别名的方法,使用 alias loader。

原文链接:http://www.sitepoint.com/how-laravel-facades-work-and-how-to-use-them-elsewhere/ (作者:Reza Lavaryan)本文系 OneAPM 工程师编译整理。

OneAPM for PHP 能够深入到所有 PHP 应用内部完成应用性能管理 能够深入到所有 PHP 应用内部完成应用性能管理和监控,包括代码级别性能问题的可见性、性能瓶颈的快速识别与追溯、真实用户体验监控、服务器监控和端到端的应用性能管理。想技术文章,请访问 OneAPM 官方技术博客。

本文转自 OneAPM 官方博客

转载于:https://my.oschina.net/oneapmofficial/blog/597301

如何使用 Laravel Facades ?相关推荐

  1. 代码提示_PHPStorm 支持 Laravel Facades 的代码提示

    好久没用 Laravel 和 PHPStorm 了,最近开发了小项目,用的时候发现 Laravel 的 Facades 是没有代码补全提示的,原因是 Facades 是基于 静态方法重载 __call ...

  2. Laravel Facades的实现原理

    Facades 什么是Facades Facades是我们在Laravel应用开发中使用频率很高的一个组件,叫组件不太合适,其实它们是一组静态类接口或者说代理,让开发者能简单的访问绑定到服务容器里的各 ...

  3. 简单两步就能将 Laravel Log 信息发到其他平台上

    我们在写代码时,都想自己的代码尽可能的不影响现有的代码. 或者说,最大化不改动任何代码的情况下,如何嵌入我们的新功能?这是我们常说的「非侵入式」的开发方式. 使用「非侵入式」的开发模式,主要在提供第三 ...

  4. 优雅的使用 PhpStorm 来开发 Laravel 项目

    [目录] Prerequisites plugin installation and configuration 1 Ensure Composer is initialized 2 Install ...

  5. Laravel 服务提供者和门面模式

    以 Laravel 自带的文件系统为例,在 config/app.php 的配置文件的 providers 数组中,注册了一个服务提供者: IlluminateFilesystemFilesystem ...

  6. Laravel 测试: PHPUnit 入门教程

    介绍 PHPUnit 测试的基础知识,使用基本的 PHPUnit 断言和 Laravel 测试助手. 介绍 PHPUnit 是最古老和最著名的 PHP 单元测试包之一.它主要用于单元测试,这意味着可以 ...

  7. Laravel核心解读 -- 外观模式

    外观模式 外观模式(Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加 ...

  8. Laravel中使用webhook开发Telegram机器人自定义指令

    Laravel使用Dingo API+JWT实现认证机制 无痛刷新Token 一.操作Telegram 1. 创建Telegram机器人 2. 创建command指令 二.安装[Telegram-Bo ...

  9. laravel集成Telegram Bot 机器人

    1 github 下载 https://github.com/irazasyed/telegram-bot-sdk 或者 composer 用2.0版本 3.0都没文档 composer requir ...

最新文章

  1. 网络异常_网络异常易频发,流量分析来排查
  2. 序列化和反序列化的几种方式(DataContractSerializer)(二)
  3. 转载:关于错排的相关知识
  4. php5.6.30环境报错Call to undefined function ImageCreate() 编译安装 gd库
  5. 电脑中毒的表现有哪些?电脑中毒了怎么办?
  6. ACL2020 | 线上搜索结果大幅提升!亚马逊提出对抗式query-doc相关性模型
  7. python if not a_python --- if not
  8. java守护线程与用户线程_详解Java线程-守护线程与用户线程
  9. 未来的 AI 芯片将提升百倍性能!
  10. 深入理解CSS Media媒体查询
  11. 图解设计模式+代码(一):创建型模式
  12. Elsevier旗下期刊利用latex模板撰写论文记录
  13. Redis的配置文件详解
  14. python在线编辑器编译excel_如何利用ONLYOFFICE将在线文档编辑器集成到Python Web应用程序中?...
  15. 蓝桥:8皇后·改(⼋皇后问题)
  16. 小新pro13睡眠后无法唤醒_小新air12、air13、air13pro睡眠后无法唤醒的调试方法
  17. 机器学习必须需要大量数据?小数据集也能有大价值!
  18. python 全栈开发,Day87(ajax登录示例,CSRF跨站请求伪造,Django的中间件,自定义分页)...
  19. 毕业设计 stm32单片机的目标检测与跟踪系统 -物联网 openmv 嵌入式
  20. ESP32使用双cpu同时工作测试-arduino开发环境

热门文章

  1. 使用OData协议查询Windows日志
  2. 中国移动试商用GPS手机导航业务 包月资费15元
  3. pycharm的pip配置
  4. maven详解scope
  5. JUnit5 @BeforeAll注解示例
  6. java 解析docx_java解析xlsx和docx 文件 | 学步园
  7. 女生在java开发和前端之间不知道选择哪个怎么办?
  8. python 类和对象_面向对象的编程思想和Python的类,访问和属性,继承
  9. linux复制文件属性不变例子,linux中文件系统属性chattr权限
  10. db2数据库服务器时间怎么修改,DB2数据库中,肿么修改数据的创建时间,求SQL语句。...