Home > Laravel | PHP > Laravel コードで見るファサードクラスの仕組み

Laravel コードで見るファサードクラスの仕組み

この記事の所要時間: 839

Laravel の特徴として良く挙げられるファサードクラスの仕組みをコードで見てみました。

laravel

Laravel のファサードクラス

Laravel では、Input::get()Route::get()など、クラスメソッドでフレームワークの機能を利用する場面が多くあります。

これは一見すると、InputやRouteクラスで提供されているクラスメソッドを実行しているだけに見えますが、これらのクラスにメソッドの実装があるわけではなく、実際はIoCコンテナに格納されているインスタンスのメソッドを実行しています。

例えば、InputであればIlluminate\Http\Requestクラス、RouteであればIlluminate\Routing\Routerのインスタンスメソッドが実行されます。

これらのインスタンスは、IoCコンテナにて管理されており、ファサードクラスのクラスメソッドが実行されると、IoCコンテナから定められたインスタンスを取得して、そのインスタンスメソッドを実行する仕組みになっています。

ファサードクラスの利用により、コードの記述がシンプルになります(ここは意見が分かれるところですが)。また、ファサードクラスでのメソッド呼び出しでは、自身では処理を行わずに、IoCコンテナに格納されたインスタンスなどに委譲します。つまり、ファサードクラスが呼び出すインスタンスを差し替えることで実際に処理を行うクラスを変えることができます。

Laravel のファサードクラスでは、shouldReceive()メソッドにて、テスト時に実行するインスタンスをモックオブジェクトと差し替える機能が標準で用意されています。

ファサードクラス実行の流れ

ではRoute::get()を例にして、ファサードクラスがどのように実行されるか見て行きましょう。

  • 1) ファサードクラスの解決
  • 2) ファサードクラスのクラスメソッド実行
  • 3) IoCコンテナからインスタンス取得
  • 4) インスタンスメソッドの実行

1. ファサードクラスの解決

まずRouteクラスの解決を行います。実は、Routeクラスは、フレームワークでは定義されていません。

では、なぜRouteクラスのクラスメソッドが実行できるのかというと、class_alias()関数にて、存在するクラスの別名として、Routeを定義するためです。

この処理はIlluminate\Foundation\AliasLoaderにて行います。

Routeクラスへのアクセスがあると、クラス定義を探すためにオートローダが起動します。オートローダには、Illuminate\Foundation\AliasLoaderとComposerのものが登録されているのですが、はじめにIlluminate\Foundation\AliasLoaderが実行されます。

Illuminate\Foundation\AliasLoaderでオートロードを行う箇所が以下です。

$this->aliasesに対象のクラス名(ここではRoute)があれば、class_alias()で、クラス別名を設定します。

	public function load($alias)
	{
		if (isset($this->aliases[$alias]))
		{
			return class_alias($this->aliases[$alias], $alias);
		}
	}

$this->aliasesは、app/config/app.phpで定義しているaliasesキーの内容がセットされています。

Routeは、下記のように設定されているため、Illuminate\Support\Facades\Routeへの別名として設定されます。

		'Route'           => 'IlluminateSupportFacadesRoute',

次にIlluminate\Support\Facades\Routeについて、オートロードによる読み込みが行われるので、RouteIlluminate\Support\Facades\Routeの別名として利用できるようになります。

2. Routeクラスのクラスメソッドを実行

次に、Routeクラスのget()メソッドを実行します。

Routeクラス(Illuminate\Support\Facades\Route)を見ると、get()というメソッドは存在しません。

Illuminate\Support\Facades\Routeの基底クラスであるIlluminate\Support\Facade\Facadeには、__callStatic()メソッドが定義されているため、このメソッドが呼ばれます。

__callStatic()メソッドが下記です。

	public static function __callStatic($method, $args)
	{
		$instance = static::resolveFacadeInstance(static::getFacadeAccessor());

		switch (count($args))
		{
			case 0:
				return $instance->$method();

			case 1:
				return $instance->$method($args[0]);

			case 2:
				return $instance->$method($args[0], $args[1]);

			case 3:
				return $instance->$method($args[0], $args[1], $args[2]);

			case 4:
				return $instance->$method($args[0], $args[1], $args[2], $args[3]);

			default:
				return call_user_func_array(array($instance, $method), $args);
		}
	}

}

3. IoC コンテナからインスタンス取得

__callStatic()メソッドの先頭では、resolveFacadeInstance()メソッドを実行して、IoC コンテナから処理対象のインスタンスを取得します。

まず、static::getFacadeAccessor()を実行して、どのインスタンスを取得するかを決定します。このメソッドは各ファサードクラスで定義されており、Routeクラスの場合は下記のようになっています。ここでは、routerという文字列を返しています。

このようにファサードクラスではgetFacadeAccessor()の戻り値で、実行するインスタンスを指定します。

	protected static function getFacadeAccessor() { return 'router'; }

このrouterを引数にresolveFacadeInstance()メソッドを実行して、インスタンスを取得します。

resolveFacadeInstance()メソッドの実装は以下です。

	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];
	}

引数で与えられた$name(ここではrouter)がオブジェクトであれば、そのまま返します。もし、すでにファサードクラスで解決済ならそのインスタンスを返します。そうでなければ、IoCコンテナからインスタンスを取得して返します。

IoCコンテナからインスタンスを取得した場合は、Fasadeクラスのクラス定数$resolvedInstanceにキャッシュされる仕組みになっているので、常に同じインスタンスが利用されます。

もし実行インスタンスを変えたい場合はswap()clearResolvedInstance()を使うと良いでしょう。

4. インスタンスメソッドの実行

  1. で取得したインスタンスについて、指定されたインスタンスメソッド(この場合get())を実行します。

さいごに

フレームワークのソースから Laravel のファサードクラスの仕組みを見てきました。

ファサードクラスの作り方として、ServiceProvider の構築が良く手順に含まれていますが、実はファサードクラスを作る上ではこれは必須ではありません。ファサードクラスが IoC コンテナからインスタンスを取得するため、そのインスタンスを事前にコンテナに設定するために ServiceProvider を利用することが多いだけです。

ファサードクラスを自作する際もこうした動きを知っていると作りやすいですね。

なお、Laravel のファサードクラスは、GoF のファサードパターンとは異なり、IoCコンテナにあるインスタンスを透過的に呼び出す仕組みとなっており、便利なサービスロケータと言った方がイメージしやすいかもしれません。( ちなみに、Laravel のファサードと GoF のファサードパターンは違うものだそうです。)

Pocket

follow us in feedly

Home > Laravel | PHP > Laravel コードで見るファサードクラスの仕組み

検索
フィード
メタ情報

Return to page top