突然好奇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');