laravel Facades—通过别名\外观的方式访问解析服务容器中对应的服务

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数组中加入新服务提供者
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值