PHP反射機制簡單理解
什麼是反射呢?
在PHP的面向物件程式設計中的物件,它被系統賦予自省的能力,而這個自省的過程,我們把它叫做反射。
我們對反射的直觀理解可以是,根據達到地,找到出發地和來源這麼一個過程,通俗來講就是,我給你一個光禿禿的物件,完事你可以根據這個物件,知道它所屬的類,擁有哪些方法。
在PHP中,反射是指在PHP執行狀態中,擴充套件分析PHP程式,匯出或者提取出關於類、屬性、方法、引數等的詳細資訊,包括註釋。這種動態獲取資訊以及動態呼叫物件方法的功能,被稱為反射API。
我們接下來通過一段程式碼來感受下:
class person{ public $name; public $age; public function say() { echo $this->name."<br>".$this->age; } public function set($name,$value) { echo 'set name to value'; $this->$name = $value; } public function get($name) { if(!isset($this->$name)){ echo 'unset name'; $this->$name = 'seting~~~'; } return $this->$name; } } $stu = new person(); $stu->name = 'luyaran'; $stu->age = 26; $stu->sex = 'girl';
上述程式碼是一個簡單的類,我們通過例項化它,以及賦值,讓它含有意義。
完事,我們就來通過反射API獲取這個stu物件的方法和屬性的一個列表:
//獲取物件的屬性列表 $reflect = new ReflectionObject($stu); $props = $reflect->getProperties(); foreach ($props as $key_p => $value_p) { var_dump($value_p->getName()); } //獲取物件的方法列表 $method = $reflect->getMethods(); foreach ($method as $key_m => $value_m) { var_dump($value_m->getName()); }
除了反射API之外,我們還可以使用class函式來獲取物件的各種屬性以及方法的資料,如下:
// 獲取物件的屬性的關聯陣列
var_dump(get_object_vars($stu));
//獲取類屬性
var_dump(get_class_vars(get_class($stu)));
//獲取類的方法名稱組成的陣列
var_dump(get_class_methods(get_class($stu)));
值得一說的是,這個get_class這個函式,還可以獲取從其他頁面傳遞過來的物件的屬性列表以及所屬的類。
不過,class函式和反射API相比較起來,個人感覺還是後者更勝一籌啊。
反射API甚至可以還原這個類的原型,包括方法的訪問許可權,來看程式碼感受下:
//例項化反射API獲取類名
$obj = new ReflectionObject($stu);
$class_name = $obj->getName();
$method_arr = $props_arr = array();
//獲取物件的屬性列表
$props = $obj->getProperties();
foreach ($props as $key_p => $value_p) {
$props_arr[$value_p->getName()] = $value_p;
}
//獲取物件的方法列表
$method = $obj->getMethods();
foreach ($method as $key_m => $value_m) {
$method_arr[$value_m->getName()] = $value_m;
}
//格式化輸出類的屬性以及方法
echo "class $class_name { \n";
is_array($props_arr) && ksort($props_arr);
foreach ($props_arr as $key_o => $value_o) {
echo "\t";
echo $value_o->isPublic() ? 'public' : ' ' ,$value_o->isPrivate() ? 'private' : ' ' ,$value_o->isProtected() ? 'protected' : ' ' ,$value_o->isStatic() ? 'static' : ' ';
echo "\t$value_o\n";
}
echo "\n";
is_array($method_arr) && ksort($method_arr);
foreach ($method_arr as $key_e => $value_e) {
echo "\t";
echo $value_e->isPublic() ? 'public' : ' ' ,$value_e->isPrivate() ? 'private' : ' ' ,$value_e->isProtected() ? 'protected' : ' ';
echo "\tfunction $value_e () {} \n";
}
echo '}';
根據上述程式碼,輸出結果如下:
我們可以看到,上圖很詳細的輸出了這個類的構造。
不僅如此哦,PHP手冊中關於反射API的數量,多達幾十個,可以這麼說,反射完整的描述了一個類或者物件的原型。
同時呢,反射不僅可以用作類和物件,還可以用於函式,擴充套件模組,異常等。
咱們呢,在這裡就不贅述了,最後一點篇幅,就來聊聊反射的一些作用。
首先,它可以用作文件生成,所以,我們可以用它對文件中的類進行掃描,逐個生成掃描文件。
反射可以探知類的內部結構,也可以用作hook來實現外掛功能,還有就是可以做動態代理。
咱們來看段MySQL的動態代理的程式碼感受下:
class mysql{
public function connect($db_name)
{
echo "we will connect database $db_name \r\n";
}
}
class sql_proxy{
private $target;
public function __construct($tar)
{
$this->target = new $tar();
}
public function __call($name,$args)
{
$reflect = new ReflectionClass($this->target);
$method = $reflect->getMethods();
if ($method) {
foreach ($method as $key_method => $value_method) {
if($value_method->isPublic() && !$value_method->isAbstract()){
echo "方法前攔截記錄LOG\r\n";
$value_method->invoke();
echo "方法後攔截記錄LOG\r\n";
}
}
}
}
}
$obj = new sql_proxy('mysql');
$obj->coonect('luyaran');
上述程式碼真正的操作類是mysql,下面的sql_proxy只是根據動態傳入的引數,來代替了實際執行的類,並且可以在方法執行的前後進行攔截,還可以動態地改變類中的方法和屬性,這個可以叫做簡單的動態代理類。
在我們平常的開發中,用到反射的地方不多,一般是用來對物件進行除錯,還有就是獲取類的資訊,但是在MVC和外掛中,比較常見,並且反射的消耗也是不小的,我們在有另外一種方案的時候,儘量不要選擇反射。
我們還可以通過PHP中的token函式來實現簡單的反射功能,不過,從簡單靈活的角度來看,還是使用已有的反射API比較好。
很多時候,善用某個東西,會使得我們的程式碼,簡潔又優雅,但是不能貪多,比如這個反射API,用的多了,會破壞我們類的封裝性,使得本不應該暴露的方法暴露了出來,這是優點也是缺點,我們要搞搞清楚。
好啦,本次記錄就到這裡了。
如果感覺不錯的話,請多多點贊支援哦。。。