[Laravel 5.4] 新功能: 实时 Facade

概述

Taylor Otwell 昨天发了一条推,Laravel 5.4估计很快就要来了

file

即将到来的Laravel5.4,让我最激动的一个新功能应该是,实时Facades

file

如果你对 Laravel Facades 还不熟悉,点击这里学习 Facades


例子

安装laravel 5.4

使用laravel-installer,使用--dev参数拿到开发版

laravel new laravel54 --dev

新建一个类

我在App目录下创建了Milkmeowo.php

namespace App;

class Milkmeowo
{

    public function meow()
    {
        return 'cat meow';
    }
}

让 Facade 飞起来

php 版本小于7

use Facades\App\Milkmeowo;

php 版本 7 以上可以使用 Group use declarations

use Facades\ {
    App\Milkmeowo
};

在任何你需要用到的地方,这里我在路由文件

// pre php 7
//use Facades\App\Milkmeowo;

// php 7+
use Facades\ {
    App\Milkmeowo
};

Route::get('/meow', function () {
    return Milkmeowo::meow();
});

就可以看到

file

超级方便有木有

所以在laravel5.4中任何类都可以像Facede那样飞起来了,但注意

千万不要使用过渡


实时Facade实现原理

本文默认你已经熟悉Facade,本文主要讲实时 Facade的实现原理,有关Facade的具体加载原理,不在本文讨论范围之内

相信大部分人都知道Facade是通过class_alias来是实现的,通过Illuminate/Foundation/AliasLoader.php进行加载

实时Facade 就是改进了load方法

    /**
     * The namespace for all real-time facades.
     *
     * @var string
     */
    protected static $facadeNamespace = 'Facades\\';

    /**
     * Load a class alias if it is registered.
     *
     * @param  string  $alias
     * @return bool|null
     */
    public function load($alias)
    {
        if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) {
            $this->loadFacade($alias);

            return true;
        }

        if (isset($this->aliases[$alias])) {
            return class_alias($this->aliases[$alias], $alias);
        }
    }

可以看到

        if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) {
            $this->loadFacade($alias);

            return true;
        }

实时Facade 判断alias是否在static::$facadeNamespace命名空间下,如果是那么就调用loadFacade方法实时加载Facade

    /**
     * Load a real-time facade for the given alias.
     *
     * @param  string  $alias
     * @return bool
     */
    protected function loadFacade($alias)
    {
        tap($this->ensureFacadeExists($alias), function ($path) {
            require $path;
        });
    }

    /**
     * Ensure that the given alias has an existing real-time facade class.
     *
     * @param  string  $alias
     * @return string
     */
    protected function ensureFacadeExists($alias)
    {
        if (file_exists($path = storage_path('framework/cache/facade-'.sha1($alias).'.php'))) {
            return $path;
        }

        file_put_contents($path, $this->formatFacadeStub(
            $alias, file_get_contents(__DIR__.'/stubs/facade.stub')
        ));

        return $path;
    }

            /**
     * Format the facade stub with the proper namespace and class.
     *
     * @param  string  $alias
     * @param  string  $stub
     * @return string
     */
    protected function formatFacadeStub($alias, $stub)
    {
        $replacements = [
            str_replace('/', '\\', dirname(str_replace('\\', '/', $alias))),
            class_basename($alias),
            substr($alias, strlen(static::$facadeNamespace)),
        ];

        return str_replace(
            ['DummyNamespace', 'DummyClass', 'DummyTarget'], $replacements, $stub
        );
    }

loadFacade方法中,通过ensureFacadeExists去查找动态生成的Facade缓存文件,如果存在则返回路径

例如本例中Facades\App\Milkmeowo,sha1得到下面hash

>>> sha1('Facades\App\Milkmeowo')
=> "c84beaca684289727906d76c85df52fd1de7cdbe"

实时facade会在framework/cache目录中生成了facade-c84beaca684289727906d76c85df52fd1de7cdbe.php文件

<?php

namespace Facades\App;

use Illuminate\Support\Facades\Facade;

/**
 * @see \App\Milkmeowo
 */
class Milkmeowo extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'App\Milkmeowo';
    }
}

可以看到这个文件是标准的Facede文件

然后通过loadFacade require这个文件

        tap($this->ensureFacadeExists($alias), function ($path) {
            require $path;
        });

以上就是实时Facade的实现过程,实际上就是一个动态去生成Facade模版的方法


总结

  • 要想实时Facade,只需要在你的类的命名空间前面加上Facades\就好了

use Facades\你的类的命名空间

  • 使用时会生成cache文件

  • 千万不要觉得方便了滥用,你试想一下你维护或者debug的时候

方便了超多有木有!

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由 Summer 于 7年前 加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 13

@Summer 专栏的markdown语法支持有问题?不能使用#进行标题标记?没法渲染出来呀...

7年前 评论
Summer

@milkmeowo 单个 # 的话,直接对应于 H1。

一般情况下一个页面只有一个 H1 哦,那就是这篇文章的标题。

H2 是才是用来做段落区分。

推荐看下这个:http://baike.baidu.com/view/5362315.htm

另外,很棒的文章分享,今早上还在想什么时候能出 5.4 :smile: :+1:

7年前 评论

5.4会是LTS吗?:flushed:

7年前 评论
leo

没想到有什么具体使用的场景,解决什么问题

7年前 评论

@leo 我暂时想到的是解决依赖问题,方便使用,因为facade其实还是通过容器make出来,有时候想单纯使用某个方法,但又不想去解决依赖问题,可能就用得上了。

说实在我也没对应的上实际业务中有什么场景适合。不知道各位大大有什么见解

7年前 评论

@milkmeowo 基本同意你的看法。我对于 Facade 的理解比较粗浅,就是当它为传统的静态工具类使用,以便于代码的重用。

7年前 评论
leo

@milkmeowo 感觉鸡肋,如果想要make一个类直接app(CLASS::class)即可,比这个facade快多了

7年前 评论

@leo 我也没找到实际场合,感觉我们都没法理解作者的意图

file

刚翻了一下,找到这句,但我表示理解不能,求解

7年前 评论

@milkmeowo 我的理解是 Taylor 可能想 new App\User(new someDependency())User又依赖于某些类时,想要更简单的语法,直接new User。没有实时 Facade 时的做法可能是把 User 先绑定到容器里面,然后再从容器解析出来自动 resolve 依赖。有实时 Facade 后就能自动为 User 生成 Facade,并从容器中解决依赖。方便很多。

7年前 评论

@leo 我也感觉这种方法很比实时 Facade 来的直接

7年前 评论

看起来尽量少用这个功能啊....影响OO设计

7年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!