PHP的依赖注入(控制反转)
控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。依赖注入应用比较广泛。
以上内容摘自百度百科
下面使用一个面向对象的留言小程序来理解IOC.
举个栗子
message.php
<?php
/* 留言实体类 */
class message
{
public $name;
public $email;
public $content;
/* 魔术方法用于添加一个不存在的属性或者不可访问的属性(private/protected)时,将传过来的属性值赋值给传过来的属性 */
public function __set($name,$value)
{
$this->name = $value;
}
/* 魔术方法用于获取一个不存在的属性时返回自定义数据 */
public function __get($name)
{
/* 如果要获取的属性不存在或者不可访问(private protected)则返回一个null */
if(!isset($this->$name))
{
$this->$name = null;
}
}
}
上面的代码定义了一个留言实体类,定义了三个公有属性+两个魔术方法,在发送留言数据时会自动调用__set和__get魔术方法来设置属性的属性值.
gbookModel.php
<?php
require_once 'message.php';
class gbookModel {
private $bookPath;
private $data;
/* 设置文本写入路径(带文件名) */
public function setBookPath($bookPath)
{
//得到文本写入路径
$this->bookPath = $bookPath;
}
/* 得到留言文本文件的路径(带文件名) */
public function getBookPath()
{
return $this->bookPath;
}
/* open方法用于打开一个文件获得句柄 */
public function open()
{}
/* close方法用于关于打开的文件句柄 */
public function close()
{ }
/* read方法用于读取文件句柄的内容 */
public function read()
{
return file_get_contents($this->bookPath);
}
/* write方法用于将内容写入到一个句柄中 */
public function write($data)
{
/* 在数据写入之前,先对数据进行过滤处理 */
$this->data = self::safe($data)->name."&".self::safe($data)->email."\r\nsaid:\r\n".self::safe($data)->content."\r\n";
/* 将留言数据写入文本 */
return file_put_contents($this->bookPath, $this->data,FILE_APPEND);
}
/* 对数据对象进行处理 */
public static function safe($data)
{
/* object处理 */
$reflect =new ReflectionObject($data);
$props = $reflect->getProperties();
$messagebox = new stdClass();//stdClass是zend的一个保留类,可以在任何时候被new实例化,初始化时没有任何的属性和方法.
foreach ($props as $key=>$prop)
{
$ivar = $prop->getName();//得到传进来的对象的属性名字
/* 只是简单的对数据进行去除空白字符处理 */
$messagebox->$ivar = trim($prop->getValue($data));//对属性值进行处理后赋值给STDClass对象
}
return $messagebox;
}
/* 删除留言文本文件的内容并写入"it 's empty now " */
public function delete()
{
file_put_contents($this->bookPath, "it 's empty now.\n\r");
}
/* 分页显示内容 */
public function readByPage()
{
/* 打开文件获得一个数组(\n换行为分界符) */
$handle = file('test.txt');
$count = count($handle);//计算多少条留言内容
$page = isset($_GET['page']) ? $_GET['page'] : 1;//获得分页页面的num
//判断传过来的num数字是否小于1或者是否大于内容总数
if($page < 1 || $page > $count) $page = 1;
/* 每页显示9条 */
$pnum = 9 ;
/* 开始为(当前页-1)*每页显示 */
$begin = ($page-1) * $pnum;
/* 结束为(如果当前num+每页显示大于总数)则为总数的页码,否则为开始页+每页显示 */
$end = ($begin + $pnum) > $count ? $count:$begin+$pnum;
for ($i=$begin;$i<$end;$i++)
{
echo '<strong>(',$i+1,') </strong>',$handle[$i],'<br>';
}
for ($i=1;$i<=ceil($count/$pnum);$i++)
{
echo "<a href='?page=${i}'>${i}  </a>";
}
}
}
上面的php代码实现了将留言数据写入到指定存储介质(上面的例子是将数据写入到文本文件中)/删除/数据安全过滤/分页功能,并且使用了反射类对对象进行了反射(方便拿到属性=>属性值).其后将数据写入到文本中
leaveModel.php
<?php
require_once 'gbookModel.php';
class leaveModel
{
/** 接收一个gbookModel的对象和留言的文本内容 **/
public function write(gbookModel $gb,$data)
{
$book = $gb->getBookPath();//得到写入数据的文件路径
$gb->write($data);//将数据写入
//write log
}
}
将留言的数据传入gbookModel的write方法,用于写入.
authorControl.php
<?php
require_once 'leaveModel.php';
class authorControl
{
public function message(leaveModel $l,gbookModel $g,message $data)//如果传入的对象不是该接口的实例,则会报错,可以捕获异常
{
$l->write($g, $data);
}
public function view(gbookModel $g)
{
return $g->read();
}
public function delete(gbookModel $g)
{
$g->delete();
echo self::view($g).'<br>';
}
public function viewByPage(gbookModel $g)//传入对应接口的对象 ?
{
return $g->readByPage();
}
}
$message = new message();
$message->name = 'phper';//使用魔术方法进行写入对象属性
$message->email = 'phper@php.net';//使用魔术方法进行写入对象属性
$message->content = 'a crazy phper love php so much!!';//使用魔术方法进行写入对象属性
$gb = new authorControl();//self
$pen = new leaveModel();//leaveModel
$book = new gbookModel();//gbookModel
$book->setBookPath('test.txt');//设置保存路径
$gb->message($pen, $book, $message);//调用自己的message方法,传入三个接口对应的对象
echo $gb->viewByPage($book);
/* 删除文本内容 */
$gb->delete($book);
这是前端控制方法,在这里设置了留言者的名称,邮箱,留言内容,并把他们set到message类的属性中.也是IOC用的最多的一个类.简单的使用了IOC,其实我还是有点懵逼的,只是理解了那么一点点.如果有不足的地方请指出来.PS:以上例子出自PHP核心技术与最佳实践
一书.
IOC学习总结:
- IOC是面向对象设计五大原则之一(依赖倒置原则).
- 依赖注入在使用的时候,传入的对象必须是该接口的实例.一般用法是public function test(ClassName \(Object){}//\)Object必须是ClassName的实例,否则会爆一个
Catchable fatal error
级别的错误,这时候可以自己手动抛出异常并捕获. - PHP不是一个好的OOPL,但是是一个很好的WEB开发语言.
其实我理解的也不是很清晰,需要多多学习,也希望各位前辈能指出的理解错误的地方.
劝君莫惜金缕衣,劝君惜取少年时。
花开堪折直须折,莫待无花空折枝。