ホームページ  >  記事  >  バックエンド開発  >  Laravelのファサード外観システムの分析

Laravelのファサード外観システムの分析

不言
不言オリジナル
2018-07-06 17:13:261482ブラウズ

この記事では主に Laravel のファサード外観システムの分析を紹介します。これには一定の参考値があります。今、あなたに共有します。困っている友達は参考にしてください。

今日は Laravel のコア アーキテクチャについて学びます. もうひとつのテーマ「ファサード」。

この記事では、Laravel の Facade の動作原理を包括的に説明するために、次の側面から開始します。

    「外観」デザイン パターンの簡単な紹介;
  • Laravel の読み込み原理」外観";
  • Laravelの基本的な使い方「外観」。
  • 「外観」デザイン パターンとは

外観パターン定義

サブシステム内の一連のインターフェイスに統一されたエントリを提供します。ファサード パターンは、このサブシステムを使いやすくする高レベルのインターフェイスを定義します。

外観パターンは、非常に頻繁に使用される構造設計パターンです。外観の役割を導入することで、クライアントとサブシステム間の対話を簡素化します。

複雑なサブシステム呼び出しを提供します。統一された入口により、クライアント間の結合が軽減されます。サブシステムとクライアント、およびクライアント呼び出しは非常に便利です。 - デザインパターン Java Edition

Core

は、クライアント (ユーザー)サブシステム (インターフェースまたはサービス) 「外観」の間のリンクを導入することです。キャラクター。 ユーザーとサブシステム間の直接的な結合を、「外観」クラスによってユーザーに提供される統一インターフェイスに変換して、クライアントとサブシステム間の結合を軽減します。

構造図:

## 「外観モード」については、デザインパターン Java Edition - 外観モードLaravelのファサード外観システムの分析

Laravel 外観コンポーネント

を参照してください。

Laravel の「外観」コンポーネントは、実際にはサービス コンテナ内の基礎となるクラスの「静的プロキシ」であり、Laravel コアで定義された「コントラクト」(

サービス、コントラクト、または通常のものとも呼ばれます) を使用します。これは、静的かつ呼び出し可能な方法で各「外観」サービスにカプセル化されており、使用することができます。

外観読み込みの原則

外観コンポーネントの使用方法を説明する前に、まず「外観」コンポーネントが Laravel によってプロジェクトにどのように読み込まれるかを詳しく分析します。このステップは、「外観」コンポーネントを有効に活用するための前提条件です。

外観コンポーネントの設定


すべての組み込みの外観コンポーネントの設定データは、他の Laravel サービスと同様に、

config/app.php

ファイルで定義されます。

aliases

ノードの構成データを見てみましょう:

    ...
    'aliases' => [

        'App' => Illuminate\Support\Facades\App::class,
        'Artisan' => Illuminate\Support\Facades\Artisan::class,
        ...
    ],
    ...
外観構成定義の形式は、"Alias":"Appearance Class" のデータ形式に従います。 。 HTTP リクエストが受信されると、これらの「ルック アンド フィール」コンポーネントがリクエスト処理フェーズ中にサービスにロードされます。

次に、外観サービスの読み込みプロセスを詳細に分析します。 外観サービスの読み込み

「外観」サービスの読み込み作業は、

Illuminate\Foundation\Http\Kernel

カーネル

\Illuminate\Foundation\ で定義されています。 Bootstrap \RegisterFacades::class

ランチャーが完了します。 ブート スタート アピアランス サービスLaravel サービス プロバイダーの実装原則の詳細な分析に関する私の別の記事を読んでいるなら、ブートストラップ プログラムについてあまり詳しくないはずです。

ブートストラップは、HTTP リクエストを処理した後、ブートの起動

bootstrap()

を完了します。したがって、ここでは、より詳細な処理を理解するために、

RegisterFacades

クラスを深く掘り下げる必要があります。

<?php

namespace Illuminate\Foundation\Bootstrap;

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

/**
 * @link https://github.com/laravel/framework/blob/56a58e0fa3d845bb992d7c64ac9bb6d0c24b745a/src/Illuminate/Foundation/Bootstrap/RegisterFacades.php
 */
class RegisterFacades
{
    /**
     * Bootstrap the given application. 引导启动服务
     */
    public function bootstrap(Application $app)
    {
        // 清除已解析的「外观」服务实例
        Facade::clearResolvedInstances();

        // 将 Laravel 服务容器注入到「外观」服务
        Facade::setFacadeApplication($app);

        // 加载所有外观服务
        AliasLoader::getInstance(array_merge(
            $app->make(&#39;config&#39;)->get(&#39;app.aliases&#39;, []),
            $app->make(PackageManifest::class)->aliases()
        ))->register();
    }
}
外観サービスのロードには AliasLoader コンポーネントの補完があります:

まず、構成ファイル

config/app からロードされます。 .php
    すべての「外観」サービス構成を読み取ります
  • aliases

    ;次に、マニフェスト ファイルからエイリアス サービスを読み取ります

    $app->make (PackageManifest ::class)->aliases()
  • ;
  • 2 つの構成配列を結合し、

    AliasLoader
  • に挿入します。
  • 登録を完了します (登録する) ###。

    外観サービスの登録

    最後に、
  • AliasLoader
ローダーがすべての「外観」サービスをシステムにロードする方法を見てみましょう。

<?php

namespace Illuminate\Foundation;

/**
 * @link https://github.com/laravel/framework/blob/56a58e0fa3d845bb992d7c64ac9bb6d0c24b745a/src/Illuminate/Foundation/AliasLoader.php
 */
class AliasLoader
{
    /**
     * Get or create the singleton alias loader instance. 获取或创建「别名加载器」单例实例。
     */
    public static function getInstance(array $aliases = [])
    {
        if (is_null(static::$instance)) {
            return static::$instance = new static($aliases);
        }

        $aliases = array_merge(static::$instance->getAliases(), $aliases);

        static::$instance->setAliases($aliases);

        return static::$instance;
    }

    /**
     * Set the registered aliases. 设置需注册别名数据。
     */
    public function setAliases(array $aliases)
    {
        $this->aliases = $aliases;
    }

    /**
     * Register the loader on the auto-loader stack. 将加载器注册到自动加载中。
     */
    public function register()
    {
        if (! $this->registered) {
            $this->prependToLoaderStack();

            $this->registered = true;
        }
    }

    /**
     * Prepend the load method to the auto-loader stack. 设置自动加载方法。
     */
    protected function prependToLoaderStack()
    {
        // 将 AliasLoader 的 load 方法作为 __autoload 的实现
        spl_autoload_register([$this, &#39;load&#39;], true, true);
    }

    /**
     * Load a class alias if it is registered.从注册过的服务中加载这个「外观」服务。
     */
    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);
        }
    }
}

Note ここは知識ポイントです。AliasLoader->register()

で「外部サービスの登録」を完了するには、次の 2 つの PHP 知識を適用する必要があります:

PHP 組み込みマジック メソッドの使用法 __autoload

;
  • PHP でクラスのエイリアスを作成する方法。

  • ➤ 1. 外観サービスの動的導入

  • __autoload
マジック メソッドは、未定義のクラスをロードしようとすることを知っています。導入されていないクラスを使用すると、このクラスが自動的に導入されます。

更优的解决方案是通过 spl_autoload_register 函数,将自定义的类加载程序作为 __autoload 的实现,以替代默认 __autoload() 模式函数或方法的行为。

所有 prependToLoaderStack() 方法:

    /**
     * Prepend the load method to the auto-loader stack. 设置自动加载方法。
     */
    protected function prependToLoaderStack()
    {
        // 将 AliasLoader 的 load 方法作为 __autoload 的实现
        spl_autoload_register([$this, 'load'], true, true);
    }

就是去完成这样的作用,将 AliasLoader->load() 方法作为自动加载程序的实现,在使用「外观」服务时动态引入这个类。

➤ 2. 支持外观服务别名

我们已经了解到当「外观」服务被使用时,由 AliasLoader->load() 去自动加载这个类。

与此同时,load 方法通过 class_alias($original, $alias) 函数完成别名注册。

这样,当我们使用 App 类时实际上就是在使用 Illuminate\Support\Facades\App 类。

很完美么,我们的「狗蛋」终于与「世界上最好的语言」画上了等号。你就是我,我就是你。

到这里其实已经完成了「外观」服务工作原理分析工作的 70%

探秘 Facade

最后我们将揭开 Facade 的神秘面纱,研究一下 Laravel 是如何实现 Facade 设计模式的。

我们拿 IlluminateSupportFacadesApp 外观服务开刀,去解开类似 App::make() 静态方法使用的奥秘。

深入 FacadesApp

<?php

namespace Illuminate\Support\Facades;

class App extends Facade
{
    /**
     * Get the registered name of the component.
     */
    protected static function getFacadeAccessor()
    {
        return &#39;app&#39;;
    }
}

我们看到它的实现内部仅仅定义了一个 getFacadeAccessor 方法,该方法的功能是获取已注册组件的名称 app;除此之外,一无所有。

看来在这里我们得不到什么有用的信息了。继续调查基类 IlluminateSupportFacadesFacade。如果你有去通便浏览全部的源码。

<?php

namespace Illuminate\Support\Facades;

use Mockery;
use RuntimeException;
use Mockery\MockInterface;

/**
 * @link https://github.com/laravel/framework/blob/5.6/src/Illuminate/Support/Facades/Facade.php
 */
abstract class Facade
{
    /**
     * Handle dynamic, static calls to the object.
     */
    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();

        if (! $instance) {
            throw new RuntimeException(&#39;A facade root has not been set.&#39;);
        }

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

你会发现这个 Facade 基类并没有定义类似 make 的方法,那么这里能够静态调用 App::make() 看来是需要从 __callStatic 着手才行。

不过在这里我们需要再次厘清一个事实:「外观」模式的功能是什么?

将使用者与子系统从直接耦合,转变成由「外观」类提供统一的接口给使用者使用,以降低客户端与子系统之间的耦合度。

这句话的意思就是我「外观」啥也不提供,就是一层对服务(或者说组件或接口)的封装,然后以统一的方式提供给你们外部调用。

好了现在我们来看看 Facade::__callStatic 是如何获取实际的服务并调用响应的方法的吧。

  • 首先,通过 getFacadeRoot 静态方法获取实际服务的实例对象;

  • 然后,调用实例对象的相关方法并返回处理结果。

<?php

namespace Illuminate\Support\Facades;

use Mockery;
use RuntimeException;
use Mockery\MockInterface;

abstract class Facade
{
    /**
     * Get the root object behind the facade. 从 facade 中解析出真实服务的对象
     */
    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }

    /**
     * Resolve the facade root instance from the container.me 从 Laravel 服务容器中解析出真实服务的对象
     */
    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];
    }
}

getFacadeRoot 解析对象的功能中我们可以看到:它会调用实现「外观」的 getFacadeAccessor 方法获取到组件(服务或者说接口)的名称;然后从 Laravel 服务容器 static::$app[$name](app 是在 RegisterFacades 中注册到「外观」中) 中解析出相关服务。

到这里,我们就将「外观」服务的基本工作原理给分析透彻了。

另外有关「外观」组件的一些细枝末节,如:

  • 在文档「Facades Vs. 辅助函数」一节提到的测试验证是如何实现的 Cache::shouldReceive('get')

  • 什么是「实时 Facades」。

还是需要你自行深入到 Facade 基类去一探究竟。

扫盲 ArrayAccess 接口

另外补充一个知识点就是关于 static::$app[$name] 这一句代码。你不经要问,这有啥好补充的呢,不就是一个简单获取数据么。

获取数据不假,简单也不假。

不过你仔细看一下,你会发现 static::$app 静态成员变量难道不是一个 \Illuminate\Contracts\Foundation\Application 实现实例么,怎么可以从对象中以数组的方式获取值呢?

这是因为我们的服务容器 Illuminate\Container\Container 实现了 ArrayAccess 接口。

该接口的功能是提供像访问数组一样访问对象的能力的接口,这样就可以像数组一样访问对象访问成员。

/**
 *@link https://github.com/laravel/framework/blob/5.6/src/Illuminate/Container/Container.php
 */
class Container implements ArrayAccess, ContainerContract
{
    /**
     * Get the value at a given offset. 获取一个偏移位置的值,实际上从容器中解析出服务。
     */
    public function offsetGet($key)
    {
        return $this->make($key);
    }
}

Laravel「外观」基本使用

外观服务的一个典型使用场景是在定义路由时使用 Route::get('/', ...)。这样一看似乎「Laravel 别名服务」也就不这么神秘了。

上記がこの記事の全内容です。皆様の学習に役立つことを願っています。その他の関連コンテンツについては、PHP 中国語 Web サイトに注目してください。

関連する推奨事項:

#Laravel サービス プロバイダー (ServiceProvider) の解釈

#Laravel コア解釈ファサード

以上がLaravelのファサード外観システムの分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。