IOC容器
class IOC{ /** * 绑定 全局单例对象 * @var array ['class_name' => obj] */ private static $single = []; /** * 绑定 非单例对象 * @var array ['class_name' => class_name] */ private static $nosingle = []; /** * 注册全局单例对象 * @param $name class_name 类名::class * @param $obj new class_name obj * @throws Exception */ public static function singleton($name, $obj){ if (!is_object($obj)) { throw new Exception('method singleton args $obj must be object'); } static::$single[$name] = $obj; } /** * 注册非单例对象,运行时候需要参数自动实例化 * 这里name可能是顶层接口,也有可能是类名 * @param $name name 类名::class * @param $class_name class_name 类名::class */ public static function nosingleton($name, $class_name){ static::$nosingle[$name] = $class_name; } /** * 通过IOC容器获取对象实例 * @param $name class::name * @return object * @throws ReflectionException */ public static function getInstance($name) { //用反射查看该类实例化需要的参数,也就是构造函数参数 $param = static::getParams($name); return $param['param'] ? (new ReflectionClass($name))->newInstanceArgs($param['param']) : (new ReflectionClass($name))->newInstance(); } /** * 获取参数 * @param $name 类名 * @param string $method 方法 * @return mixed * @throws ReflectionException */ public static function getParams($name, $method = '__construct') { $param['param'] = []; //参数 $param['value'] = []; //默认值 //如果没有构造函数 则返回空 if ($method == '__construct') { $rf = new ReflectionClass($name); if (!$rf->hasMethod($method)) { return $param; } } //反射方法 获取参数 $rf_method = new ReflectionMethod($name, $method); $params = $rf_method->getParameters(); if (!empty($params)) { foreach ($params as $k => $v) { if ($v_class = $v->getClass()) { //如果这个参数是对象,则获取对象类名,查看容器内是否有对应的设置 $v_class_name = $v_class->getName(); if (array_key_exists($v_class_name, static::$single)) { //单例对象 直接返回 $param['param'][] = static::$single[$v_class_name]; } elseif (array_key_exists($v_class_name, static::$nosingle)) { //非单例对象,通过name去实例化对象 $param['param'][] = static::getInstance(static::$nosingle[$v_class_name]); } else { //容器内没有这个,直接调用getInstance $param['param'][] = static::getInstance($v_class_name); } } else { //非对象 $param_name = $v->getName(); if ($v->isDefaultValueAvailable()) { // 是否包含默认值 $param_default_value = $v->getDefaultValue(); $param['value'][$param_name] = $param_default_value; } $param['param'][] = $param_name; } } } return $param; } /** * 容器的运行入口 主要负责加载类方法,并将运行所需的标量参数做映射和默认值处理 * @param $name 类名 * @param $method 方法 * @param array $params 参数 * @return mixed * @throws ReflectionException */ public static function run($name, $method, array $params = array()) { if (!class_exists($name)) { throw new Exception($name . "not found!"); } if (!method_exists($name, $method)) { throw new Exception($name . "::" . $method . " not found!"); } //调用getInstance 通过ioc容器获取类对象 $instance = static::getInstance($name); //获取此方法需要的参数 $param = static::getParams($name, $method); //通过param 和 传入的$params 合并 方法运行参数 $_param = array_map(function ($v) use ($param, $params) { // 对象参数 if (is_object($v)) { return $v; } // 传入的运行参数 有对应的设置 if (array_key_exists($v, $params)) { return $params[$v]; } // 使用默认值 if (array_key_exists($v, $param['value'])) { return $param['value'][$v]; } // 没有匹配到任何值 报出异常 throw new Exception($v . ' args error'); }, $param['param']); // 运行 return call_user_func_array([$instance, $method], $_param); } }
相关依赖
// 它将被以单例模式注入 全局的所有注入点都使用的同一实例 class singleton { public $name = 'singleton'; } // 它将以普通依赖模式注入 各注入点会分别获取一个实例 class noSingleton { public $name = 'noSingleton'; } // 接口注入 interface Animal { public function fly(); } // 接口实现 class Cat implements Animal { public function fly() { echo 'Cat fly'; } } // 接口实现 class Dog implements Animal { public function fly() { echo 'Dog fly'; } }
测试运行
// 具体的运行类 class TestController { public $singleton; public $noSingleton; // 这里自动注入一次 public function __construct(singleton $singleton, noSingleton $noSingleton) { $this->singleton = $singleton; $this->noSingleton = $noSingleton; } // 这里的 singleton 和 noSingleton 和 Animal 会再次注入一次 public function show(singleton $singleton1, singleton $singleton2, noSingleton $noSingleton1, noSingleton $noSingleton2,$name, Animal $animal, $age = 25) { //是否单例传入 var_dump($singleton1 === $singleton2); echo "<br>"; //是否非单例传入 var_dump($noSingleton1 === $noSingleton2); echo "<br>"; //是否实现接口 $animal->fly(); echo "<br>"; //其他标量参数 var_dump($name); echo "<br>"; var_dump($age); echo "<br>"; } } try{ // 依赖的单例注册 IOC::singleton(singleton::class, new singleton()); // 依赖的非单例注册 IOC::nosingleton(noSingleton::class, noSingleton::class); // 接口的注册 Animal 注册关联具体的实现类 cat dog 这里绑定一只猫 IOC::nosingleton(Animal::class, Cat::class); // 运行 IOC::run(TestController::class, 'show', ['name' => '小文blog']); } catch (Exception $e) { echo $e->getMessage(); } //bool(true) //bool(false) //Cat fly //string(10) "小文blog" //int(25)
思考
这么麻烦干嘛,我为何不能需要什么对象就new一下
当然不能,ioc/di最大好处就是解耦,比如 我show方法 的第一个参数需要改变成另一个对象,那我直接修改依赖即可,如果我的animal的想让dog fly 那我注册的时候 就把dog注册上即可, 类里面不需要任何改变
这个就是所谓的面向接口编程,接口可以理解为一个规范、一个约束。高层模块不直接依赖于低层模块,它们都应该依赖于抽象(指接口)。
总结IOC/DI
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则。其中**最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
依赖注入是个花俏的名词,事实上是指:类的依赖通过构造器或在某些情况下通过「setter」方法「注入」
使用依赖注入,最重要的一点好处就是有效的分离了对象和它所需要的外部资源,使得它们松散耦合,有利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
其实DI和IOC说的是一个事情,IOC就是要控制反转,而DI是实现IOC的方式!