🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
我们来详细的描述一下: 首先我们假设A依赖B和C,B依赖D,C依赖E,D依赖F,现在我们来看看怎么实例化A? ~~~ $a = new A(new B(new D(new F)), new C(new E())); ~~~ 是不是感觉要崩溃了,实例化A之前就得先知道它依赖哪些类。 从一开始我们为了解决依赖的问题,到现在依赖却成了最棘手的问题,怎么办? 有人提出:如果我们也有一个类似composer的管理容器,在实例化A的时候,能够解决各个类之间的依赖问题是不是就很ok了? 没错,就是这样。 php有一个IoC容器的概念,其作用,就是帮助我们解决这个问题。 IoC容器又是什么?所谓的IoC容器呢,并没有多么高大上,它其实就是一个类,为了解决依赖问题,IoC应该提供如下功能: 存储定义的类 实例化类 下面来看一个简版的实现 ~~~ Class Container { private $_definitions; public function set($class, $definition) { $this->_definitions[$class] = $definition; } public function get($class, $params = []) { $definition = $this->_definitions[$class]; return call_user_func($definition, $params); } } ~~~ Container类封装的很简单,set方法用于存储类,get用于实例化类。 还以上一节用户发送邮件为例 ~~~ class EmailSenderBy163 { private $_name; public function __construct($name = '') { $this->_name = $name; } public function send() { } } ~~~ 我们看一下如何借用IoC容器操作 ~~~ $container = new Container; $container->set('EmailSenderBy163', function ($name = '') { return new EmailSenderBy163($name); }); $emailSenderBy163 = $container->get('EmailSenderBy163', '163'); echo "<pre>"; print_r($emailSenderBy163); ~~~ 结果 ~~~ EmailSenderBy163 Object ( [_name:EmailSenderBy163:private] => 163 ) ~~~ 简单分析下上面代码的含义 首先我们定义了一个容器类 Container 定义了一个私有属性 $_definitions, 用于保存定义的类 set 方法,添加类及其定义到 $_definitions 属性中,第一个参数是类名,第二个参数这里是一个回调函数,用于创建类 $class get 方法,传递类名,通过 属性 $_definitions 找到类的定义,并调用 call_user_func 函数调用该定义, 第二个参数 $params 是set时第二个参数即回调函数的参数 当然,这只是一个简版的实现 下面我们扩展一下,如果要实现有依赖其他对象的类,该如何实现呢? 我们把Container的get方法整理一下 ~~~ Class Container { private $_definitions; public function set($class, $definition) { $this->_definitions[$class] = $definition; } public function get($class, $params = []) { if (isset($this->_definitions[$class]) && is_callable($this->_definitions[$class], true)) { $definition = $this->_definitions[$class]; return call_user_func($definition, $this, $params); } else { throw new Exception("error"); } } } ~~~ 注意同刚才的get方法的区别。相比于简版的实现,get方法在判断回调函数上趋于严谨。 User类定义如下 ~~~ class User { private $_emailSenderObject; public function __construct(EmailSenderBy163 $emailSenderObject) { $this->_emailSenderObject = $emailSenderObject; } public function register() { // other code $this->_emailSenderObject->send(); } } ~~~ 注意看User类,我们想在实例化User的时候,通过构造函数“注入” EmailSenderBy163 的对象,并赋值给User::$_emailSenderObject属性。 来看看此时我们应该如何用Container实例化 EmailSenderBy163 和 User 。 ~~~ $container = new Container; $container->set('EmailSenderBy163', function ($container, $name = '') { return new EmailSenderBy163($name); }); $container->set('User', function ($container, $params = []) { return new User($container->get($params[0], $params[1])); }); echo '<pre>'; print_r($container->get('EmailSenderBy163', ['163'])); print_r($container->get('User', ['EmailSenderBy163', '163'])); ~~~ 结果 ~~~ EmailSenderBy163 Object ( [_name:EmailSenderBy163:private] => Array ( [0] => 163 ) ) User Object ( [_emailSenderObject:User:private] => EmailSenderBy163 Object ( [_name:EmailSenderBy163:private] => 163 ) ) ~~~ 似乎没什么问题,一个类依赖的其他对象我们也解决了。 但是如果我们要解决文中一开始抛出的问题,还是需要先把所有的类全部set进去,然后get的时候指定其所依赖的类,依然很麻烦。 上一节文末我们让大家课后看一下反射的知识,不知道你熟不熟悉? 我们简单的聊两句:反射,是php5增加的功能,通过反射,可以导出或提取出关于类、方法、属性、参数等的详细信息。 也就是说,我们可以利用反射相关的一系列API,来分析类所依赖的对象,并做自动实例化处理。 限于篇幅,反射相关的API我们就不多做介绍了,不熟悉的可以看一下手册,了解即可。 下面我们基于反射,让我们的Container更强悍一些。 ~~~ Class Container { public function get($class, $params = []) { return $this->build($class, $params); } public function build($class, $params) { $dependencies = []; $reflection = new ReflectionClass($class); $constructor = $reflection->getConstructor(); if ($constructor !== null) { foreach ($constructor->getParameters() as $param) { $c = $param->getClass(); if ($c !== null) { $dependencies[] = $this->get($c->getName(), $params); } } } foreach ($params as $index => $param) { $dependencies[$index] = $param; } return $reflection->newInstanceArgs($dependencies); } } ~~~ 同样是User类,现在我们可以这样实例化了 ~~~ $container = new Container; $user = $container->get('User'); echo '<pre>'; print_r($user); ~~~ 结果 ~~~ User Object ( [_emailSenderObject:User:private] => EmailSenderBy163 Object ( [_name:EmailSenderBy163:private] => ) ) ~~~ # 完整的例子 ~~~ <?php class Container { public function get($class, $params = []) { return $this->build($class, $params); } public function build($class, $params) { $dependencies = []; $reflection = new ReflectionClass($class); $constructor = $reflection->getConstructor(); if ($constructor !== null) { foreach ($constructor->getParameters() as $param) { $c = $param->getClass(); if ($c != null) { $dependencies[] = $this->get($c->getName(), $params); } } } if (isset($params[$class])) { $param = $params[$class]; foreach ($param as $index => $value) { $dependencies[$index] = $value; } unset($params[$class]); } return $reflection->newInstanceArgs($dependencies); } } class SendQQ { private $name; public function __construct($name = '未知') { $this->name = $name; } public function send() { echo "发消息给 {$this->name} \n"; } } class User { private $SendQQ; public function __construct(SendQQ $SendQQ) { $this->SendQQ = $SendQQ; } public function register() { $this->SendQQ->send(); // echo "User中的register方法 \n"; } } class login { private $obj; public function __construct(User $user) { $this->obj = $user; } public function login() { echo "login类中login方法 \n"; } } class Test { private $_login; private $_user; public $_message; public function __construct(login $login, User $user, $message) { $this->_login = $login; $this->_user = $user; $this->_message = $message; } public function test() { $this->_user->register(); echo "\n message的值是: {$this->_message} \n"; // echo "test类中test方法 \n"; } } $Container = new Container(); $Test = $Container->get('Test', ['SendQQ' => ['name' => 'jdxia'], 'Test' => ['message' => '成功']]); $Test->test(); print_r($Test); ~~~