【Laravel-海贼王系列】第十二章,Facade 模式解析

栏目: PHP · 发布时间: 6年前

内容简介:我们经常这样使用一些类先看对应的所以调用的时候实际容器会去解析

我们经常这样使用一些类

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Log;

class IndexController extends Controller
{
    public function index()
    {
        Log::info('hahaha~');
    }
}
复制代码

注册别名

先看对应的 Log 类在框架中注册的部分,在 app.php 文件中的别名数组

'aliases' => [
        'App' => Illuminate\Support\Facades\App::class,
        'Artisan' => Illuminate\Support\Facades\Artisan::class,
        'Auth' => Illuminate\Support\Facades\Auth::class,
        'Blade' => Illuminate\Support\Facades\Blade::class,
        'Broadcast' => Illuminate\Support\Facades\Broadcast::class,
        'Bus' => Illuminate\Support\Facades\Bus::class,
        'Cache' => Illuminate\Support\Facades\Cache::class,
        'Config' => Illuminate\Support\Facades\Config::class,
        'Cookie' => Illuminate\Support\Facades\Cookie::class,
        'Crypt' => Illuminate\Support\Facades\Crypt::class,
        'DB' => Illuminate\Support\Facades\DB::class,
        'Eloquent' => Illuminate\Database\Eloquent\Model::class,
        'Event' => Illuminate\Support\Facades\Event::class,
        'File' => Illuminate\Support\Facades\File::class,
        'Gate' => Illuminate\Support\Facades\Gate::class,
        'Hash' => Illuminate\Support\Facades\Hash::class,
        'Lang' => Illuminate\Support\Facades\Lang::class,
        'Log' => Illuminate\Support\Facades\Log::class,
        'Mail' => Illuminate\Support\Facades\Mail::class,
        'Notification' => Illuminate\Support\Facades\Notification::class,
        'Password' => Illuminate\Support\Facades\Password::class,
        'Queue' => Illuminate\Support\Facades\Queue::class,
        'Redirect' => Illuminate\Support\Facades\Redirect::class,
        'Redis' => Illuminate\Support\Facades\Redis::class,
        'Request' => Illuminate\Support\Facades\Request::class,
        'Response' => Illuminate\Support\Facades\Response::class,
        'Route' => Illuminate\Support\Facades\Route::class,
        'Schema' => Illuminate\Support\Facades\Schema::class,
        'Session' => Illuminate\Support\Facades\Session::class,
        'Storage' => Illuminate\Support\Facades\Storage::class,
        'URL' => Illuminate\Support\Facades\URL::class,
        'Validator' => Illuminate\Support\Facades\Validator::class,
        'View' => Illuminate\Support\Facades\View::class,
    ],
复制代码

所以调用的时候实际容器会去解析

lluminate\Support\Facades\Log::class 这个类

我们来看这个类

<?php

namespace Illuminate\Support\Facades;

/**
 * @method static void emergency(string $message, array $context = [])
 * @method static void alert(string $message, array $context = [])
 * @method static void critical(string $message, array $context = [])
 * @method static void error(string $message, array $context = [])
 * @method static void warning(string $message, array $context = [])
 * @method static void notice(string $message, array $context = [])
 * @method static void info(string $message, array $context = [])
 * @method static void debug(string $message, array $context = [])
 * @method static void log($level, string $message, array $context = [])
 * @method static mixed channel(string $channel = null)
 * @method static \Psr\Log\LoggerInterface stack(array $channels, string $channel = null)
 *
 * @see \Illuminate\Log\Logger
 */
class Log extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'log';
    }
}

复制代码

调用分析

最开始我们的调用方法是 Log::info 这里要追踪到父类 Facade 里面

方便阅读精简一些方法

<?php

namespace Illuminate\Support\Facades;

use Closure;
use Mockery;
use RuntimeException;
use Mockery\MockInterface;

abstract class Facade
{
    protected static $app;

    protected static $resolvedInstance;
    
    ...
    
    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);
    }
}

复制代码

这里的调用逻辑是通过 __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);
    }
复制代码

这里也就解释了为什么我们能以静态调用的方式调用对应的方法。

开始分析

$instance = static::getFacadeRoot();
复制代码

看样子是要解析一个实例出来,这里要注意的是

static 调用会指向调用的类,其次才是继承的类。

public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }
复制代码

所以这里的 static::getFacadeAccessor() 实际指向 Log

protected static function getFacadeAccessor()
    {
        return 'log';
    }
复制代码

原来是获取一个别名,那么推测后面就是通过别名从容器拿对象了

继续看看如何拿对象

protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }

        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }

        return static::$resolvedInstance[$name] = static::$app[$name];
    }
复制代码

看到这里就知道最主要是 static::$app[$name] 来获取对象

拓展- $app 来自哪里?

但是疑问来了, $app 如果是 Application 对象的话又是在什么地方赋值?

回到内核 Kernel 来看看解答,如何启动请回顾 【Laravel-海贼王系列】第四章,Kernel 类解析handle 方法。

protected $bootstrappers = [
        ...
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        ...
    ];
复制代码

直接上代码

<?php

namespace Illuminate\Foundation\Bootstrap;

use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\Facades\Facade;
use Illuminate\Foundation\PackageManifest;
use Illuminate\Contracts\Foundation\Application;

class RegisterFacades
{
    public function bootstrap(Application $app)
    {
        Facade::clearResolvedInstances();

        Facade::setFacadeApplication($app);

        AliasLoader::getInstance(array_merge(
            $app->make('config')->get('app.aliases', []),
            $app->make(PackageManifest::class)->aliases()
        ))->register();
    }
}
复制代码

真香~, Facade::setFacadeApplication($app); 在这儿传入的 Application

这里又涉及到了一些问题,那我怎么知道 $app['log'] 里面的对象是谁呢?

拓展-容器的绑定和解析!

哈哈这里去看 【Laravel-海贼王系列】第三章,Container 类解析 ,里面的 bind() 方法就是在容器中绑定抽象和实现的功能,这里也是容器的知识。 这里的 logApplication 在启动的时候提前以及帮我们搞定了~

位于 Application__construct()

public function __construct($basePath = null)
    {
        ...
        $this->registerBaseServiceProviders();
        ...
    }
复制代码
protected function registerBaseServiceProviders()
    {
        ...
        $this->register(new LogServiceProvider($this));
        ...
    }
复制代码
<?php

namespace Illuminate\Log;

use Illuminate\Support\ServiceProvider;

class LogServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton('log', function () {
            return new LogManager($this->app);
        });
    }
}
复制代码

最后就看到了真正的对象 return new LogManager($this->app);


以上所述就是小编给大家介绍的《【Laravel-海贼王系列】第十二章,Facade 模式解析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Spark SQL内核剖析

Spark SQL内核剖析

朱锋、张韶全、黄明 / 电子工业出版社 / 2018-8 / 69.00元

Spark SQL 是 Spark 技术体系中较有影响力的应用(Killer application),也是 SQL-on-Hadoop 解决方案 中举足轻重的产品。《Spark SQL内核剖析》由 11 章构成,从源码层面深入介绍 Spark SQL 内部实现机制,以及在实际业务场 景中的开发实践,其中包括 SQL 编译实现、逻辑计划的生成与优化、物理计划的生成与优化、Aggregation 算......一起来看看 《Spark SQL内核剖析》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码