Laravel 核心--Facades 门面
Laravel 核心--Facades 门面
介绍
Facades 为应用的 IoC 服务容器 的类提供了一个静态的接口。Laravel 里面自带了一些 Facades,如Cache等。Laravel 的门面作为服务容器中底层类的“静态代理”,相比于传统静态方法,在维护时能够提供更加易于测试、更加灵活、简明优雅的语法。
解释
在 Laravel 应用这个上下文里面,一个 Facade 就是一个类,使用这个类可以访问到来自容器里的一个对象,这个功能就是在 Facade 类里面定义的。Laravel 的 Facades 还有任何你自己定义的 Facades,都会去继承 Facade 这个类。
你的 Facade 类只需要实施一个的方法:getFacadeAccessor。要在容器里 resolve 什么出来,都是在这个方法里去做的。Facade 这个基类里面使用了__callStatic() 魔术方法,可以延迟到 resolved 对象上的,来自 Facade 的调用。
所以,当你使用 Facade 调用的时候,比如像这样:Cache:get,laravel 会从 Ioc 服务容器 里面 resolves 缓存管理类,然后再去调用这个类上面的 get 方法。Laravel 的 Facades 可以去定位服务,它是一种使用 Laravel 的 Ioc 服务容器 的更方便的语法。
优点
Facade 有诸多优点,其提供了简单、易记的语法,让我们无需记住长长的类名即可使用 Laravel 提供的功能特性,此外,由于他们对 PHP 动态方法的独到用法,使得它们很容易测试。
实际使用
下面的例子,去调用了一下 Laravel 的缓存系统。先看一下下面这行代码,你可能会觉得,这是直接去调用 Cache 这个类上面的一个叫 get 的静态的方法。
$value = Cache::get('key');
不过,如果你查看 Illuminate\Support\Facades\Cache 这个类,你会发现这里根本就没有 get 这个静态方法:
class Cache extends Facade {
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor() { return 'cache'; }
}
Cache 这个类继承了 Facade 这个基类,它里面定义了一个叫 getFacadeAccessor() 的方法。注意,这个方法的干的事就是去返回一个 Ioc 绑定的名字,这里就是 cache。
当用户在引用任何在 Cache 这个 Facade 上的静态方法的时候,Laravel 就会从 Ioc 服务容器 里面去 resolves cache 这个绑定,并且会去执行在对象上的这个所请求的方法(这里就是 get 这个方法)。
所以,我们在调用 Cache::get 的时候,它的真正的意思是这样的:
$value = $app->make('cache')->get('key');
导入 Facades
注意,在使用
facade的时候,如果控制器里面用到了命名空间,你需要把 Facade 类导入到这个命名空间里。所有的 Facades 都是在全局命名空间下:
<?php namespace App\Http\Controllers;
use Cache;
class PhotosController extends Controller {
/**
* Get all of the application photos.
*
* @return Response
*/
public function index()
{
$photos = Cache::get('photos');
//
}
}
创建 Facades
创建 Facade 只需要三个东西:
- 一个 IoC 绑定。
- 一个 Facade 类。
- 一个 Facade 别名的配置。
在下面我们定义了一个类:PaymentGateway\Payment 。
namespace PaymentGateway;
class Payment {
public function process()
{
//
}
}
我们需要能在 Ioc 服务容器 里面去 resolve 这个类。所以,先要去添加一个 Service Provider 绑定:
App::bind('payment', function()
{
return new \PaymentGateway\Payment;
});
去注册这个绑定最好的方法就是去创建一个新的 Service Provider ,把它命名为 PaymentServiceProvider ,然后把它绑定到 register 方法上。再去配置 laravel 在 config/app.php 这个配置文件里加载你的 Service Provider。
下一步就是去创建自己的 Facade 类:
use Illuminate\Support\Facades\Facade;
class Payment extends Facade {
protected static function getFacadeAccessor() {
return 'payment';
}
}
最后,如果你愿意,可以去给 Facade 添加一个别名,放到 config/app.php 配置文件里的 aliases 数组里。
可以去调用 Payment 类的一个实例上的 process 这个方法了。像这样:
Payment::process();
何时使用 Facade
注意
在使用 Facade 也有需要注意的地方,一个最主要的危险就是类范围蠕变。由于Facade如此好用并且不需要注入,在单个类中使用过多Facade,会让类很容易变得越来越大。使用依赖注入则会让此类问题缓解,因为一个巨大的构造函数会让我们很容易判断出类在变大。因此,使用Facade的时候要尤其注意类的大小,以便控制其有限职责。
注:构建与 Laravel 交互的第三方扩展包时,最好注入 Laravel 契约而不是使用门面,因为扩展包在 Laravel 之外构建,你将不能访问 Laravel 的门面测试辅助函数。
Facade vs. 依赖注入
依赖注入的最大优点是可以替换注入类的实现,这在测试时很有用,因为你可以注入一个模拟或存根并且在存根上断言不同的方法。
但是在静态类方法上进行模拟或存根却行不通,不过,由于Facade 使用了动态方法对服务容器中解析出来的对象方法调用进行了代理,我们也可以像测试注入类实例那样测试门面。例如,给定以下路由:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
我们可以这样编写测试来验证 Cache::get 方法以我们期望的方式被调用:
use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
Facade vs. 辅助函数
除了Facade之外,Laravel 还内置了许多辅助函数用于执行通用任务,比如生成视图、触发事件、分配任务,以及发送 HTTP 响应等。很多辅助函数提供了和相应 Facade 一样的功能,例如,下面这个Facade调用和辅助函数调用是等价的:
return View::make('profile');
return view('profile');
Facade和辅助函数之间并不存在实质性差别,使用辅助函数的时候,可以像测试相应门面那样测试它们。例如,给定以下路由:
Route::get('/cache', function () {
return cache('key');
});
在调用底层, cache 方法会去调用 Cache Facade上的 get方法,因此,尽管我们使用这个辅助函数,我们还是可以编写如下测试来验证这个方法以我们期望的方式和参数被调用:
use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
Facade 工作原理
在 Laravel 应用中,Facade就是一个为容器中对象提供访问方式的类。该机制原理由 Facade 类实现。Laravel 自带的 Facade,以及我们创建的自定义门面,都会继承自 Illuminate\Support\Facades\Facade 基类。可以参考 Facade 实现原理
Facade 类只需要实现一个方法:getFacadeAccessor。正是 getFacadeAccessor方法定义了从容器中解析什么,然后 Facade 基类使用魔术方法 __callStatic() 从你的门面中调用解析对象。
下面的例子中,我们将会调用 Laravel 的缓存系统,浏览代码后,也许你会觉得我们调用了 Cache 的静态方法 get:
<?php
namespace App\Http\Controllers;
use Cache;
use App\Http\Controllers\Controller;
class UserController extends Controller{
/**
* 为指定用户显示属性
*
* @param int $id
* @return Response
*/
public function showProfile($id)
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
注意我们在顶部位置引入了 Cache Facade。该门面作为代理访问底层 Illuminate\Contracts\Cache\Factory 接口的实现。我们对门面的所有调用都会被传递给 Laravel 缓存服务的底层实例。
如果我们查看 Illuminate\Support\Facades\Cache 类的源码,将会发现其中并没有静态方法 get:
class Cache extends Facade
{
/**
* 获取组件注册名称
*
* @return string
*/
protected static function getFacadeAccessor() {
return 'cache';
}
}
Cache Facade 继承 Facade 基类并定了 getFacadeAccessor方法,该方法的工作就是返回服务容器绑定类的别名,当用户引用 Cache
类的任何静态方法时,Laravel 从服务容器中解析 cache
绑定,然后在解析出的对象上调用所有请求方法(本例中是 get
)
门面类列表
下面列出了每个门面及其对应的底层类,这对深入给定根门面的 API 文档而言是个很有用的工具。服务容器绑定键也被包含进来:
门面 Facade |
类 class |
服务容器绑定 |
|---|---|---|
| App | Illuminate\Foundation\Application | app |
| Artisan | Illuminate\Contracts\Console\Kernel | artisan |
| Auth | Illuminate\Auth\AuthManager | auth |
| Blade | Illuminate\View\Compilers\BladeCompiler | blade.compiler |
| Bus | Illuminate\Contracts\Bus\Dispatcher | |
| Cache | Illuminate\Cache\Repository | cache |
| Config | Illuminate\Config\Repository | config |
| Cookie | Illuminate\Cookie\CookieJar | cookie |
| Crypt | Illuminate\Encryption\Encrypter | encrypter |
| DB | Illuminate\Database\DatabaseManager | db |
| DB(Instance) | Illuminate\Database\Connection | |
| Event | Illuminate\Events\Dispatcher | events |
| File | Illuminate\Filesystem\Filesystem | files |
| Gate | Illuminate\Contracts\Auth\Access\Gate | |
| Hash | Illuminate\Contracts\Hashing\Hasher | hash |
| Lang | Illuminate\Translation\Translator | translator |
| Log | Illuminate\Log\Writer | log |
| Illuminate\Mail\Mailer | mailer |
|
| Notification | Illuminate\Notifications\ChannelManager | |
| Password | Illuminate\Auth\Passwords\PasswordBrokerManager | auth.password |
| Queue | Illuminate\Queue\QueueManager | queue |
| Queue(Instance) | Illuminate\Contracts\Queue\Queue | queue |
| Queue(Base Class) | Illuminate\Queue\Queue | |
| Redirect | Illuminate\Routing\Redirector | redirect |
| Redis | Illuminate\Redis\Database | redis |
| Request | Illuminate\Http\Request | request |
| Response | Illuminate\Contracts\Routing\ResponseFactory | |
| Route | Illuminate\Routing\Router | router |
| Schema | Illuminate\Database\Schema\Blueprint | |
| Session | Illuminate\Session\SessionManager | session |
| Session(Instance) | Illuminate\Session\Store | |
| Storage | Illuminate\Contracts\Filesystem\Factory | filesystem |
| URL | Illuminate\Routing\UrlGenerator | url |
| Validator | Illuminate\Validation\Factory | validator |
| Validator(Instance) | Illuminate\Validation\Validator | |
| View | Illuminate\View\Factory | view |
| View(Instance) | Illuminate\View\View |
小礼物走一走,来简书关注我
Laravel 核心--Facades 门面的更多相关文章
- 核心概念 —— 门面(Facades)
1.简介 门面为应用的服务容器中的绑定类提供了一个"静态"接口.Laravel 内置了很多门面,你可能在不知道的情况下正在使用它们.Laravel 的门面作为服务容器中的底层类的& ...
- Laravel核心之IOC和Facade 架构分析1
控制反转(Inversion of Control) 缩写为IoC 最常见的方式叫做依赖注入 简单说来,就是一个类把自己的的控制权交给另外一个对象,类间的依赖由这个对象去解决. Laravel 中的使 ...
- Laravel 核心概念
工欲善其事,必先利其器.在开发Xblog的过程中,稍微领悟了一点Laravel的思想.确实如此,这篇文章读完你可能并不能从无到有写出一个博客,但知道Laravel的核心概念之后,当你再次写起Larav ...
- Laravel核心解读--HTTP内核
Http Kernel Http Kernel是Laravel中用来串联框架的各个核心组件来网络请求的,简单的说只要是通过public/index.php来启动框架的都会用到Http Kernel,而 ...
- Laravel开发:Laravel核心——Ioc服务容器源码解析(服务器绑定)
服务容器的绑定 bind 绑定 bind 绑定是服务容器最常用的绑定方式,在 上一篇文章中我们讨论过,bind 的绑定有三种: 绑定自身 绑定闭包 绑定接口 今天,我们这篇文章主要从源码上讲解 Ioc ...
- Laravel核心解读--ENV的加载和读取
Laravel在启动时会加载项目中的.env文件.对于应用程序运行的环境来说,不同的环境有不同的配置通常是很有用的. 例如,你可能希望在本地使用测试的Mysql数据库而在上线后希望项目能够自动切换到生 ...
- laravel 核心类Kernel
vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php.是laravel处理网络请求的最核心类,在app容器准备好了之后, ...
- Laravel开发:Laravel核心——Ioc服务容器
服务容器 在说 Ioc 容器之前,我们需要了解什么是 Ioc 容器. Laravel 服务容器是一个用于管理类依赖和执行依赖注入的强大工具. 在理解这句话之前,我们需要先了解一下服务容器的来龙去脉: ...
- 小白也能看懂的 Laravel 核心概念讲解
自动依赖注入 什么是依赖注入,用大白话将通过类型提示的方式向函数传递参数. 实例 1 首先,定义一个类: /routes/web.php class Bar {} 假如我们在其他地方要使用到 Bar ...
随机推荐
- TCP长连接保持连接状态
对于TCP长连接保活是十分必要的,原因如下: 1.系统多在OA网和外网间有防火墙隔离,很多防火墙对一段时间内没有报文活动的socket会自动关闭. 2.对于非正常断开的连接系统并不能侦测到,比如防火墙 ...
- 使用Intellij IDEA将web项目导出为war包
前言:IDEA导出war包的方式与MyEclipse有一点不同,使笔者在使用的时候有点困惑,在网上查阅相关资料的时候,发现其描述十分不清晰,于是就有了这篇随笔的诞生.话不多说,直接进入正题. 注:ID ...
- centos7下安装docker(18.2docker日志---ELK)
ELK是三个软件得组合:Elasticsearch,Logstash,Kibana Elasticsearch:实时查询的全文搜索引擎.Elasticsearch的设计目的就是能够处理和搜索巨量的日志 ...
- 详解Transformer模型(Atention is all you need)
1 概述 在介绍Transformer模型之前,先来回顾Encoder-Decoder中的Attention.其实质上就是Encoder中隐层输出的加权和,公式如下: 将Attention机制从Enc ...
- 【转】FFmpeg获取DirectShow设备数据(摄像头,录屏)
这两天研究了FFmpeg获取DirectShow设备数据的方法,在此简单记录一下以作备忘.本文所述的方法主要是对应Windows平台的. 1. 列设备 ffmpeg -list_devic ...
- Linux:Day6(下) vim编辑器
vim编辑器 简介: vi:Visual Interface,文本编辑器 文本:ASCII,Unicode 文本编辑种类: 行编辑器:sed 全屏编辑器:nano,vi VIM - Vi IMprov ...
- Z370主板的黑苹果usb3.0驱动安装
注意每个版本的usb端口代码是不一样的!一般是10.10到10.12 是一个版本,10.13 是一个版本 配合USBInjectAll.kext这个驱动就能顺利驱动你的usb3.0 .如果你是比较新 ...
- span<T>之高性能字符串操作实测
.net中的字符串操作性能问题由来已久,幸运的是微软推出了span<T>高性能指针操作封装工具类.这个类到底有多高的性能呢?网上传言反正很高,但是实际上在网上很难找到合适的测试实例,这让本 ...
- 算法相关——Java排序算法之插入排序(四)
0. 前言 本系列文章将介绍一些常用的排序算法.排序是一个非常常见的应用场景,也是开发岗位面试必问的一道面试题,有人说,如果一个企业招聘开发人员的题目中没有排序算法题,那说明这个企业不是一个" ...
- 圆角矩形shader
在游戏中,有时需要对一张矩形图片进行切割,绘制成圆角矩形. circelrect.vert attribute vec4 a_position; attribute vec4 a_normal; at ...