Modelで引っかかったところがあって先に読んだけどまず気になるのはやはりルーティング周りになるよね。
読んでみる
プロジェクトを作ると、app/routes.phpが作られる
初期状態だと
Route::get('/', function() { return View::make('hello'); });
となっている。
Routeにgetやpostなどのメソッドが用意されてて、pathと処理を渡す感じになるんだなとわかる。
これはControllerではなく無名関数でやっちゃってる
Routeは何かなと見てみると
'Route' => 'Illuminate\Support\Facades\Route',
となってるのでlaravel/framework/src/illuminate/Support/Facades/Route.phpを見ると
class Route extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'router'; } }
何もない・・・
これはFacadeを読むことになりそうな予感
親クラスのFacedeのgetFacadeAccessorだけoverrideして名前を返してる
こういう場合、マジックメソッドとか経由して実体のメソッドを呼ぶことになるはず。
laravel/framework/src/illuminate/Support/Facades/Facade.phpを見ると__callStaticが定義されている
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Support/Facades/Facade.php#L198
instanceの取得はgetFacadeRoot
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Support/Facades/Facade.php#L116
ここでサブクラスのgetFacadeAccessorを使っている
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Support/Facades/Facade.php#L146
キャッシュとか色々しているが、結局返しているのがstatic::$app[$name]
$app
って何よ?というとsetFacadeApplication
でセットしている
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Support/Facades/Facade.php#L188
これはどこで呼ばれとるんじゃ!
ということになるけど、laravel/framework/src/illuminate/Foundation/start.phpで呼ばれている
ではたまたこれは・・・で整理すると
public/index.php(エントリポイント)
$app = require_once __DIR__.'/../bootstrap/start.php';
bootstrap/start.php
$app = new Illuminate\Foundation\Application; ... $framework = $app['path.base']. '/vendor/laravel/framework/src'; require $framework.'/Illuminate/Foundation/start.php';
ここでrequireされてそのまま実行されている
なので、$app
はnew Illuminate\Foundation\Application
そのものってことがわかる
普通のオブジェクトじゃん、でも$app[$name]
って配列風のアクセス。ってことはArrayAccess Interfaceを実装しているところを見ればいい
今重要なのはoffsetGet
なのでみると、$this->make($key)
と単に自分のmake
というメソッドを呼び出している
make
は結構めんどくさい
最終的には解決したinstanceを返しているだけなんだけど
getConcreteでinstanceを取得しているっぽいが、中身は$this->bindings
の名前のキーを取得している
$this->bindings
に値をセットしているのはbind
メソッド
bindが呼ばれるのはどこかーというとoffsetSetですよね。
これArrayAccessなので、呼び出し元は$app['hoge'] = $hoge
とかで、メソッド呼び出しじゃないのでIDEを使っても追いにくいのが面倒なところ。適当にapp['router'] =
で調べると出てきた
laravel/framework/src/illuminate/Routing/RoutingServiceProvider.php
protected function registerRouter() { $this->app['router'] = $this->app->share(function($app) { $router = new Router($app['events'], $app); // If the current application environment is "testing", we will disable the // routing filters, since they can be tested independently of the routes // and just get in the way of our typical controller testing concerns. if ($app['env'] == 'testing') { $router->disableFilters(); } return $router; }); }
こういう感じでregisterっていうのがいろんなもので呼ばれているはずだ。
(こういうProviderとかServiceっていう言葉をどういう時にどういうものに使えばいいかいまいちわからないので1つの使い方として押さえておきたい)
とりあえずそれは後回してRouterについてだけ見ると、登録しているのは$this->app->share()
にClosureを渡したもの・・・
また面倒な
Illuminate\Container\Container.phpのContainer
に定義されている
public function share(Closure $closure) { return function($container) use ($closure) { // We'll simply declare a static variable within the Closures and if it has // not been set we will execute the given Closures to resolve this value // and return it back to these consumers of the method as an instance. static $object; if (is_null($object)) { $object = $closure($container); } return $object; }; }
これ、Closureを取ってClosureを返す関数というまた面倒な・・・ 引数を1つ取って元のClosureに適用したものを返す感じか
これどういう意味があるんだろう。わからないな
この場合やりたいことはunittestの時はfilterを無効にしたRoutingのインスタンスを返したいんだろうけど
これでやっとgetConcreteが終わり。
で、その後はbuild
でコンストラクタ取得してほげほげするなりそのままインスタンス化するなりしてinstanceを返している。
あとCallback登録しておくとこの後呼んでくれるらしい
こういうイベントとコールバックは今後もよく出てくる
これでやっと$app['router']が解決できた・・・
途中見落としかけたけど、結局のところ返されているのはnew Router()だ
Illuminate\Routing\Router
HTTPの各メソッド用の関数が用意されてあり、メソッド名を第1引数ともともとのurlとハンドラを渡してaddRouteを呼ぶ
ほうほう、それでそれぞれRouteを作ってRouterのメンバ変数のroutesにaddしている
さて、とりあえず登録するところまで呼んだので次にどう使われているかをある程度把握しておこうと思う
当然エントリポイントはpublic/index.phpで$app->run()
public function run(SymfonyRequest $request = null) { $request = $request ?: $this['request']; $response = with($stack = $this->getStackedClient())->handle($request); $response->send(); $stack->terminate($request, $response); }
with
は$stack
に代入しつつ使うための小細工。
この後のhandleあたりがまたつらいので一旦区切る・・
ルーティングを調べるはずがほぼFacadeを読むことになってしまった。
最後にちょっと寄り道
けどFacadeって相変わらずよくわかっていないのでこれを気に寄り道してちゃんと確認しておこう
appに色々登録している箇所はこのくらいある。
Facade経由で呼び出されるものがたくさんあることがわかる。
これらをどうやって登録しているのかとかも後で読んでみる
grep -r "app\[.*\] = " . [~/sandbox/.../vendor/laravel] ./framework/src/Illuminate/Auth/AuthManager.php: $this->app['config']['auth.driver'] = $name; ./framework/src/Illuminate/Auth/AuthServiceProvider.php: $app['auth.loaded'] = true; ./framework/src/Illuminate/Cache/CacheManager.php: $this->app['config']['cache.prefix'] = $name; ./framework/src/Illuminate/Cache/CacheManager.php: $this->app['config']['cache.driver'] = $name; ./framework/src/Illuminate/Database/DatabaseManager.php: $this->app['config']['database.default'] = $name; ./framework/src/Illuminate/Events/EventServiceProvider.php: $this->app['events'] = $this->app->share(function($app) ./framework/src/Illuminate/Exception/ExceptionServiceProvider.php: $this->app['exception'] = $this->app->share(function($app) ./framework/src/Illuminate/Exception/ExceptionServiceProvider.php: $this->app['exception.plain'] = $this->app->share(function($app) ./framework/src/Illuminate/Exception/ExceptionServiceProvider.php: $this->app['exception.debug'] = $this->app->share(function($app) ./framework/src/Illuminate/Exception/ExceptionServiceProvider.php: $this->app['whoops'] = $this->app->share(function($app) ./framework/src/Illuminate/Exception/ExceptionServiceProvider.php: $this->app['whoops.handler'] = $this->app->share(function() ./framework/src/Illuminate/Exception/ExceptionServiceProvider.php: $this->app['whoops.handler'] = $this->app->share(function() ./framework/src/Illuminate/Foundation/start.php: $app['env'] = $env = $testEnvironment; ./framework/src/Illuminate/Mail/MailServiceProvider.php: $this->app['swift.mailer'] = $this->app->share(function($app) ./framework/src/Illuminate/Mail/MailServiceProvider.php: $this->app['swift.transport'] = $this->app->share(function($app) use ($config) ./framework/src/Illuminate/Mail/MailServiceProvider.php: $this->app['swift.transport'] = $this->app->share(function($app) use ($config) ./framework/src/Illuminate/Mail/MailServiceProvider.php: $this->app['swift.transport'] = $this->app->share(function() ./framework/src/Illuminate/Queue/QueueManager.php: $this->app['config']['queue.default'] = $name; ./framework/src/Illuminate/Remote/RemoteManager.php: $this->app['config']['remote.default'] = $name; ./framework/src/Illuminate/Routing/RoutingServiceProvider.php: $this->app['router'] = $this->app->share(function($app) ./framework/src/Illuminate/Routing/RoutingServiceProvider.php: $this->app['url'] = $this->app->share(function($app) ./framework/src/Illuminate/Routing/RoutingServiceProvider.php: $this->app['redirect'] = $this->app->share(function($app) ./framework/src/Illuminate/Session/SessionManager.php: $this->app['config']['session.driver'] = $name; ./framework/src/Illuminate/Session/SessionServiceProvider.php: $this->app['config']['session.driver'] = 'array';