Интересная фича, возможность использовать Фасады (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
Перевод: Demiurge Ash