查看: 796|回复: 0

[PHP学习] PHP通过反射实现自动注入参数

发表于 2017-9-21 08:00:05
尚学堂AD

现在的框架中都有一个容器, 而容器解决依赖的问题是通过反射来达到的,

首先先说明一下项目文件结构:

  1. / ROOT_PATH
  2. ├─src
  3. │ ├─Controllers
  4. │ │ └─IndexController.php
  5. | ├─Application.php (核心,获得实例)
  6. │ ├─Http.php
  7. │ └─Request.php
  8. ├─vendor
  9. │ └─autoload.php
  10. ├─composer.json
  11. └─index.php
复制代码

而我们要运行IndexController.php,而这个控制器的构造函数需要一个Request类,而Request类构造函数需要一个Http类。


IndexController.php

  1. <?php
  2. namespace Waitmoonman\Reflex\Controllers;
  3. use Waitmoonman\Reflex\Request;
  4. class IndexController
  5. {
  6. /**
  7. * 注入一个 Request 类
  8. * IndexController constructor.
  9. * @param Request $request
  10. */
  11. public function __construct(Request $request)
  12. {
  13. echo '我是 ' . __CLASS__ . ' 我依赖' . $request->className;
  14. }
  15. }
复制代码

Application.php

  1. <?php
  2. namespace Waitmoonman\Reflex;
  3. use Exception;
  4. use ReflectionClass;
  5. class Application
  6. {
  7. /*
  8. * @param $class
  9. * @param array $parameters
  10. * @return mixed
  11. * @throws Exception
  12. */
  13. public static function make($class, $parameters = [])
  14. {
  15. // 通过反射获取反射类
  16. $rel_class = new ReflectionClass($class);
  17. // 查看是否可以实例化
  18. if (! $rel_class->isInstantiable())
  19. {
  20. throw new Exception($class . ' 类不可实例化');
  21. }
  22. // 查看是否用构造函数
  23. $rel_method = $rel_class->getConstructor();
  24. // 没有构造函数的话,就可以直接 new 本类型了
  25. if (is_null($rel_method))
  26. {
  27. return new $class();
  28. }
  29. // 有构造函数的话就获取构造函数的参数
  30. $dependencies = $rel_method->getParameters();
  31. // 处理,把传入的索引数组变成关联数组, 键为函数参数的名字
  32. foreach ($parameters as $key => $value)
  33. {
  34. if (is_numeric($key))
  35. {
  36. // 删除索引数组, 只留下关联数组
  37. unset($parameters[$key]);
  38. // 用参数的名字做为键
  39. $parameters[$dependencies[$key]->name] = $value;
  40. }
  41. }
  42. // 处理依赖关系
  43. $actual_parameters = [];
  44. foreach ($dependencies as $dependenci)
  45. {
  46. // 获取对象名字,如果不是对象返回 null
  47. $class_name = $dependenci->getClass();
  48. // 获取变量的名字
  49. $var_name = $dependenci->getName();
  50. // 如果是对象, 则递归new
  51. if (array_key_exists($var_name, $parameters))
  52. {
  53. $actual_parameters[] = $parameters[$var_name];
  54. }
  55. elseif (is_null($class_name))
  56. {
  57. // null 则不是对象,看有没有默认值, 如果没有就要抛出异常
  58. if (! $dependenci->isDefaultValueAvailable())
  59. {
  60. throw new Exception($var_name . ' 参数没有默认值');
  61. }
  62. $actual_parameters[] = $dependenci->getDefaultValue();
  63. }
  64. else
  65. {
  66. $actual_parameters[] = self::make($class_name->getName());
  67. }
  68. }
  69. // 获得构造函数的数组之后就可以实例化了
  70. return $rel_class->newInstanceArgs($actual_parameters);
  71. }
  72. }
复制代码

Http.php

  1. <?php
  2. namespace Waitmoonman\Reflex;
  3. class Http
  4. {
  5. public $className;
  6. public function __construct()
  7. {
  8. $this->className = __CLASS__;
  9. }
  10. }
复制代码

Request.php

  1. <?php
  2. namespace Waitmoonman\Reflex;
  3. class Request
  4. {
  5. public $className;
  6. public function __construct(Http $http)
  7. {
  8. $this->className = __CLASS__;
  9. $this->className = $this->className . ' -> ' . $http->className;
  10. }
  11. }
复制代码

index.php

  1. <?php
  2. // 要实现自动载入
  3. use Waitmoonman\Reflex\Application;
  4. require 'vendor/autoload.php';
  5. // new 一个 ReflectionClass 类, 放入需要实例的类名
  6. $ctl = Application::make(\Waitmoonman\Reflex\Controllers\IndexController::class);
  7. var_dump($ctl);
复制代码

输出:

  1. 我是 Waitmoonman\Reflex\Controllers\IndexController 我依赖Waitmoonman\Reflex\Request -> Waitmoonman\Reflex\Http
  2. F:\phpStudy\WWW\reflex\index.php:12:
  3. object(Waitmoonman\Reflex\Controllers\IndexController)[9]
复制代码

这就是一个完整的反射类动态注入参数的实例。
以上代码可以查看我的git仓库



回复

使用道具 举报