LaravelのメソッドのTypeHintがどうやってパラメータをうまい具合にセットしているのか?
Routingクロージャや、Controllerメソッドで
<?php Route::get('/', function (Authenticatable $user) {
とか
<?php public function index(Authenticatable $user)
とした時に、どうやってこの$userを良い感じで\App\Userにしてくれるのかを追ってみた。
タイプヒンティングの取り方
Reflectionを使うところから知らなかったのでまずそこから。
Reflection使ってMethodやConstructorの情報を得るのは下記のようにする。参照
<?php class Foo { public function __construct(Bar $bar, Foo $foo) { } public function methodA(Bar $bar, Foo $foo){ } } class Bar { public function __construct() { } public function methodA(Bar $bar){ } } getRef('Foo'); getRef('Bar'); function getRef($class) { $reflection = new ReflectionClass($class); $params = $reflection->getConstructor()->getParameters(); echo "Constructor params\n"; foreach ($params AS $param) { echo " ". $param->getClass()->name . "\n"; } echo "---\n"; $params = $reflection->getMethod('methodA')->getParameters(); echo "methodA params\n"; foreach ($params AS $param) { echo " ". $param->getClass()->name . "\n"; } echo "---\n"; }
というので、Reflectionを使うことでMethodが何のクラスを欲しがっているかを解決できる。
Laravelでは、Illuminate/Routing/RouteDependencyResolverTrait.phpのReflectionでどうこうやってるみたい。
Routingのクロージャを使った場合
<?php Route::get('/', function (Authenticatable $user) {
この場合、Illuminate/Routing/Route.php のrunCallable()がコールされる。
<?php /** * Run the route action and return the response. * * @return mixed */ protected function runCallable() { $parameters = $this->resolveMethodDependencies( $this->parametersWithoutNulls(), new ReflectionFunction($this->action['uses']) ); $callable = $this->action['uses']; return $callable(...array_values($parameters)); }
このresolveMethodDependenciesにより、$this->action['uses']はタダのタイプヒントからObjectになっている。
RouteDependencyResolverTrait::resolveMethodDependencies()メソッドから・・・
- RouteDependencyResolverTrait::resolveMethodDependencies()
- RouteDependencyResolverTrait::transformDependency()
- Illuminate/Foundation/Application::make("Illuminate\Contracts\Auth\Authenticatable")
- Illuminate/Container/Container.php::make("Illuminate\Contracts\Auth\Authenticatable")
- "Illuminate\Contracts\Auth\Authenticatable"は、"Illuminate\Auth\AuthServiceProvider"のインスタンスが当該のObjectを作ってくれると判定。このAuthServiceProviderインスタンスにこのAuthenticatableクラスのインスタンスを作らせる。(GoFのBuilderパタン)
では、どうやってAuthenticatableはAuthServiceProviderが作ってくれると判明(設定)したのか?
Illuminate/Foundation/Application の$bindingプロパティに依る。
この$bindingにこのauthの対応を登録しているのは、Illuminate/Auth/AuthServiceProvider.phpの
<?php protected function registerUserResolver() { $this->app->bind( AuthenticatableContract::class, function ($app) { return call_user_func($app['auth']->userResolver()); } ); }
になる。
ここでようやく、$app['auth']がregisterCoreContainerAliases()でヒモづけられた'Illuminate\Auth\AuthManager'であり、そのAuthManagerがconfig/auth.phpのproviders/users/modelを参照しているということになる。
ここまで抽象化されるとわからんな。。。