Laravel4、そもそもサービスプロバイダーとはなんなのか

タグ: Laravel4  

サービスプロバイダーがわからないというのは多分、これが何なのかという概念が理解できていないからでしょう。これがわかってしまえば、後はやり方を確認し、使えるでしょう。(もしくは、使わないという判断ができるでしょう。)

今回はどうすればよいかではなく、なんなのかという説明です。

サービスプロバイダーとは

一言で言ってしまえば、「準備コード」をまとめておくクラスのことです。それだけです。

Laravelでは、app/start下に、準備コードを書くことができる、スタートファイルが準備されています。global.phpはいつでも読み込まれるもの、artisan.phpはartisanコマンドを実行する場合の準備コードです。もしくは、自分で動作環境名を付けたファイルを設置することができ、その場合は付けた名前の環境名の場合のみ、実行されます。local環境のlocal.phpは初めから用意されています。

例えば自分で独立した認証コードを書き、global.phpにそのための準備コードを書いたとしましょう。この場合、このコードはフレームワークが起動されるたび、毎回実行されます。ですが、アクセスには認証を必要としないものもあるでしょう。すると、独自認証の準備コード実行は、無駄になります。余計なオーバーヘッドとなります。

サービスプロバイダーのロード

サービスプロバイダーのロード方法には、通常通り直ちにロードされるものと、遅延ロードされるものがあります。

遅延ロードというのは、ただ遅らせるという意味ではなく、「必要になるまで遅らせ、必要なければロードしない」という意味です。クラスのプロパティで、'protected $defer = true;'と宣言されています。

Laravelの機能は、ほとんどComposerのコンポーネント単位で構成され、独立しています。そのための準備コードは、サービスプロバイダーで準備され、ほとんどが遅延ロードされるように設定されています。

もし、コアで必要となる可能性がある準備コードを全部一度に読み込んでしまえば、様々な機能を持った「フル・スタック」フレームワークであるLaravelは、準備に時間がかかり、大変遅くなってしまいます。(そのため、フレームワークによっては、使用する前に自分で明示的にロードしなくてはなりません。これは確かに余計なオーバーヘッドがかかりませんので、早いですが、おしゃれで近代的な方法ではありません。)

Laraveでは、ある機能が呼び出された場合、ロードされているかチェックし、ロードされていなければサービスプロバイダーを呼び出し、初期準備を整え、呼び出しています。

もちろん、コアの中でもアクセスごとに必ず必要となる基本的な機能もあります。そうしたパッケージは遅延されず、普通に毎回読み込まれます。

私達ユーザーが作成する機能も、初期化のための準備が必要であれば、サービスプロバイダーを自由に作成し、通常ロード/遅延ロードを指定することができます。

登録と機能の分割

サービスプロバイダーは、app/config/app.phpで行います。サービスプロバーダーの名前空間付きクラス名を配列に登録するだけです。

例えば、皆さんの作成したアプリが本体のWebアプリと、それを補助するArtisanコマンドで構成されていたとしましょう。ある環境では、両方共必要ですが、別の環境では本体のWebアプリだけ必要だとしましょう。

こうした場合、サービスプロバイダーを本体用と、コマンド用とに分けておけば、必要に応じ動作させる機能を設定ファイルで指定できます。

Artisanコマンドの場合、引数を付けずに実行すると、コマンドヘルプが表示され、実行可能なサブコマンドがリスト表示されます。これは便利ですが、絶対に使用しないコマンドまで表示され、うざったいと思うこともあります。

Web本体とArtisanコマンドを同じサービスプロバイダーで準備してしまうと、必要もないAritsanコマンドがリスト表示されてしまいます。これは、あなたのアプリケーションのユーザーを惑わすことになります。

この理由から、Laravelのサービスプロバイダーは、コア本体とコマンドが別のサービスプロバイダーになっているものもあります。もし、皆さんが絶対に使用しないと思われるコマンドやコアの機能があれば、サービスプロバイダーの登録を削除すれば、動作しなくなります。(コア間の依存を理解していない場合、コアの削除はおすすめしません。コマンドの削除は比較的お勧めです。サブコマンドのリスト表示がわかりやすくなりますから。ユーザーに操作させる場合は特に当てはまります。)

いつロードされるか

通常ロードのサービスプロバイダーは、フレームワークの起動手順の中で実行されます。では、遅延ロードされるサービスプロバイダーをいつロードするか、Laravelはどうやって判断しているのでしょうか。

それは、IoCコンテナーに関連しています。IoCコンテナーに登録するには、登録名が必要です。難しくありません。IoCコンテナーをグローバルな何でも収容できる配列だと考えてください。登録名とは要するにキーのことです。

サービスプロバイダーで遅延ロードする場合、providersというメソッドを用意し、登録名の配列を返します。

サービスプロバイダーが登録されるときに、このメソッドからの戻り値を覚えておき、Laravelは「IoCコンテナに対して、これらの名前でアクセスされたら、このサービスプロバイダーを起動すればよいのか」と理解するわけです。もちろん、一度ロードしたら、同じセッション中にそのサービスプロバイダーを再ロードすることはありません。

サービスプロバイダーで登録するもの

初期化コードを2つにわけましょう。IoCコンテナへの登録と、それ以外です。

IoCコンテナの登録は、通常インスタンス化方法の定義です。これらはregisterメソッドの中で行います。registerメソッドの中では、IoCコンテナへの登録以外は行いません。

IoCコンテナの一番の利用目的は、オブジェクトのインスタンス化方法を登録することです。自分のオブジェクトが実行時に必要な、他のクラスインスタンスを指定することで、依存を定義することもできます。オブジェクトを入れ替えることも可能になります。(この話は長くなります。主題が別ですので今回はこれぐらいにしましょう。)

基本的に定義だけですし、その他の機能を動作させるには、実装しているクラスのインスタンスが必要になりますので、先ず最初にIoCコンテナへの登録が最初になります。

もうひとつの重要な点は、あるサービスプロバイダーは、別のサービスプロバイダー中で定義されているコンテナ登録名を使い、インスタンスを取得できることです。

そのため、他の処理を行うのとは別に、特別なregisterメソッドが用意され、ここでIoCコンテナ登録だけを行います。そして全サービスプロバーダーの登録時に、このregisterメソッドを実行し、一旦IoCコンテナに全部登録してしまいます。

それから、順番に各サービスプロバイダーのbootメソッドを実行します。bootメソッドには、「IoCコンテナへの登録以外」の処理を記述します。既にIoCコンテナには全コンポーネントの登録名と、それに対応するインスタンス化方法が定義済みですから、どのサービスプロバイダーで定義されているIoCコンテナ名でも、使えます。

エイリアスは別機能

Laravelの注目機能であるファサードに注目すると、エイリアスとサービスプロバイダーにも関係付けて使用されていますが、それぞれは別々の機能です。

例えば、名前空間のついた長い名前が面倒ならファサードクラスに限定せずとも、app/config/app.phpのエイリアス定義配列に定義し、短い別名を使用することができます。

これは、PHPが持っているエイリアス機能をそのまま利用しています。

いつ必要になるか

Laravelを学ぶ初段では、サービスプロバイダーは必要ではありません。基本的には、Composerパッケージとして独立した機能を実現したり、自分のアプリを構造や機能で分離して構築する場合に必要となります。

もし、小さなアプリを作成し、初期化コードが大したものでなければ、global.phpに書けば済みます。

逆に構造的に複雑なものや、大きなアプリを作成する場合、サービスプロバイダーを選択するのは、一手です。初期化に使用するクラスに過ぎませんので、必須ではありません。特に公開を前提としていない場合は、独自に初期化コードを記述し、好みの場所に設置することも可能です。

逆に、第三者にパッケージ形式などで使用してもらう場合、サービスプロバイダーをapp.phpに追加してもらうというのが、Laravelの常套手段となっているため、Laravelユーザーには分かりやすい方法です。(使っていないパッケージは、ユーザーが使う気にならないでしょう。)

公開しない場合でも、アプリが大きく、機能的に分離が可能であれば、それぞれを独立させ開発することができます。その場合、必要な初期化コードをglobal.phpに直接書いたり、インクルードするよりは、サービスプロバイダーを使用し、初期化コードを同じ手段でまとめておくのは、メリットとなるでしょう。IoCコンテナを経由し、お互いのインスタンスを使用するように設計しておけば、独立して開発を進められます。