Автоматические Фасады в Laravel

Автоматические Фасады в Ларавел

Интересная фича, возможность использовать Фасады (Facades) на лету, появилась в Laravel 5.4. Я не буду объяснять, что такое Фасады — все подробно описано в официальной документации. Предполагается, что вы с ними знакомы.

Как создать свой Фасад

В качестве примера я позаимствую класс, используемый Taylor Otwell:

namespace App; 

class Zonda
{ 
    public function zurf() 
    {
        return 'Zurfing'; 
    } 
}

Учитывая этот класс, мы должны определить собственный Фасад следующим образом:

namespace App\Facades; 

use Illuminate\Support\Facades\Facade; 

class FacadeName extends Facade 
{ 
    protected static function getFacadeAccessor() 
    { 
        return App\Zonda::class;
    } 
}

Последний шаг — регистрируем Фасад в config/app.php:

'aliases' => [
    // ...
    'Zonda' => App\Facades\FacadeName::class
]

Идея автоматических фасадов заключается в автоматизации этого потока для определения встроенных фасадов:

use Facades\{ 
    App\Zonda 
}; 

Route::get('/', function () { 
    return Zonda::zurf(); 
});

Используя общее пространство имен Facades\.

За кулисами

Каждый раз, когда мы используем групповой импорт, который начинается с пространства имен Facades, новый фасад создается на лету. Это также работает и с однострочным импортом.

Это поведение обрабатывает Illuminate\Foundation\AliasLoader. Он регистрирует пользовательскую функцию автозагрузки при старте фреймворка.

/**
 * Register the loader on the auto-loader stack.
 *
 * @return void
 */
public function register()
{
    if (! $this->registered) {
        $this->prependToLoaderStack();

        $this->registered = true;
    }
}

/**
 * Prepend the load method to the auto-loader stack.
 *
 * @return void
 */
protected function prependToLoaderStack()
{
    spl_autoload_register([$this, 'load'], true, true);
}

Когда фреймворк загружается, он вызывает метод register и добавляет в стек загрузчика метод load.

/**
 * Load a class alias if it is registered.
 *
 * @param  string  $alias
 * @return bool|null
 */
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);
     }
}

Если $alias начинается с $facadeNamespace (по умолчанию это Facade), то он загружает пользовательский фасад на лету, используя метод loadFacade.

/**
 * Load a real-time facade for the given alias.
 *
 * @param  string  $alias
 * @return bool
 */
protected function loadFacade($alias)
{
    tap($this->ensureFacadeExists($alias), function ($path) {
        require $path;
    });
}

После того, как Фасад гарантировано существует, ему требуются сгенерированные в реальном времени фасады в пространстве имен.

Метод sureFacadeExists делает волшебство реальным.

/**
 * Ensure that the given alias has an existing real-time facade class.
 *
 * @param  string  $class
 * @return string
 */
protected function ensureFacadeExists($alias)
{
    if (file_exists($path = base_path('bootstrap/cache/facade-'.sha1($alias).'.php'))) {
        return $path;
    }

    file_put_contents($path, $this->formatFacadeStub(
        $alias, file_get_contents(__DIR__.'/stubs/facade.stub')
    ));

    return $path;
}

Первым делом проверяется, существует ли уже Фасад. Как вы видите, кэшированные фасады генерируются с использованием хеш-функции sha1 на псевдониме. Если фасад кэширован, то он возвращает путь к файлу.

В противном случае Фасад генерирует на лету, используя эту заглушку.

namespace DummyNamespace;

use Illuminate\Support\Facades\Facade;

/**
 * @see \DummyTarget
 */
class DummyClass extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'DummyTarget';
    }
}

Как вы видите, это похоже на то, что мы использовали раньше, создавая его вручную. Я показал вам, что именно делает formatFacadeStub.

Остальная часть метода load работает также, как и раньше, поэтому остальное можете посмотреть сами :D

Фасады
То, что было добавлено для внедрения этой фичи

Предлагаю начать использовать автоматические фасады, когда это возможно, их влияние на производительность незначительно.

О том, когда обычно используются фасады, вы можете прочесть в официальной документации.

Автор: Nicola Malizia
Перевод: Алексей Широков

Наш Телеграм-канал — следите за новостями о Laravel.