PHP控制反转(IOC)与依赖注入(DI)

编程

概念

IOC与DI 据我了解其实早期是JAVA的理念,长期并不被PHP业界接受,因为PHP的理念是简单高效,但是长期的发展使得PHP必须为了工程规范和开发解耦必须走上这条路。Laraval框架为PHP的发展带来了的理念,逐步的各大框架都开始走上了标准化的开发步伐。这其中包含了ThinkPHP、EasySwoole、Swoft等等。

控制反转

传统的开发模式如果我们想要的到一个对象,我们必须去使用new。 这种方式的类控制权限在人的手中,是程序需要的时候认为主动创建依赖的对象(见下图1-1)。

// 图 1-1

// 通常的依赖注入模式

class Course

{

protected $user;

public function __construct(User $user) {

$this->user = $user;

}

}

依赖注入

前者说控制反转中,系统会将所有的请求单例Bean或者 全局单例Bean 保存在专用的IoC 容器当中,根据代码的需要 选择性的注入对应的需求类,注入的类 由IoC 容器管理, 降低程序的耦合,使得开发人员只需要关注对应的业务逻辑。

两者之间的关联

可以说两者是相辅相成的,依赖注入依靠IOC 控制反转,是IOC的最终目的,而 IOC 对于 依赖注入 是其实现的前提。 这里我们举个栗子 , 其实 PHP的composer 我们可以把它理解为一个IOC 容器, 我们根据程序的需要 动态的加载和删除对应的包文件。而加载的过程可以理解为对应的依赖注入过程。

IOC容器的简单实现

简单的容器接口

<?php

namespace libraryContainer;

interface ContainerInterface {

public function get(string $bean);

public function has(string $bean);

public function set(string $bean, $value)

}

定义容器基类

<?php

namespace libraryContainer;

class ContainerAccess implements ArrayAccess {

private $keys = [];

public function __construct(array $value = []) {

}

public function offsetExists($offset) {

return isset($this->keys[$offset]);

}

public function offsetGet($offset) {

return $this->keys[$offset];

}

public function offsetSet($offset, $value) {

$this->keys[$offset] = $value;

}

public function offsetUnset($offset) {

if(isset($this->keys[$offset])) {

unset($this->keys[$offset])

}

}

}

容器类实现

<?php

namespace libraryContainer;

class Container extends ContainerAccess implements ContainerInterface

{

/**

* 存放bean方法

*

* @var array

*/

protected $inject = [];

/**

* 存放bean对象

*

* @var array

*/

protected $instance = [];

/**

* 获取bean对象

*

* @param string $bean

* @return mixed

*/

public function get($bean)

{

return $this->offsetGet($bean);

}

/**

* 是否存在bean

*

* @param [type] $bean

* @return boolean

*/

public function has($bean)

{

return $this->offsetExists($bean);

}

/**

* 设置bean对象

*

* @param string $bean

* @param string $value

* @return void

*/

public function set($bean, $value)

{

$this->offset($bean, $value);

}

/**

* 获取bean

*

* @param string $bean

* @return void

*/

public function __get($bean)

{

return $this->offset($bean);

}

/**

* 获取bean

*

* @param string $bean

* @return void

*/

public function __set($bean, $value)

{

$this->offset($bean, $value);

}

/**

* 设置注入方法

*

* @param string $bean

* @param string $methodName

* @param Closure $methodBody

* @return void

*/

public function setInjectMethod($bean, $methodName, $methodBody)

{

if (!isset($this->inject[$bean][$methodName])) {

$this->inject[$bean][$methodName] = $methodBody;

}

}

/**

* 获取注入方法

*

* @param string $bean

* @param string $methodName

* @return mixed

*/

public function getInjectMethod($bean, $methodName)

{

if (isset($this->inject[$bean][$methodName])) {

return $this->inject[$bean][$methodName];

}

}

/**

* 调用bean方法

*

* @param string $bean

* @param string $methodName

* @param array $parameters

* @return void

*/

public function callback($bean, $methodName, $parameters = [])

{

$method = get_class_methods($this->get($bean));

if (in_array($methodName, $method, TRUE)) {

call_user_func_array([$this->get($bean), $methodName], $parameters);

} else {

$injectMethod = $this->getInjectMethod($bean, $methodName);

if (isset($injectMethod)) {

call_user_func($injectMethod, $parameters);

} else {

throw new RuntimeException("$bean 上不存在 $methodName 方法");

}

}

}

}

注入案例

<?php

$user = new modelUser;

$user->age = 25;

$user->name = "xxxx";

$container = new libraryContainerContainer();

// 存入容器

$container->set("user", $user);

// 获取userBean

$userBean = $container->user;

// 调用user方法

$userBean->callback("user", "save");

// 注入类中不存在的方法

$container->setInjectMethod("user", "methodTest", function () use ($userBean) {

for ($i = 0; $i < 6; $i++) {

echo $i;

}

});

// 调用类不存在的方法

$container->callback("user", "methodTest");

仿Laravel的依赖注入简单版

<?php

namespace libraryContainer;

class ClassGenerator extends Container

{

/**

* 类构造器

*

* @param string $className

* @return stdClass

*/

public function build($className)

{

if (is_string($className) && isset($this->instance[$className])) {

return $this->instance[$className];

}

// 反射类

$reflector = new ReflectionClass($className);

// 去除不可实例化

if (!$reflector->isInstantiable()) {

throw new RuntimeException("$className can"t instantiate");

}

// 获取构造

$construct = $reflector->getConstructor();

// 无构造

if (is_null($construct)) {

return new $className;

}

// 有构造

$parameters = $construct->getParameters();

$denpendencies = [];

foreach ($parameters as $parameter) {

// 获取参数类型

$denpendency = $parameter->getClass();

if (is_null($denpendency)) {

if ($parameter->isDefaultValueAvailable()) {

$denpendencies[] = $parameter->getDefaultValue();

}

} else {

// 递归注入

$denpendencies[] = $this->build($denpendency->name);

}

}

// 实例化类

$class = $reflector->newInstanceArgs($denpendencies);

$this->instance[$className] = $class;

return $class;

}

}

应用

框架的应用

在HTTP框架中, 依赖注入的应用最为广泛的就是Request对象和Response对象如下图,通常的http执行逻辑都是路由映射控制器方法,框架一般是通过反射来执行控制器函数,而在反射类的同时就可以采用上述类laravel 的方式 获取类型对象,同时可以将Request对象和Response对象直接注入construct构造中,使得用户专心处理业务逻辑。

<?php

namespace appcontroller;

class baseController

{

/**

* 请求对象

*

* @var Request

*/

private $request;

/**

* 返回对象

*

* @var Response

*/

private $response;

/**

* 控制基类构造

*

* @param Request $request

* @param Response $response

*/

public function __construct(Request $request, Response $response)

{

$this->request = $request;

$this->response = $response;

}

}

类SpringBoot的实现思路

在一个请求当中, 存在请求Bean 和 全局Bean 如果要实现类似springboot的解析注入,只需要在当前的基础上获取类中属性,然后根据属性的注解获取到对应的类名称,注入对应的类。当然在这里 篇幅有限,不做详解,这种注解方式对 php-fpm的意义不大,只有常驻进程的框架有实现的意义。有兴趣的同学可以了解一下 Swoft。

以上是 PHP控制反转(IOC)与依赖注入(DI) 的全部内容, 来源链接: utcz.com/z/517540.html

回到顶部