控制反转以及php的反射机制,PHP 依赖注入(DI)和控制反转(IoC)

PHP 依赖注入(DI)和控制反转(IoC)

说明|优点用来减少程序间耦合的一种设计模式

依赖注入可以有效分离对象和所需资源,是整个体系变的灵活

概念依赖注入和控制反转对同一件事情的不同描述(描述的角度不同)

依赖注入:应用程序依赖容器创建并注入它所需要的外部资源

控制反转:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源

以代码为例子,来深刻理解一下

这个例子为正常User类调用FileLog类时的写法<?php

//文件记录日志

class FileLog{

public function write(){

echo 'file log write...';

}

}

//登录成功,记录登录日志

class User

{

protected $log;

public function __construct()

{

$this->log = new FileLog();

}

public function login()

{

echo 'login success...';

$this->log->write();

}

}

$user = new User();

$user->login();

/**

*当User类需要FileLog类时,FileLog类就相当于User类的外部资源

*主动实例化FileLog类

*弊端:当我们不想用文件存储日志时,我们需要改动User类中构造方法

*/

?>

这个例子为使用了IoC/DI容器后例子<?php

//文件记录日志

class FileLog{

public function write(){

echo 'file log write...';

}

}

//登录成功,记录登录日志

class User

{

private $log;

public function setLog(FileLog $log){

$this->log = $log;

}

public function write(){

$this->log->write();

}

}

$fileLog = new FileLog();

$user = new User();

/**当User类需要FileLog类的时候,采用注入的方式*/

$user->steLog($fileLog);

$user->write();

?>

在了解下面代码之前,我们需要简单的回顾一下匿名函数以及它使用外部参数和如何调用,同样,我们以一段代码为例子来简单说明一下<?php

//定义一个简单的匿名函数

$p1 = 'p1';//匿名函数外部参数需要use($param)才能使用

$test = function ($p0)use ($p1){

echo $p0,'------',$p1;

};

//调用匿名函数

$test('p0');

/**

* 输出:

* p0------p1

*/

?>

上面简单的通过代码了解了上面代码,下面我们把上面代码完善优化一下<?php

interface Log

{

public function write();

}

// 文件记录日志

class FileLog implements Log

{

public function write(){

echo 'file log write...';

}

}

// 数据库记录日志

class DatabaseLog implements Log

{

public function write(){

echo 'database log write...';

}

}

class User

{

protected $log;

public function __construct(Log $log)

{

$this->log = $log;

}

public function login()

{

// 登录成功,记录登录日志

echo 'login success...';

$this->log->write();

}

}

class Ioc

{

public $binding = [];

public function bind($abstract, $concrete)

{

//这里为什么要返回一个closure呢?因为bind的时候还不需要创建User对象,所以采用closure等make的时候再创建FileLog;

$this->binding[$abstract]['concrete'] = function ($ioc) use ($concrete) {

echo 3,"-----";

return $ioc->build($concrete);

};

}

public function make($abstract)

{

//根据key获取binding的值

echo 1,"-----";

$concrete = $this->binding[$abstract]['concrete'];

echo 2,"-----";

return $concrete($this);

}

// 创建对象

public function build($concrete) {

echo $concrete,"----";

$reflector = new ReflectionClass($concrete);

echo 4,"-----";

$constructor = $reflector->getConstructor(); //获取反射类的构造方法

var_dump(is_null($constructor));

if(is_null($constructor)) {

echo "--------",5,"-------";

return $reflector->newInstance(); // 从指定的参数创建一个新的类实例

}else {

echo "--------",6,"-------";

$dependencies = $constructor->getParameters(); //获取参数

$instances = $this->getDependencies($dependencies); // 获取依赖

return $reflector->newInstanceArgs($instances); //从给出的参数创建一个新的类实例

}

}

// 获取参数的依赖

protected function getDependencies($paramters) {

$dependencies = [];

echo 7,"-------";

foreach ($paramters as $paramter) {

$dependencies[] = $this->make($paramter->getClass()->name); //getClass 返回对象实例 obj 所属类的名字。如果 obj 不是一个对象则返回 FALSE。

}

return $dependencies;

}

}

//实例化IoC容器

$ioc = new Ioc();

$ioc->bind('Log','FileLog');

$ioc->bind('user','User');

$user = $ioc->make('user');

$user->login();

/**

*运行

*$ php Test.php

* 1-----2-----3-----User----4-----bool(false)------6-------7-------

* 1-----2-----3-----FileLog----4-----bool(true)-------5-------

* login success...file log write...

* 由上面可以看出来,

* $ioc = new Ioc(); //实例化Ioc容器

*

* //匿名函数外部参数需要use($param)才能使用

* $ioc->bind('Log','FileLog');//把参数为外部参数为FileLog的匿名函数赋值给以$this->binding['Log']['concrete']

* $ioc->bind('user','User');//把参数为外部参数为User的匿名函数赋值给以$this->binding['user']['concrete']

*

* $user = $ioc->make('user');//获取 $this->binding['user']['concrete'] 的匿名函数,然后调用匿名函数 $concrete($this),

* 走3,调用build($concrete)方法,利用反射类获取到原始类的属性和方法。判断 is_null($constructor) 为false(因为User类有构造方法),

* 走6,获取构造方法需要传入的参数,走7,走1, 获取依赖类 $this->binding['Log']['concrete'] ,往次反复,加载到所需要的依赖类。

* 最后执行

* $user->login();

* login success...file log write...

*/

?>Ioc 容器维护 binding 数组记录 bind 方法传入的键值对如:log=>FileLog, user=>User

在 ioc->make ('user') 的时候,通过反射拿到 User 的构造函数,拿到构造函数的参数,发现参数是 User 的构造函数参数 log, 然后根据 log 得到 FileLog。

这时候我们只需要通过反射机制创建 $filelog = new FileLog ();

通过 newInstanceArgs 然后再去创建 new User ($filelog);

拓展:ReflectionClass 类报告了一个类的有关信息。<?php

class X {

}

class_alias('X','Y');

class_alias('Y','Z');

$z = new ReflectionClass('Z');

echo $z->getName(); // X

?><?php

/**

* abc

*/

class a{}

$ref = new ReflectionClass('a');

$ref = unserialize(serialize($ref));

var_dump($ref);

var_dump($ref->getDocComment());

// object(ReflectionClass)#2 (1) {

// ["name"]=>

// string(1) "a"

// }

// PHP Fatal error: ReflectionClass::getDocComment(): Internal error: Failed to retrieve the reflection object

?>

另:欢迎大家一起交流学习,以上为个人理解,如有错误,请在评论区留言纠正,不甚感激。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值