オープンソースこねこね

Webプログラミングなどについてあれこれ。

LaravelのRouteクラスがグローバル空間で呼び出せる仕組み

最近Laravelを触っています。

で、LaravelだとURLと実行するアクションを関連付けるルーティング部分を

Route::get('/', function()
{
    return 'Hello World';
});

こんなふうに、RubySinatraっぽくかけるんですよね。ところがこのRouteというクラス、事前にuseで使う名前空間の指定もしていないし、実際にどのPHPクラスが使われているかよくわからない。そこでソースを追ってみました。

エイリアス

フロントコントローラのpubic/index.phpを起点に読み始めていくと、

pubic/index.php->bootstrap/start.php->vendor/laravel/framework/src/Illuminate/Foundation/start.phpとファイルが読み込まれて、この中で、

$aliases = $config['aliases'];

AliasLoader::getInstance($aliases)->register();

ということをやっている。でregisterメソッドで$aliasesのデータつかってspl_autoload_register関数を呼び出してオートローディングを設定している。$aliasesの内容はメインのコンフィグファイルconfig/app.phpにかかれていて、デフォルトは以下のようになっている。

'aliases' => array(

                'App'             => 'Illuminate\Support\Facades\App',
                'Artisan'         => 'Illuminate\Support\Facades\Artisan',
                'Auth'            => 'Illuminate\Support\Facades\Auth',
                'Blade'           => 'Illuminate\Support\Facades\Blade',
                'Cache'           => 'Illuminate\Support\Facades\Cache',
                'ClassLoader'     => 'Illuminate\Support\ClassLoader',
                'Config'          => 'Illuminate\Support\Facades\Config',
                'Controller'      => 'Illuminate\Routing\Controller',
                'Cookie'          => 'Illuminate\Support\Facades\Cookie',
                'Crypt'           => 'Illuminate\Support\Facades\Crypt',
                'DB'              => 'Illuminate\Support\Facades\DB',
                'Eloquent'        => 'Illuminate\Database\Eloquent\Model',
                'Event'           => 'Illuminate\Support\Facades\Event',
                'File'            => 'Illuminate\Support\Facades\File',
                'Form'            => 'Illuminate\Support\Facades\Form',
                'Hash'            => 'Illuminate\Support\Facades\Hash',
                'HTML'            => 'Illuminate\Support\Facades\HTML',
                'Input'           => 'Illuminate\Support\Facades\Input',
                'Lang'            => 'Illuminate\Support\Facades\Lang',
                'Log'             => 'Illuminate\Support\Facades\Log',
                'Mail'            => 'Illuminate\Support\Facades\Mail',
                'Paginator'       => 'Illuminate\Support\Facades\Paginator',
                'Password'        => 'Illuminate\Support\Facades\Password',
                'Queue'           => 'Illuminate\Support\Facades\Queue',
                'Redirect'        => 'Illuminate\Support\Facades\Redirect',
                'Redis'           => 'Illuminate\Support\Facades\Redis',
                'Request'         => 'Illuminate\Support\Facades\Request',
                'Response'        => 'Illuminate\Support\Facades\Response',
                'Route'           => 'Illuminate\Support\Facades\Route',
                'Schema'          => 'Illuminate\Support\Facades\Schema',
                'Seeder'          => 'Illuminate\Database\Seeder',
                'Session'         => 'Illuminate\Support\Facades\Session',
                'SSH'             => 'Illuminate\Support\Facades\SSH',
                'Str'             => 'Illuminate\Support\Str',
                'URL'             => 'Illuminate\Support\Facades\URL',
                'Validator'       => 'Illuminate\Support\Facades\Validator',
                'View'            => 'Illuminate\Support\Facades\View',

        ),

つまりRouteクラスの実体はIlluminate\Support\Facades\Routeということがわかる。

Laravelは自前で設定ファイルからオートローディングの登録を行うロジックをもっていて、それをつかってグローバル空間にクラスのエイリアスを作るという機能を実装していたわけだった。

※じつはIlluminate\Support\Facades\Routeにはルーティングに使用していた上記のgetメソッドは直接定義されておらず、ここからさらにFacadeという仕組みをつかって、別のクラスに実装を移しているのだけど割愛。

PHPDSLっぽいこと

調べてみると、どうってことはない作りでした。でもこの仕組みは、クラスの実装はネームスペースで構造化された空間に定義でき、インターフェースのみグローバル空間に公開するというのが、うまく出来てると思う。これに無名関数を組み合わせることで、LaravelのRoute定義のような、Rubyの内部DSL的な記述をPHP上に表現しているのだな、とちょっと感心したのでした。

おしまい。