新しくLaravelアプリケーションをインストールし、起動してウェルカムページが表示されました。他の皆さんと同じように、どのように表示されるか確認したいので、 web.php
ファイルを開くと次のコードが表示されます:
ウェルカムビューの取得方法は明らかですが、Laravelのルーターがどのように動作するのか興味があるので、コードを調べることにしました。最初の仮定は、 Route
静的メソッドを呼び出すクラス get()
しかし、クリックしても get()
そこには方法があります。それでは、どのような黒魔術が起こっているのでしょうか? これを解明してみましょう!
通常のファサード
簡潔にするために、PHPDoc のほとんどを削除し、型をインライン化したことに注意してください。「…」は、追加のコードを指します。
混乱を避けるために、IDE を開いてコードに沿って進むことを強くお勧めします。
私たちの例に従って、 Route
クラス。
ここには大したことはないけど、 getFacadeAccessor()
文字列を返すメソッド router
これを念頭に置いて、親クラスに移りましょう。
$method(...$args);
}
}
親クラスにはたくさんのメソッドがあり、 get()
方法があります。しかし、興味深い方法があります。 __callStatic()
方法です。 魔法 メソッドは、定義されていない静的メソッド、例えば get()
我々の場合、は呼ばれます。したがって、我々の呼び出しは __callStatic('get', ('/', Closure()))
呼び出し時に渡したものを表します Route::get()
、 ルート /
そして Closure()
ウェルカムビューを返します。
いつ __callStatic()
トリガーされると、まず変数を設定しようとする $instance
電話をかける getFacadeRoot()
、 $instance
呼び出しを転送する実際のクラスを保持します。詳しく見てみましょう。すぐに意味がわかります。
// Facade.php
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
おい、見てよ getFacadeAccessor()
子どもクラスから Route
は、文字列 router
。 これ router
文字列は次に渡されます resolveFacadeInstance()
は、それをクラスに解決しようとします。これは、「この文字列はどのクラスを表しているか」を示す一種のマッピングです。見てみましょう。
// Facade.php
protected static function resolveFacadeInstance($name)
{
if (isset(static::$resolvedInstance($name))) {
return static::$resolvedInstance($name);
}
if (static::$app) {
if (static::$cached) {
return static::$resolvedInstance($name) = static::$app($name);
}
return static::$app($name);
}
}
まず静的配列かどうかをチェックし、 $resolvedInstance
は、指定された値を持つ $name
(これもまた router
) 。一致するものが見つかった場合は、その値を返します。これは、パフォーマンスを少し最適化するための Laravel のキャッシュです。このキャッシュは、単一のリクエスト内で行われます。このメソッドが同じリクエスト内で同じ引数で複数回呼び出された場合は、キャッシュされた値が使用されます。これが最初の呼び出しであると仮定して、先に進みましょう。
次に、 $app
が設定され、 $app
アプリケーションコンテナのインスタンスである
// Facade.php
protected static \Illuminate\Contracts\Foundation\Application $app;
アプリケーション コンテナーがどのようなものか知りたい場合は、クラスが格納されているボックスと考えてください。クラスが必要な場合は、そのボックスに手を伸ばすだけです。このコンテナーは、時々ちょっとした魔法をかけてくれます。ボックスが空であっても、クラスを取ろうと手を伸ばすと、自動的に取得されます。これは別の記事で取り上げるトピックです。
さて、あなたは「 $app
設定しますか?そうしないと、 $instance
このアプリケーションコンテナは、アプリケーションのブートストラッププロセス中に設定されます。 \Illuminate\Foundation\Http\Kernel
クラス:
app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}
}
リクエストが届くと、それはルーターに送られます。その直前に、 bootstrap()
メソッドが呼び出され、 bootstrappers
アプリケーションを準備するための配列。 bootstrapWith()
方法 \Illuminate\Foundation\Application
クラスでは、これらのブートストラッパーを反復処理して、 bootstrap()
方法。
簡単にするために、 \Illuminate\Foundation\Bootstrap\RegisterFacades
、これには bootstrap()
呼び出されるメソッド bootstrapWith()
make('config')->get('app.aliases', ()),
$app->make(PackageManifest::class)->aliases()
))->register();
}
}
これで、アプリケーションコンテナを Facade
静的メソッドを使用するクラス setFacadeApplication().
// RegisterFacades.php
public static function setFacadeApplication($app)
{
static::$app = $app;
}
ほら、私たちは割り当てます $app
私たちがテストしているプロパティ resolveFacadeInstance()
これで質問の答えは出ました。続けましょう。
// Facade.php
protected static function resolveFacadeInstance($name)
{
if (isset(static::$resolvedInstance($name))) {
return static::$resolvedInstance($name);
}
if (static::$app) {
if (static::$cached) {
return static::$resolvedInstance($name) = static::$app($name);
}
return static::$app($name);
}
}
我々は確認した $app
アプリケーションのブートストラップ中に設定されます。次のステップは、解決されたインスタンスをキャッシュする必要があるかどうかを確認することです。 $cached
デフォルトではtrueです。最後に、アプリケーションコンテナからインスタンスを取得します。この場合、次のように尋ねます。 static::$app('router')
文字列にバインドされた任意のクラスを提供する router
。
さて、なぜアクセスするのか疑問に思うかもしれません $app
アプリケーションコンテナのインスタンスであるにもかかわらず、配列のように 物体そうですね、その通りです!しかし、アプリケーションコンテナはPHPインターフェースを実装しています。 ArrayAccess
配列のようなアクセスが可能になります。この事実を確認するために、以下を見てみましょう。
だから、 resolveFacadeInstance()
実際にバインドされたインスタンスを返します router
文字列、具体的には、 \Illuminate\Routing\Router
どうして分かったのか? Route
ファサード;多くの場合、PHPDoc @see
このファサードが何を隠しているか、より正確には、メソッド呼び出しがどのクラスにプロキシされるかを示唆します。
さて、私たちの __callStatic
方法。
$method(...$args);
}
}
我々は持っています $instance
、のオブジェクト \Illuminate\Routing\Router
クラス。設定されているかどうかをテストし (この場合は確認済み)、そのメソッドを直接呼び出します。つまり、次のようになります。
// Facade.php
return $instance->get('/', Closure());
そして今、あなたは get()
内に存在する \Illuminate\Routing\Router
クラス。
addRoute(('GET', 'HEAD'), $uri, $action);
}
}
これで終わりです。結局、難しくなかったですか? まとめると、ファサードはコンテナにバインドされた文字列を返します。たとえば、 hello-world
に縛られるかもしれない HelloWorld
クラス。ファサードで未定義のメソッドを静的に呼び出すと、 HelloWorldFacade
例えば、 __callStatic()
介入する。
登録された文字列を解決し、 getFacadeAccessor()
メソッドをコンテナ内にバインドされているものに渡し、取得したインスタンスへの呼び出しをプロキシします。 (new HelloWorld())->method()
それが本質です!まだピンと来ませんか?それではファサードを作成してみましょう!
ファサードを作ろう
次のようなクラスがあるとします。
目標は、 HelloWorld::greet()
これを行うには、クラスをアプリケーションコンテナにバインドします。まず、 AppServiceProvider
。
app->bind('hello-world', function ($app) {
return new HelloWorld;
});
}
// ...
}
今では、私たちがリクエストするたびに hello-world
アプリケーションコンテナ(または前述のボックス)から、 HelloWorld
残っているのは?文字列を返すファサードを作成するだけです hello-world
。
これで準備は完了です。 web.php.
私達はことを知っています greet()
存在しない HelloWorldFacade
ファサード、 __callStatic()
がトリガーされます。文字列で表されるクラスを取得します(hello-world
このバインディングは既にアプリケーションコンテナから作成されています。 AppServiceProvider
; インスタンスを提供するように指示しました HelloWorld
誰かがリクエストするたびに hello-world
したがって、 greet()
は、取得したインスタンスに対して操作を行います。 HelloWorld
。 以上です。
おめでとうございます!独自のファサードを作成しました。
Laravel リアルタイム ファサード
ファサードについてよく理解できたところで、もう1つ魔法のトリックを紹介します。 HelloWorld::greet()
ファサードを作成せずに、リアルタイム ファサードを使用します。
みてみましょう:
コントローラの名前空間にプレフィックスを付けることで Facades
、先ほどと同じ結果が得られます。しかし、 HelloWorld
コントローラには静的メソッドがありません greet()
!そして、 Facades\App\Http\Controllers\HelloWorld
どこから来たのか?これは魔法のように思えるかもしれないが、一度理解してしまえば、非常に簡単なことだ。
詳しく見てみましょう \Illuminate\Foundation\Bootstrap\RegisterFacades
先ほど確認したように、 $app:
make('config')->get('app.aliases', ()),
$app->make(PackageManifest::class)->aliases()
))->register(); // Interested here
}
}
最後に、 register()
メソッドが呼び出されます。中身を見てみましょう:
registered) {
$this->prependToLoaderStack();
$this->registered = true;
}
}
}
の $registered
変数は最初に false
したがって、 if
声明を出し、 prependToLoaderStack()
メソッドです。では、その実装を見てみましょう。
// AliasLoader.php
protected function prependToLoaderStack(): void
{
spl_autoload_register(($this, 'load'), true, true);
}
ここで魔法が起こります!Laravelは spl_autoload_register()
関数は、未定義のクラスにアクセスしようとしたときに呼び出される組み込みのPHP関数です。これは、そのような状況で実行するロジックを定義します。この場合、Laravelは load()
未定義の呼び出しに遭遇したときにメソッドを実行します。
さらに、 spl_autoload_register()
呼び出されるメソッドまたは関数に、未定義のクラスの名前が自動的に渡されます。
探検してみましょう load()
メソッド。それがキーになるはずです。
// AliasLoader.php
public function load($alias)
{
if (static::$facadeNamespace && str_starts_with($alias, static::$facadeNamespace)) {
$this->loadFacade($alias);
return true;
}
if (isset($this->aliases($alias))) {
return class_alias($this->aliases($alias), $alias);
}
}
確認します $facadeNamespace
が設定されており、どのクラスが合格しても、この場合は Facades\App\Http\Controllers\HelloWorld
設定されたものから始まる $facadeNamespace
ロジックは以下をチェックします $facadeNamespace
が設定されており、渡されたクラス(この場合は Facades\App\Http\Controllers\HelloWorld
(未定義)は、 $facadeNamespace.
// AliasLoader.php
protected static $facadeNamespace = 'Facades\\';
コントローラの名前空間に接頭辞を付けたので Facades
条件を満たすので、次に進みましょう。 loadFacade()
// AliasLoader.php
protected function loadFacade($alias)
{
require $this->ensureFacadeExists($alias);
}
ここで、メソッドは、 ensureFacadeExists()
したがって、次のステップでは、その実装について詳しく検討します。
// AliasLoader.php
protected function ensureFacadeExists($alias)
{
if (is_file($path = storage_path('framework/cache/facade-'.sha1($alias).'.php'))) {
return $path;
}
file_put_contents($path, $this->formatFacadeStub(
$alias, file_get_contents(__DIR__.'/stubs/facade.stub')
));
return $path;
}
まず、次の名前のファイルが framework/cache/facade-'.sha1($alias).'.php'
存在します。今回の場合、このファイルは存在しないため、次の手順が実行されます。 file_put_contents()
この関数はファイルを作成し、指定された場所に保存します。 $path
ファイルの内容は、 formatFacadeStub()
は、その名前から判断すると、スタブからファサードを作成します。 facade.stub
次のような結果が見つかります。
見覚えがありますか?これは基本的に手動で行ったことです。 formatFacadeStub()
ダミーコンテンツを未定義のクラスに置き換えます。 Facades\\
プレフィックス。この更新されたファサードは保存されます。その結果、 loadFacade()
ファイルが必要な場合、正しく実行され、最終的に次のファイルが必要になります。
そして、通常のフローでは、アプリケーションコンテナに文字列にバインドされたインスタンスを返すように要求します。 App\Http\Controllers\HelloWorld
不思議に思うかもしれませんが、この文字列は何もバインドされておらず、 AppServiceProvider
しかし、冒頭でアプリケーション コンテナーについて述べたことを覚えていますか?
ボックスが空であってもインスタンスを返しますただし、クラスにはコンストラクタがあってはなりません。そうでなければ、クラスはそれをどのように構築すればよいかわかりません。私たちの場合、 HelloWorld
クラスの構築には引数は必要ありません。そのため、コンテナはそれを解決して返し、すべての呼び出しはそれにプロキシされます。
リアルタイムファサードの要約: クラスにプレフィックスを付けました Facades
アプリケーションのブートストラップ中に、Laravelは spl_autoload_register()
未定義のクラスを呼び出すと発生します。これは最終的に load()
方法。内部 load()
、現在の未定義のクラスにプレフィックスが付いているかどうかを確認します。 Facades
一致するので、Laravel はそれをロードしようとします。
ファサードが存在しないため、スタブからファサードを作成し、ファイルを要求します。すると、出来上がりです。通常のファサードがありますが、これはオンザフライで作成されました。かなりクールですよね?
結論
ここまでお読みいただき、ありがとうございます。少し圧倒されるかもしれませんね。理解できなかったセクションは、戻ってもう一度読んでみてください。IDE でフォローするのも役立ちます。でも、黒魔術はもうやめて、気分がいいに違いありません。少なくとも、最初はそう感じました。
そして、次にメソッドを静的に呼び出すときには、そうではないかもしれないことを覚えておいてください🪄