>백엔드 개발 >PHP 튜토리얼 >Laravel의 Facade 외관 시스템 분석

Laravel의 Facade 외관 시스템 분석

不言
不言원래의
2018-07-06 17:13:261527검색

이 글은 주로 Laravel의 Facade 외관 시스템에 대한 분석을 소개합니다. 이제 이를 여러분과 공유합니다. 도움이 필요한 친구들이 참고할 수 있습니다.

오늘은 Laravel의 핵심 아키텍처인 "Facade"(Appearance)에 대한 또 다른 주제를 공부하겠습니다. )".

이 기사는 Laravel에서 Facade의 작동 원리를 포괄적으로 설명하기 위해 다음 측면에서 시작됩니다. 후속 작업에서 "외관"으로 번역되는 모든 Facade에 대한 이해를 돕기 위해:

  • A "모양" 디자인 패턴에 대한 간략한 소개

  • Laravel "모양" 로딩 원리

  • Laravel "모양"의 기본 사용.

"외관" 디자인 패턴이란 무엇입니까?

외관 패턴 정의

는 하위 시스템의 인터페이스 집합에 대한 통합 입구를 제공합니다. 파사드 패턴은 이 하위 시스템을 더 쉽게 사용할 수 있도록 하는 상위 수준 인터페이스를 정의합니다.

외형 패턴은 매우 자주 사용되는 구조적 디자인 패턴입니다. 이는 외관 역할을 도입하여 클라이언트와 하위 시스템 간의 상호 작용을 단순화하고,
복잡한 하위 시스템 호출에 대한 통합 입구를 제공하며 하위 시스템 간의 결합 비용을 줄입니다. 시스템과 클라이언트가 매우 좋고 클라이언트 통화가 매우 편리합니다. - Design Pattern Java Edition

의 핵심은 클라이언트(사용자)하위 시스템(인터페이스 또는 서비스) 사이에 "외관" 역할을 도입하는 것입니다.

사용자와 하위 시스템 간의 직접적인 결합을 사용자를 위한 "외관" 클래스에서 제공하는 통합 인터페이스로 변환하여 클라이언트와 하위 시스템 간의 결합을 줄입니다.

구조 다이어그램:

Laravel의 Facade 외관 시스템 분석

"Appearance Mode"에 대해서는 Design Pattern Java Edition - Appearance Mode를 읽을 수 있습니다.

Laravel Appearance Component

Laravel의 "Appearance" 컴포넌트는 실제로 기본 클래스의 "정적 프록시"입니다. Laravel 커널에 정의된 "계약"(서비스, 계약 또는 일반적으로 Laravel에서 인터페이스라고 부르는 것이라고도 함)을 우리가 사용할 수 있도록 정적이고 호출 가능한 방식으로 다양한 "외관" 서비스로 캡슐화하는 서비스 컨테이너 " "에 있습니다.

Appearance 로딩 원리

Appearance 컴포넌트 사용 방법을 설명하기 전에 먼저 Laravel이 "appearance" 컴포넌트를 프로젝트에 로드하는 방법을 심층적으로 분석합니다. 이 단계는 "외관" 구성 요소를 효과적으로 활용하기 위한 전제 조건입니다.


Appearance 컴포넌트 구성

모든 내장된 Appearance 컴포넌트의 구성 데이터는 다른 Laravel 서비스와 마찬가지로

config/app.php

파일에 정의되어 있습니다. aliases 노드의 구성 데이터를 살펴보겠습니다.

    ...
    'aliases' => [

        'App' => Illuminate\Support\Facades\App::class,
        'Artisan' => Illuminate\Support\Facades\Artisan::class,
        ...
    ],
    ...
외관 구성 정의 형식은

"Alias":"Appearance Class"

의 데이터 형식을 따릅니다. HTTP 요청이 수신되면 이러한 "모양과 느낌" 구성 요소가 요청 처리 단계에서 서비스에 로드됩니다. 다음으로는 출연 서비스 로딩 과정에 대해 심층 분석을 진행해보겠습니다.

모양 서비스 로드

"모양" 서비스 로드는

IlluminateFoundationBootstrapRegisterFacades::class

시작 프로그램에 의해 IlluminateFoundationHttpKernel 커널에 정의되어 완료됩니다. 부트스트랩 시작 출현 서비스

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

구성 요소 완성:

    먼저 모든 "외관" 서비스 구성
  • aliases

    을 구성 파일 config/app.php에서 읽은 다음

  • ; from 매니페스트 파일에서 별칭 서비스 읽기
  • $app->make(PackageManifest::class)->aliases()

    ;

  • 두 구성 배열을 결합하여
  • AliasLoader

    완료 register에 삽입합니다( 등록) .

  • 외모 서비스 등록

마지막으로

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()에서 "외국인 서비스 등록"을 완료하려면 다음 두 가지 PHP 지식을 적용해야 합니다.

    PHP에 내장된 매직 메소드 사용
  • __autoload

    ;

  • PHP 클래스 별칭을 만드는 방법.
  • ➤ 1. Appearance 서비스의 동적 도입

우리는

__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 중국어 홈페이지를 참고해주세요!

관련 권장 사항:

Laravel 서비스 공급자(ServiceProvider) 해석

Laravel 핵심 해석 Facades

위 내용은 Laravel의 Facade 외관 시스템 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.