読者です 読者をやめる 読者になる 読者になる

エンジニアですよ!

頑張れ俺くん、巨匠と呼ばれるその日まで

Laravel Reading ServiceProvider Register

ルーティング周りを読んでいて気になった内部的なローディングのことを少し確認しておく。

laravelはたくさんのServiceProviderが提供するサービスを利用して動いている(?)

ServiceProviderをregisterすることで、色々なところから使える状態になる

https://github.com/laravel/framework/blob/4.2/src/Illuminate/Support/ServiceProvider.php#L44

以前読んだRoutingのServiceに加えて、Event, ExceptionはBaseServiceProviderとして特別扱いされていて、Applicationのコンストラクタで呼ばれてregisterされている

    protected function registerBaseServiceProviders()
    {
        foreach (array('Event', 'Exception', 'Routing') as $name)
        {
            $this->{"register{$name}Provider"}();
        }
    }

それ以外はstart.phpから。

https://github.com/laravel/framework/blob/4.2/src/Illuminate/Foundation/start.php#L208L210

$providers = $config['providers'];

$app->getProviderRepository()->load($app, $providers);

このProviderRepositoryがeagerに読み込むかlazyに読み込むかなど振り分けつつ、ここでServiceProviderたちをregisterしている

registerは何をすることなのか。

Routingの場合は$app['router']にRouteをセットすることだった。

AuthServiceProvider

    public function register()
    {
        $this->app->bindShared('auth', function($app)
        {
            // Once the authentication service has actually been requested by the developer
            // we will set a variable in the application indicating such. This helps us
            // know that we need to set any queued cookies in the after event later.
            $app['auth.loaded'] = true;

            return new AuthManager($app);
        });
    }

CacheServiceProvider

    public function register()
    {
        $this->app->bindShared('cache', function($app)
        {
            return new CacheManager($app);
        });

        $this->app->bindShared('cache.store', function($app)
        {
            return $app['cache']->driver();
        });

        $this->app->bindShared('memcached.connector', function()
        {
            return new MemcachedConnector;
        });

        $this->registerCommands();
    }

CommandsServiceProvider

 public function register()
    {
        $this->app->bindShared('command.session.database', function($app)
        {
            return new Console\SessionTableCommand($app['files']);
        });

        $this->commands('command.session.database');
    }

これらを見ているとbindSharedを呼び出すのがメインっぽい

bindSharedのやっていることはこれだけで、bindを単にちょっとラップしてるだけ。offsetSetからもbindは呼ばれていて、ただその場合は第3引数が渡されないのでsharedがfalseになる。

    public function bindShared($abstract, Closure $closure)
    {
        return $this->bind($abstract, $this->share($closure), true);
    }

bindにClosureが渡されていない場合はgetClosureというのが呼ばれる

    protected function getClosure($abstract, $concrete)
    {
        return function($c, $parameters = array()) use ($abstract, $concrete)
        {
            $method = ($abstract == $concrete) ? 'build' : 'make';

            return $c->$method($concrete, $parameters);
        };
    }

結構Closureを多用している感あるなー。今回はclosureが渡されているので関係ないけど

aliasの設定とかゴニョゴニョしつつ

array(
    'concrete' => Closure,
    'shared' => bool
)

な形で$appからアクセスできるように追加している。これがregisterということかな。

sharedは何に使われているのかまだ見ていないけど、$appにブラケットでアクセスしようとすると、そのkeyでmake($key)が呼ばれて、concreteのclosureを使って作られたインスタンスを取得できるという感じ。