1. 程式人生 > >PHP反射與代理模式

PHP反射與代理模式

面向物件程式設計中物件被賦予了自省的能力,而這自省的過程就是反射.
給出一個物件,我們可以通過該物件知道它所屬的類,有用的方法,屬性.

PHP反射API

下面通過反射的API獲取person類的原型

<?php
    //構造一個類
    class person{
        public $name;
        public $gender;
        public function say(){

        }
        public function __set($name,$v){

        }
        public function
__get($name){
} } $obj = new ReflectionClass('person');//獲取類 $classname = $obj->getName(); $methods = $properties = array(); foreach($obj->getProperties() as $p)//獲取類的屬性 { $properties[$p->getName()] = $p; } foreach($obj->getMethods() as $p)//獲取類的方法
{ $methods[$p->getName()] = $p; } echo "class {$classname} {<br>"; is_array($properties) && ksort($properties);//如果屬性陣列不為空,就根據key排序 foreach($properties as $key => $v) { echo "\t"; echo $v->isPublic()?'public':'', $v
->isPrivate()?'private':'', $v->isProtected()?'protected':'', $v->isStatic()?'static':''; echo "\t{$key}<br>"; } echo "<br>"; if(is_array($methods)) ksort($methods); foreach($methods as $key => $v) { echo "\tfunction {$key}(){}<br>"; } echo "}";
//輸出如下
class person {
public  gender
public  name

function __get(){}
function __set(){}
function say(){}
}

反射有什麼用

一個常見的應用是實現動態代理和文件生成.
我們先說說靜態代理

<?php
    interface work{
        function todo();
    }
    //委託類
    class realobj implements work{
        function todo(){
            echo "realobj do something";
        }
    }
    //代理類,其中target就是委託類的引用,通過依賴注入或者手動建立
    class staticproxy implements work{
        public $target ;
        function __construct(){
            $this->target = new realobj();
        }
        function todo(){
            $this->target->todo();
        }
    }

    //測試
    $proxy = new staticproxy();
    $proxy->todo();

靜態代理就是以上的模式,他可以隱藏委託類的實現;可以實現客戶與委託類間的解耦,在不修改委託類程式碼的情況下能夠做一些額外的處理.但是靜態代理這個模式本身有個大問題,如果類方法數量越來越多的時候,代理類的程式碼量是十分龐大的。所以引入動態代理來解決此類問題。

動態代理:

<?php
interface work{
    function todo();
}
interface run{
    function run();
}
//委託類
class realobj implements work{
    function todo(){
        echo "realobj do something<br>";
    }
}
class realobj2 implements run{
    function run(){
        echo "i can run <br>";
    }
}
class proxy{
    public $target;
    public function bind($tar){
        $this->target = new $tar();
    }
    function __call($name,$args){
        if(!isset($this->target))
        {
            exit("未繫結委託類");
        }
        $r = new ReflectionClass($this->target);
        if($method = $r->getMethod($name))
        {
            if(!$method->isAbstract() && $method->isPublic())//查詢target內有沒有這個方法
            {
                echo "在呼叫方法前可以做一些操作<br>";
                $method->invoke($this->target,$args);//呼叫target的方法
                echo "在呼叫方法後可以做一些操作<br>";
            }
        }
    }
}

$obj = new proxy();
$obj->bind('realobj');//繫結委託類
$obj->todo();
$obj->bind('realobj2');
$obj->run();

輸出:

在呼叫方法前可以做一些操作
realobj do something
在呼叫方法後可以做一些操作
在呼叫方法前可以做一些操作
i can run 
在呼叫方法後可以做一些操作

明顯,Proxy類的程式碼量被固定下來,不會因為業務的逐漸龐大而龐大;解耦,通過引數就可以判斷真實類,不需要事先例項化,更加靈活多變。