サービスプロバイダー(Service Provider)を理解するためにはサービスコンテナ(Service Container)を事前に理解しておく必要があります。

サービスプロバイダーを理解していなくてもLaravelでアプリケーションを構築することも可能な上、Laravelへのサービスの追加(composerでインストールするパッケージ)も公開されているサービスのインストール手順に従ってコピー&ペーストを行えばサービスプロバイダーを意識することなく利用することができます。そのためLaravelの開発者の中にも実は理解していない人や一度も利用した経験がないという人も多数いるはずです。たとえ実装した経験がなかったとしてもサービスプロバイダーの質問をされた時に困らないようにしっかりと理解しておきましょう。

サービスプロバイダーの説明を読み進める前にサービスコンテナに関する下記の文書を事前に読んでいることをおすすめしています。

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

サービスプロバイダーの役目はサービスコンテナへのサービスの登録です。

Laravelではサービスコンテナに登録されているサービスを利用してアプリケーションの開発を行なっていきます。サービスコンテナはサービスを入れる入れ物の役割をもっており、サービスを利用するためには、サービスコンテナに事前にサービスを登録しておく必要があります。そのサービスを登録する役目を持つものがサービスプロバイダーです。

Laravelのサービスとはメール送信、暗号化やファイル操作といったLaravelアプリケーションで利用する機能です。
fukidashi

サービスプロバイダーを理解

サービスプロバイダーを登録する場所と登録したサービスの利用方法の確認、最後に自分でサービスプロバイダーを作成することを通してサービスプロバーダーの理解を深めていきます。

サービスプロバイダーの登録場所

まずはサービスプロバイダーを使ったサービスの登録がどこで行われているのかコードを見ながら確認していきましょう。

ブラウザからアクセスがあるとpubicフォルダのindex.phpが実行され、bootstrapフォルダのapp.phpが読み込まれます。


$app = require_once __DIR__.'/../bootstrap/app.php';

bootstrapフォルダのapp.phpからLaravelのコアであるApplicationクラスがインスタンス化されています。


$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);

このIlluminate\Foundation\Application.phpの中身を見るとregisterConfirureProvidersメソッドで、サービプロバイダーを登録している箇所が確認できます。このコードからサービプロバイダーはconfig[‘app.providers’]を使って読み込まれています。config[‘app.providers’]に入る値はconfigフォルダにあるapp.phpファイル内のprovidersから取得しています。


public function registerConfiguredProviders()
{
  $providers = Collection::make($this->config['app.providers'])
      ->partition(function ($provider) {
          return Str::startsWith($provider, 'Illuminate\\');
      });

configフォルダのapp.phpを開いて、providersを確認するとサービスプロバイダーの一覧を確認することができます。ここまでのコードの確認でサービスプロバイダーの登録場所がどこなのかを理解することができました。


   'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
        Illuminate\Cookie\CookieServiceProvider::class,
        Illuminate\Database\DatabaseServiceProvider::class,
        Illuminate\Encryption\EncryptionServiceProvider::class,
        Illuminate\Filesystem\FilesystemServiceProvider::class,

サービスプロバイダーの中身を確認

Laravelのサービスは、config/app.phpファイルに記述されているサービスプロバイダーから登録されます。

サービスプロバイダーに関する情報がconfig/app.phpにあることがわかったので、記述されているサービスプロバイダーの個別の中身を確認します。コードがわかりやすいEncryptionServiceProviderとFilesystemServiceProviderを使ってサービスプロバイダーの処理内容を確認するのがおすすめです。ここではEncryptionServiceProvider.phpを使います。

Encryptionは暗号化という意味を持ち、Encryptionは暗号化を行う際に利用するサービスです。FilesystemServiceProviderはファイルシステムのファイルを操作する際に利用するサービスです。
fukidashi

EncryptionServiceProvider.phpを開くとregisterメソッドの中でsingletonメソッドを使ってサービスコンテナへの登録を行なっています。

singletonメソッドは一度インスタンスを作成すると何度singletonメソッドを実行しても同じインスタンスを利用します。
fukidashi

public function register()
{
    $this->app->singleton('encrypter', function ($app) {
        $config = $app->make('config')->get('app');

        // If the key starts with "base64:", we will need to decode the key before handing
        // it off to the encrypter. Keys may be base-64 encoded for presentation and we
        // want to make sure to convert them back to the raw bytes before encrypting.
        if (Str::startsWith($key = $this->key($config), 'base64:')) {
            $key = base64_decode(substr($key, 7));
        }

        return new Encrypter($key, $config['cipher']);
    });
}

registerメソッドの中では、singletonメソッドを使ってencrypterという名前でサービスコンテナへの登録を行なっています。Encrypterクラスをインスタンス化するためには、configファイルからキーを取得する必要があるためキーに関する処理が行われています。

サービスコンテナへはencrypterという名前で登録されているため、このサービスを使いたいときはencrypterを下記のように記述することで使用することが可能になります。

動作確認のためにweb.phpに下記のコードを記述します。


//(1)インスタンス化
$encrypt = app()->make('encrypter');
//(2)$password変数には、eyJpdiI6IitLcEpqM0....が入る
$password = $encrypt->encrypt('password');
//(3)$password変数の文字列を復号化、結果暗号化する前のpasswordが表示
dd($encrypt->decrypt($password));

(1)によって、サービスコンテナに登録されたEncryptionクラスをインスタンス化して利用が可能になります。Encrypterクラスはencryptメソッドによって文字列を暗号化することが可能なので、(2)で暗号化を行なっています。encryptメソッドを利用すとpasswordという文字列が暗号化された文字列へと変換されます。Encrypterクラスは暗号化した文字列を復号化するメソッドdecryptも持っており、(3)のdecryptメソッドで復号化しています。

サービスプロバイダーを使って、サービスコンテナの登録を行わなければ、Encryptionを使うためには毎回以下のようなコードが必要となります。


$config = $app->make('config')->get('app');

if (Str::startsWith($key = $this->key($config), 'base64:')) {
    $key = base64_decode(substr($key, 7));
}

$enctypt = new Encrypter($key, $config['cipher']);

しかし、サービスプロバイダーで登録されていればサービスのインスタンス化の処理をたった一行で終わらせることができます。サービスプロバイダーがLaravelのサービスを利用する上で重要な役割を持っていることがわかります。


$encrypt = app()->make('encrypter');

サービスプロバイダーを作成する

サービスプロバイダーの登録する場所と登録方法がわかったので自分のオリジナルのサービスプロバイダーを作って登録を行ってみましょう。

サービスプロバイダーはphp artisan make:providerで作成することができます。


$ php artisan make:provider OwnServiceProvider
Provider created successfully.

実行するとapp\Providersの下にOwnServiceProvider.phpファイルが作成されます。

OwnServiceProvider.phpの中には、regiterメソッドとbootメソッドが記述されいます。


class OwnServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

registerメソッドはサービスコンテナにサービスを登録するコードを記述します。bootメソッドは、すべてのサービスプロバイダーが読み込まれたあとに実行したいコードを記述します。

bindメソッドでサービス登録と実行

サービスプロバイダーをつかってサービスの登録を行う前にサービスコンテナへの登録方法を確認しておきます。

下記のbindメソッドを使用することでサービスコンテナへの登録が行えます。

先程のEncrypterではsingletonメソッドを用いていましたが、今回はbindメソッドを使います。bindを使った場合はクラスをインスタンスする度に毎回異なるインスタンスを作成します。

app()->bind()メソッドはweb.phpに記述しています。app()はヘルパー関数のため、コントローラーなどどこに記述しても実行することができます。
fukidashi

app()->bind('myName', function(){
    return 'John Doe';
});

登録したmyNameというサービスを利用したい場合は、makeメソッドを使います。実行するとブラウザには、John Doeが表示されます。


$name = app()->make('myName');

dd($name);

サービスプロバーダーへのコード記述

先程作成したbindメソッドをOwnServiceProvider.phpのregisterメソッドの中に記述します。


public function register()
{

    app()->bind('myName', function(){
        return 'John Doe';
    });
            
}

bindメソッドの記述方法は別のServiceProvicerと同様に下記でも行うことができます。


$this->app->bind('myName', function(){
    return 'John Doe'; });     

OwnServiceProvider.phpのregisterメソッドに追加しただけではサービスコンテナへの登録は行われないので、config/app.phpへのサービスプロバイダーの追加も忘れないで行う必要があります。app.phpを開いてOwnServiceProviderを追加します。この追加によってEncryptionServiceProviderと同様にOwnServiceProviderもサービスコンテナへの追加が行われます。


/*
 * Application Service Providers...
 */
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\OwnServiceProvider::class, //追加

web.phpに下記を追加して、ブラウザにJohn Doeが表示されればサービスプロバイダーを使ったサービスコンテナへのサービス登録は成功しています。


$name = app()->make('myName');

dd($name);

この文章を読む前まではサービスプロバイダーがわからなかった人もサービスプロバイダーの追加がこんなにも簡単だと驚いたのではないでしょうか。ここまで理解できればLaravelのコア部分であるサービスコンテナとサービスプロバイダーへの不安が解消されたと思います。