Facade 布局是在面向对象编程中常用的一种软件设计布局方式。Facade 其实是一种包括复杂函数库的类,提供了更加简洁易读的接口。Facade 布局还能为一组结构复杂、设计简陋的 API 提供统1、设计周到的 API。php
Laravel 框架与该布局的特色类似,也称为 Facades。在本教程中,咱们会学习如何在其余框架应用 Laravel 的 “Facades”。在继续学习以前,让咱们简单了解一下 Ioc 容器。html
首先,咱们了解 Laravel 的 facades 内部工做结构。以后再讨论如何将之改造并用于其余环境。laravel
Laravel facade 是一种为容器内部服务提供相似静态接口的类。据其文档描述,Facades 是可触及容器服务底层实现方式的代理。编程
不过,在 PHP 社区,有关其名称的争论一直不断。一些人坚持修改此名称以免开发者的困惑,由于其并未彻底实现 Facade 布局。若是你也受此名称困扰,大能够为其取个别名。可是,请注意,下文将会用到的 Laravel 框架基类(base class)将会称为 Facade。数组
你可能也知道,容器内的每一个服务都有个惟一名称。在 laravel 应用中,可以使用 App::make()
方法或 app()
辅助函数从容器中直接获取服务。服务器
<?php App::make('some_service')->methodName();
前面已经提过,Laravel 使用 facade 类的好处是让开发者使用服务时更加便捷。使用 facade 类以后,下面的代码就能达到相同的效果:app
// ... someService::methodName(); // ...
在 Laravel 中,全部服务都包含一个 facade 类。这些 facade 类继承自 Illuminate/Support
包中的 Facade 基类。它们只需实现 getFacadeAccessor
方法便可,后者会返回容器内的服务名。composer
在上面的示例中,someService
表明 facade 类。methodName
实际上是容器内原服务的一个方法。若是跳出 Laravel 的语境查看上面的示例,则表示一个名为 someService
的类引出名为 methodName()
的静态方法。但 Laravel 并非这样实现接口的。在下一节,咱们将介绍 Laravel 的 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 类均继承自基类。咱们只需实现 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\SomeServiceFacade SomeServiceFacade:SomeMethod();
然而,在须要某个特定的 facade 类时,咱们必须在每一个脚本文件都写一遍上面的代码。Laravel 在处理 facade 别名时有其独特的方法——别名载入器(alias loader)。
全部的别名都保存在 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 array FancyName::someMethod()
在幕后,FancyName
会对应至 App\Facades\SomeServiceFacade
。
如今,咱们已经了解 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 基类。
只需继承 Facade 基类并实现 getFacadeAccessor
方法,便可为服务建立 facade。
在本文中,全部 facades 都会保存在 src/Facades
路径下。例如:名为 some.service
的服务,其 facade 类以下:
<?php namespace App\Facades use 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 类建立别名,咱们将使用以前介绍过的 AliasLoader
。AliasLoader
类由 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 官方博客