Maison >développement back-end >tutoriel php >Analyse du système d'apparence de façade de Laravel

Analyse du système d'apparence de façade de Laravel

不言
不言original
2018-07-06 17:13:261530parcourir

Cet article présente principalement l'analyse du système d'apparence de Laravel. Il a une certaine valeur de référence. Maintenant, je le partage avec vous. Les amis dans le besoin peuvent s'y référer

Aujourd'hui, nous allons découvrir l'architecture de base de Laravel. Un autre thème "Façade".

Cet article partira des aspects suivants pour expliquer de manière exhaustive le principe de fonctionnement de Façade dans Laravel Afin de faciliter la compréhension de toutes les Façade ultérieures. traduit par "apparence" :

  • Une brève introduction au modèle de conception "apparence"

  • Le principe de chargement de "l'apparence" de Laravel ;

  • Utilisation basique de "l'apparence" de Laravel.

Qu'est-ce que le modèle de conception « Apparence » ?

Définition du modèle d'apparence

Fournit une entrée unifiée pour un ensemble d'interfaces dans un sous-système. Le modèle de façade définit une interface de haut niveau qui rend ce sous-système plus facile à utiliser.

Le modèle d'apparence est un modèle de conception structurelle très fréquemment utilisé. Il simplifie l'interaction entre le client et le sous-système en introduisant un rôle d'apparence
Fournit un appel de sous-système complexe. L'entrée unifiée réduit le couplage entre les. Le sous-système et le client, et l'appel client est très pratique. - Design Pattern Java Edition

Core consiste à introduire un lien entre le client (utilisateur) et le sous-système (interface ou service) " Caractère "Apparence".

Transformez le couplage direct entre les utilisateurs et les sous-systèmes en une interface unifiée fournie par la classe « apparence » pour les utilisateurs afin de réduire le couplage entre le client et le sous-système.

Diagramme de structure :

Analyse du système dapparence de façade de Laravel

À propos du "Mode d'apparence", vous pouvez lire Design Pattern Java Edition - Mode d'apparence

Composant d'apparence Laravel

Le composant "apparence" dans Laravel est en fait un "proxy statique" de la classe sous-jacente dans le conteneur de services. Il utilise les "Contrats" définis dans le noyau de Laravel (également
appelés services, contrats dans Laravel. ou ce que nous appelons habituellement "Interface"), qui est encapsulé dans chaque service "d'apparence" de manière statique et appelable pour que nous puissions l'utiliser.

Principe de chargement de l'apparence

Avant d'expliquer comment utiliser les composants d'apparence, nous analysons tout d'abord en profondeur comment le composant "apparence" est chargé dans le projet par Laravel. Cette étape est
le pré-requis pour bien utiliser le composant « apparence ».

Configuration des composants d'apparence

Les données de configuration de tous les composants d'apparence intégrés sont définies dans le fichier config/app.php comme les autres services Laravel. Jetons un coup d'œil aux données de configuration du nœud alias :

    ...
    'aliases' => [

        'App' => Illuminate\Support\Facades\App::class,
        'Artisan' => Illuminate\Support\Facades\Artisan::class,
        ...
    ],
    ...

Le format de définition de la configuration d'apparence suit le format de données de "alias":"classe d'apparence". Lorsqu'une requête HTTP est reçue, ces composants « look and feel » sont chargés dans le service pendant la phase de traitement de la requête.

Ensuite, nous procéderons à une analyse approfondie du processus de chargement du service d'apparence.

Chargement du service d'apparence

Le chargement du service "apparence" est complété par le programme de démarrage IlluminateFoundationBootstrapRegisterFacades::class défini dans le IlluminateFoundationHttpKernel noyau.

Bootstrap démarre le service d'apparence

Si vous avez lu mon autre article sur l'analyse approfondie des principes de mise en œuvre du fournisseur de services Laravel, vous ne devriez pas être trop familier avec le bootstrap.

Le bootstrap terminera le démarrage du démarrage bootstrap() après avoir traité la requête HTTP. Nous devons donc ici approfondir la classe RegisterFacades pour comprendre un traitement plus détaillé.

<?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();
    }
}

Le chargement du service d'apparence a complété le composant AliasLoader :

  • Tout d'abord, ce sera à partir du fichier de configuration config/ app.php Lisez toutes les configurations du service "apparence" alias

  • puis lisez le service d'alias à partir du fichier manifeste $app-> ;make (PackageManifest::class)->aliases();

  • Fusionnez les deux tableaux de configuration et injectez-les dans le AliasLoader pour terminer le inscription (s'inscrire) .

Enregistrer le service d'apparence

Enfin, voyons comment le chargeur AliasLoader charge tous les services « d'apparence » dans le système.

<?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);
        }
    }
}

Remarque Voici les points de connaissances dans AliasLoader->register() compléter "l'enregistrement du service extérieur" implique l'application de deux connaissances PHP. :

  • Utilisation de la méthode magique intégrée à PHP __autoload;

  • Comment créer des alias pour les classes en PHP.

➤ 1. Introduction dynamique du service d'apparence

Nous savons que la fonction de la méthode magique __autoload est d'essayer de charger des classes non définies, donc que lorsque nous utilisons une classe non introduite, cette classe sera automatiquement introduite pour nous.

更优的解决方案是通过 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 别名服务」也就不这么神秘了。

Ce qui précède représente l'intégralité du contenu de cet article. J'espère qu'il sera utile à l'étude de chacun. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois !

Recommandations associées :

Interprétation du fournisseur de services Laravel (ServiceProvider)

Façades d'interprétation de Laravel Core

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn