Facades
Laravel官方文档中对Facades的描述:
在整个 Laravel 文档中,您会常看到通过“外观(Facades)”与 Laravel 功能交互的代码示例。Facades 为应用程序的服务容器中可用的类提供“静态”接口。Laravel 附带了许多门面,可以访问几乎所有 Laravel 的功能。
Laravel 外观充当服务容器中底层类的“静态代理”,提供简洁、富有表现力的语法的好处,同时比传统的静态方法保持更多的可测试性和灵活性。如果您不完全了解 Facades 在幕后是如何工作的,那就完全没问题 - 只要顺其自然,继续学习 Laravel。
个人理解总结:Facades是通过别名\外观的方式访问解析服务容器中对应的服务的"静态代理"
相比传统的静态方法,更加灵活便于测试
下面laravel默认已设置好的Facades,路径:\vendor\laravel\framework\src\Illuminate\Support\Facades
通过下面2行代码,既可以使用上图别名cache的服务容器功能
use Illuminate\Support\Facades\Cache;
Cache::get(‘key’)
Facades是如何工作的
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;
Route::get('/cache', function () {
return Cache::get('key');
});
上面例子中,找到Illuminate\Support\Facades \Cache.php文件,代码仅有getFacadeAccessor方法并没有get方法,
<?php
namespace Illuminate\Support\Facades;
class Cache extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'cache';
}
}
Cache继承于Facade类,父类Facade也没有找到get方法,但是父类Facade里面有魔术方法__callStatic,
所以接下来会运行到__callStatic魔术方法处
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);
}
public static function getFacadeRoot()
{
//getFacadeAccessor方法被子类重写,以Cache类的为例,根据getFacadeAccessor方法,
//这里边返回了值:cache,即return static::resolveFacadeInstance('cache');
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
//获取组件的注册名称,这个类必须被子类重写,不然则抛出异常
protected static function getFacadeAccessor()
{
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}
protected static function resolveFacadeInstance($name)
{
//如果是对象就直接返回
if (is_object($name)) {
return $name;
}
//判断是否已存在,存在则直接返回,防止重复解析
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
//这里的$app时什么,在代码中看到$app是保护属性,那就找到他的set方法
if (static::$app) {
return static::$resolvedInstance[$name] = static::$app[$name];
}
}
public static function setFacadeApplication($app)
{
static::$app = $app;
/**
在当前代码中没有调用setFacadeApplication,那setFacadeApplication方法在哪里被调用到,
在前面了解laravel生命周期时,在kernel是有注册门面类\Illuminate\Foundation\Bootstrap\RegisterFacades::class
在注册门面时候先执行clearResolvedInstances清除实例,再调用setFacadeApplication绑定$app
那上面又是如何能通过static::$app[$name]获取到IOC容器中对应的类呢,
这是因为容器Container类继承了ArrayAccess接口,这是一个php的预定接口
class Container implements ArrayAccess.ContainerContract{
//static::$app[$name]相当于执行下面方法
//Facades是通过别名或外观的方式访问服务容器中相应的服务提供者
public function offsetGet($key)
{
return $this->make($key);//到这里就基本清晰了
}
}
**/
}
下图为laravel生命周期,\Illuminate\Foundation\Bootstrap\RegisterFacades::class执行位置加了红框
下图为Facades的思维导图
魔术方式__collStatic
<?php
/**
* @author biny
* @date 2021-07-26 19:39
*/
class Facades
{
public function __call(string $method, array $args)
{
return $method.'__call';
}
public static function __callStatic(string $method, array $args)
{
return $method.'__callStatic';
}
}
echo Facades::index();
//输出:index__callStatic
ArrayAccess(PHP预定接扣)
ArrayAccess提供以数组形式访问对象的接口
<?php
/**
* @author biny
* @date 2021-07-26 22:21
*/
class Ioc implements ArrayAccess
{
public function offsetExists($offset)
{
echo "this is offsetExists(".$offset.")<br>";
}
public function offsetGet($offset)
{
echo "this is offsetGet(".$offset.")<br>";
}
public function offsetSet($offset, $value)
{
echo "this is offsetSet(".$offset.":".$value.")<br>";
}
public function offsetUnset($offset)
{
echo "this is offsetUnset(".$offset.")<br>";
}
}
$ioc = new Ioc();
$ioc['cache'];
isset($ioc['cache']);
$ioc['cache'] = 'aaa';
unset($ioc['cache']);
输出结果:
示例
1、创建服务订单类app\Services\Order.php
<?php
/**
* @author biny
* @date 2021-07-26 23:03
*/
namespace App\Services;
class Order
{
public function index(){
return '这是订单服务类';
}
}
2、创建订单服务提供者 php artisan make:provider OrderServiceProvider
命令会创建app\Providers\OrderServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\Order;
class OrderServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
//注册订单服务类
$this->app->singleton('order',function($app){
return new Order();
});
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
}
}
3、创建Order外观类app\Facades\Order.php
<?php
/**
* @author biny
* @date 2021-07-26 22:55
*/
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
//use App\Services\Order as OrderService;
class Order extends Facade
{
protected static function getFacadeAccessor()
{
//return OrderService::class; //直接返回类
return 'order'; //返回别名,需要先将订单服务类注册到容器中,优点是都由容器统一管理调度,再通过Facade外观在编码时使用别名调用。
}
}
4、在routes\web.php中使用Order服务:
use App\Facades\Order;
Route::get('/', function () {
return Order::index();//这里就可以通过Facade的方式直接使用Order服务,通过这个方式,可用于框架任意地方
});
5、注册服务提供者:在config/app.php的providers数组中加入新服务提供者