(原文地址:https://blog.tanteng.me/2018/01/laravel-de...)
在 Laravel 的控制器的構造方法或者成員方法,都可以透過型別約束的方式使用依賴注入,如:
public function store(Request $request)
{
//TODO
}
這裡 $request 引數就使用了型別約束,Request 是型別約束的型別,它是一個類:\Illuminate\Http\Request.
本文研究 Laravel 的依賴注入原理,為什麼這樣定義不需要例項化就可以直接使用 Request 的方法呢?只是框架幫我們例項化並傳參了,我們看看這個過程。
1.路由定義
從源頭開始看起,在路由定義檔案中定義了這麼一個路由:
Route::resource('/role', 'Admin\RoleController');
這是一個資源型的路由,Laravel 會自動生成增刪改查的路由入口。
本文開頭的 store 方法就是一個控制器的方法,圖中可見路由定義的 Action 也是:App\Http\Controllers\Admin\RoleController@store
路由方法解析
根據路由定義找到控制器和方法,這個過程在 dispatch 方法中實現。
(檔案:vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php)
public function dispatch(Route $route, $controller, $method)
{
$parameters = $this->resolveClassMethodDependencies(
$route->parametersWithoutNulls(), $controller, $method
);
if (method_exists($controller, 'callAction')) {
return $controller->callAction($method, $parameters);
}
return $controller->{$method}(...array_values($parameters));
}
這裡 resolveClassMethodDependencies 方法,“顧名思義”這個方法的作用是從類的方法中獲取依賴物件:
protected function resolveClassMethodDependencies(array $parameters, $instance, $method)
{
if (! method_exists($instance, $method)) {
return $parameters;
}
return $this->resolveMethodDependencies(
$parameters, new ReflectionMethod($instance, $method)
);
}
這裡重點就是用到了 PHP 的反射,注意 RelectionMethod 方法,它獲取到類的方法引數列表,可以知道引數的型別約束,引數名稱等等。
這裡的 $instance 引數就是 RoleController 控制器類,$method 引數就是方法名稱 strore.
2.獲取依賴物件的示例
從方法的引數中獲取了依賴物件的約束型別,就可以例項化這個依賴的物件。
protected function transformDependency(ReflectionParameter $parameter, $parameters)
{
$class = $parameter->getClass();
// If the parameter has a type-hinted class, we will check to see if it is already in
// the list of parameters. If it is we will just skip it as it is probably a model
// binding and we do not want to mess with those; otherwise, we resolve it here.
if ($class && ! $this->alreadyInParameters($class->name, $parameters)) {
return $parameter->isDefaultValueAvailable()
? $parameter->getDefaultValue()
: $this->container->make($class->name);
}
}
根據類名從容器中獲取物件,這個繫結物件例項的過程在服務提供者中先定義和了。
然後把例項化的物件傳入到 store 方法中,就可以使用依賴的物件了。
3.關於 PHP 反射
舉個使用 ReflectionMethod 的例子。
class Demo
{
private $request;
public function store(Request $request)
{
}
}
列印出 new ReflectionMethod(Demo::class, ‘store’) 的內容如圖:
可以得出這個方法的引數列表,引數的約束型別,如 typeHint,Illuminate\Http\Request.
根據類名可以從容器中獲取一開始透過服務提供者繫結的例項。
本作品採用《CC 協議》,轉載必須註明作者和本文連結