概要
Laravelの代表的な機能としてファサードがある。これを使うと、staticなメソッドを呼ぶのと同じ要領で、どこからでも簡単にフレームワークの機能を呼び出すことができる。
use Illuminate\Support\Facades\Config;
Route::get('/config', function () {
return Config::get('key');
});
下の例のように、useで名前空間を指定せずとも、グローバル名前空間でアクセスするように書くことも可能。(ちなみにこれが可能なのはconfig/app.phpのaliasesに記述されている対応関係をclass_alias関数によって結びつけているから。要はエイリアス(別名)をつけて省略的に書けるようにしていると思っておけば良いと思う。たぶん。)
Route::get('/config', function () {
return \Config::get('key');
});
これを一見すると、Illuminate\Support\Facades\Configクラスに実装されたgetメソッドを呼んでいるように見えるが、実はgetメソッドはIlluminate\Support\Facades\Configクラスにも、継承元のIlluminate\Support\Facades\Facadeクラスにも存在していない。実際には、getFacadeAccessor()が実装されているのみである。
<?php
namespace Illuminate\Support\Facades;
class Config extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'config';
}
}
この時何が起こっているか、簡単に辿ってみる。
マジックメソッドと__callStatic()
Illuminate\Support\Facades\Configクラスはget()を持っていなかった。
実装されていないstaticなメソッドを呼ぼうとするとどうなるかというと、PHPでは__callStatic()というマジックメソッドが呼ばれることになる。
public static __callStatic ( string $name , array $arguments ) : mixed
__callStatic() は、 アクセス不能メソッドを静的コンテキストで実行したときに起動します。
引数 $name は、 コールしようとしたメソッドの名前です。 引数 $argumentsは配列で、メソッド $name に渡そうとしたパラメータが格納されます。
マジックメソッドはPHPにおける特殊な関数であり、開発者が直接指定して呼び出すことはほとんどなく、特定のタイミングで勝手に呼び出される。代表的なものが馴染み深い__construct()であり、新たにインスタンスが生成されるタイミングで勝手に呼び出されるマジックメソッドといえる。
中身
継承元のIlluminate\Support\Facades\Facadeクラスには、__callStatic()が実装されている。(コメントなどは省略)
<?php
namespace Illuminate\Support\Facades;
// ...
abstract class Facade
{
// ...
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) {
return $name;
}
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
if (static::$app) {
return static::$resolvedInstance[$name] = static::$app[$name];
}
}
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}
return $instance->$method(...$args);
}
}
上記のPHPマニュアルからの引用の通り、__callStatic()の引数$methodには呼び出そうとしたメソッドの名前、$argsにはその時に使用した引数が配列の形式で入る。
getFacadeRoot()でサービスコンテナ($app)から対象となるインスタンスを取得し、そのインスタンスのメソッドを引数の$methodによって実行する。
つまり、staticなメソッドを使用しているように見えて、実際には実体となるインスタンスがサービスコンテナから取り出され、指定したメソッドが実行される、という流れになっていることがわかる。