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,')&ensp;</strong>',$handle[$i],'<br>';
        }
        for ($i=1;$i<=ceil($count/$pnum);$i++)
        {
            echo "<a href='?page=${i}'>${i}&ensp;&ensp;</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开发语言.

其实我理解的也不是很清晰,需要多多学习,也希望各位前辈能指出的理解错误的地方.

posted @ 2018-04-22 12:11  Masker。  阅读(118)  评论(0编辑  收藏  举报