PHP反射與代理模式
阿新 • • 發佈:2018-12-30
面向物件程式設計中物件被賦予了自省的能力,而這自省的過程就是反射.
給出一個物件,我們可以通過該物件知道它所屬的類,有用的方法,屬性.
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類的程式碼量被固定下來,不會因為業務的逐漸龐大而龐大;解耦,通過引數就可以判斷真實類,不需要事先例項化,更加靈活多變。