聊聊PHP中实现的依赖注入(Di)实现

PHP的开发中 利用IOC容器可以很方便的储存以及获取资源 这样就可以实现解耦, 使用依赖注入的好处就是有效的分离了对象和他所需的外部资源,使得他们松散耦合,这样就有利于功能的复用。这样一来程序代码的整体结构也会变的整节可读。即随取随用。

在开发的过程中,如果我们需要一个对象就需要不断的new一个新的对象,可能会在很多处地方用到这个对象的功能,这样的话如果一些的不好的编程习惯会造成对象的无法回收,这会为
整个项目的代码和维护造成不小的困扰,所以依照我们提倡的松耦合、少入侵的原则,可以采用ICO容器也就是对这些对象的集中管理。

这里我们可以看看EasySwoole里面是怎么实现这样的容器管理的

代码入手

首先为了实现容器的统一管理,定义了一个单例trait这趟就很好的对Di这个容器进行单例化。

1
2
3
4
5
6
7
8
9
10
11
12
trait Singleton
{
private static $instance;

static function getInstance(...$args)
{
if(!isset(self::$instance)){
self::$instance = new static(...$args);
}
return self::$instance;
}
}

实现代码很简单 就是首先定义一个私有事例 在暴露一个static获取事例的function 如果已实例化则直接返回 如果没有则在初始化的时候new一个对象返回

接下来就是Di这个容器的编写 use这个单例trait时它成为单例类 然后可以想象一下容器的设置/获取这样的一些操作 那么很自然的想到我们可以这么写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Di
{
use Singleton;
private $container = array();

public function set($key, $obj,...$arg):void
{
$this->container[$key] = array(
"obj"=>$obj,
"params"=>$arg,
);
}

function delete($key):void
{
unset( $this->container[$key]);
}

function clear():void
{
$this->container = array();
}

/**
* @param $key
* @return null
* @throws \Throwable
*/
function get($key)
{
if(isset($this->container[$key])){
$obj = $this->container[$key]['obj'];
$params = $this->container[$key]['params'];
if(is_object($obj) || is_callable($obj)){
return $obj;
}else if(is_string($obj) && class_exists($obj)){
try{
$this->container[$key]['obj'] = new $obj(...$params);
return $this->container[$key]['obj'];
}catch (\Throwable $throwable){
throw $throwable;
}
}else{
return $obj;
}
}else{
return null;
}
}
}

这里定义了四个function 分别是对象的设置、获取、删除和清空。首先可以看下set也就是注入函数的原型,它接收三个参数,第一个就是key名 也就是我们再项目其他地方需要用到时可以通过key来获取这个object。在set这个function中第二个参数$obj即为需要设置储存的object。第三个参数就是一系列参数,这个参数就是object需要初始化的参数。
所以在设置时同意放入私有的container变量中 因为这个是单例类 我们不需要暴露这个container变量 我们可以通过get获取这个obj

get获取注入的资源时 首先进行判空 如果存在那么接下来就是对传之前传入对象的形式进行初始化new一个对象返回。也就是说如果是一个可调用的对象的话比如 new User() 那么直接返回这个obj。如果是一个string类型,并且存在这个class的话 很简单实例化这个类,并通过set时候传入放入初始化参数进行初始化对象并返回。另外其他的删除以及清空container的都比较简单。

在项目使用时如果需要获取这个对象那么可以直接通过get方法获取并调用 比如

1
2
$di = Di::getInstance();
$obj = $di->get('database');

那么就可以使用keydatabase的这个object。因为Di是单利类 所以对象的存取就会很好实现。 这里只是一个比较简单的IOC的实现 我们还可以根据服务的一些特性去实现其他的功能 具体的可以参考Laravel里的IOC的实现 有兴趣的可以了解一下。