依賴注入的PHP實現(一)
阿新 • • 發佈:2022-01-07
突然好奇laravel的依賴注入是怎麼實現的,翻看了下laravel的原始碼,發現在laravel在src/Illuminate/Routing/Route.php下的run方法中開始執行控制器方法
/** * Run the route action and return the response. * * @return mixed */ public function run() { $this->container = $this->container ?: new Container;try { if ($this->isControllerAction()) { return $this->runController(); } return $this->runCallable(); } catch (HttpResponseException $e) { return $e->getResponse(); } }
追蹤runController方法,一直到src/Illuminate/Routing/RouteDependencyResolverTrait.php下,我們可以看到,laravel使用反射機制進行依賴注入
/** * Resolve the object method's type-hinted dependencies. * * @param array $parameters * @param object $instance * @param string $method * @return array */ protected function resolveClassMethodDependencies(array $parameters, $instance, $method) {if (! method_exists($instance, $method)) { return $parameters; } return $this->resolveMethodDependencies( $parameters, new ReflectionMethod($instance, $method) ); }
通過對laravel原始碼的檢視,網上資料,以及PHP手冊的查閱,自己寫了個簡單的依賴注入實現。
首先假設存在一個model叫user
class User { public $table = 'users'; }
然後存在一個控制器叫UserController
class UserController { /** * 獲取表名稱 * * @param \User $user * * @return void */ public static function getTableName(User $user){ echo $user->table; } }
可以看到我們的getTableName方法中依賴注入了User類的例項,接下來我們需要通過反射來為這個方法注入引數,注入具體實現如下
class app { /** * @param mixed $controller 控制器 * @param string $method 方法 * * @throws \ReflectionException * @throws \Exception */ public static function run($controller, string $method){ //判斷方法是否存在 if (! method_exists($controller, $method)) { throw New Exception("Method [$method] is not exist !"); } //通過反射獲取方法資訊 $reflectionMethod = New ReflectionMethod($controller,$method); //攜帶引數執行該方法 $reflectionMethod->invokeArgs(new $controller,self::getMethodParams($reflectionMethod)); } /** * 獲取方法的引數並例項化 * @param $reflectionMethod * * @return array * @throws \Exception */ public static function getMethodParams($reflectionMethod) : array { $result = []; foreach ($reflectionMethod->getParameters() as $key => $param) { $class = $param->getClass(); if ($class) { $result[] = New $class->name(); }else{ throw New Exception('arguments ' . $key . 'is error!'); } } return $result; } }
最後我們執行方法,打印出User類的table屬性,這樣我們就通過對映實現了一個簡單的依賴注入。
app::run('UserController','getTableName');
所有例項程式碼如下
<?php //model class User { public $table = 'users'; } //控制器 class UserController { /** * 獲取表名稱 * * @param \User $user * * @return void */ public static function getTableName(User $user){ echo $user->table; } } //容器 class app { /** * @param mixed $controller 控制器 * @param string $method 方法 * * @throws \ReflectionException * @throws \Exception */ public static function run($controller, string $method){ //判斷方法是否存在 if (! method_exists($controller, $method)) { throw New Exception("Method [$method] is not exist !"); } //通過反射獲取方法資訊 $reflectionMethod = New ReflectionMethod($controller,$method); //攜帶引數執行該方法 $reflectionMethod->invokeArgs(new $controller,self::getMethodParams($reflectionMethod)); } /** * 獲取方法的引數並例項化 * @param $reflectionMethod * * @return array * @throws \Exception */ public static function getMethodParams($reflectionMethod) : array { $result = []; foreach ($reflectionMethod->getParameters() as $key => $param) { $class = $param->getClass(); if ($class) { $result[] = New $class->name(); }else{ throw New Exception('arguments ' . $key . 'is error!'); } } return $result; } } app::run('UserController','getTableName');