如何使用 Laravel Facades ?

WBOY
Release: 2016-06-20 12:41:29
Original
1187 people have browsed it

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();
Copy after login

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

// ...someService::methodName();// ...
Copy after login

在 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);        }    }
Copy after login

在上面的方法中,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'; }}
Copy after login

别名

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

use App\Facades\SomeServiceFacadeSomeServiceFacade:SomeMethod();
Copy after login

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

Laravel 如何给 Facades 加别名

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

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

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

现在,让我们看看 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()
Copy after login

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

在其他框架使用 Facades

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

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

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

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

<?phpSomeService::someMethod();
Copy after login

必备条件

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

composer require illuminate\support

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

创建 Facades

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

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

<?phpnamespace 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'; }}
Copy after login

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

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

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

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

<?phpIllumiante\Support\Facade::setFacadeApplication($app);
Copy after login

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

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

实现别名

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

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

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

创建别名数组

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

<?phpreturn [    'FancyName' => 'App\Facades\SomeService',];
Copy after login

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

注册别名

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

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

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

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

<?phpFancyName::methodName();
Copy after login

进行包装

一个 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 官方博客

source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template