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

Facade 優雅的設計模式

咱們常常這樣使用一些類php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Log;

class IndexController extends Controller
{
    public function index()
    {
        Log::info('hahaha~');
    }
}
複製代碼

註冊別名

先看對應的 Log 類在框架中註冊的部分,在 app.php 文件中的別名數組bootstrap

'aliases' => [
        ...
        'Log' => Illuminate\Support\Facades\Log::class,
        ...
    ],
複製代碼

因此調用的時候實際容器會去解析設計模式

lluminate\Support\Facades\Log::class 這個類數組

咱們來看這個類bash

<?php

namespace Illuminate\Support\Facades;

class Log extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'log';
    }
}

複製代碼

調用分析

最開始咱們的調用方法是 Log::info 這裏要追蹤到父類 Facade 裏面app

方便閱讀精簡一些方法框架

<?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 這個魔術方法來實現的ide

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);
    }
複製代碼

這裏也就解釋了爲何咱們能以靜態調用的方式調用對應的方法。post


開始分析ui

$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);

收尾

對象在手,就能夠任意的調用存在的方法。

return $instance->$method(...$args);

相關文章
相關標籤/搜索