【反射】PHP的反射機制【原創】

摘要:主要是參考列旭鬆、陳文著的《PHP核心技術與最佳實踐》的1.5節。

1.1 定義

反射,直觀理解就是根據到達地找到出發地和來源。比如說,給你一個光禿禿的對象,可以僅僅通過這個對象就能知道它所屬的類以及擁有的方法。
反射,指在PHP運行狀態中,擴展分析PHP程序,導出或提取出關於類、方法、屬性、參數等的詳細信息,包括註釋。這種動態獲取信息以及動態調用對象方法的功能稱爲反射API。

1.2 獲取對象屬性和方法

getMethods和getProperties分別用來獲取對象的所有方法和所有屬性,返回對象數組,然後通過getName來獲取具體的方法和屬性即可。但都是必須先通過反射獲取類的原型, 即使用$reflect = new ReflectionObject($student);來獲取對象的原型。
下面是使用反射API來獲取對象的屬性和方法:
運行:
Setting age to 24
Tom is male

對象的屬性有:
name
gender
age
Array
(
    [0] => ReflectionProperty Object
        (
            [name] => name
            [class] => Person3
        )

    [1] => ReflectionProperty Object
        (
            [name] => gender
            [class] => Person3
        )

    [2] => ReflectionProperty Object
        (
            [name] => age
            [class] => Person3
        )

)

對象的方法有:
say
__set
__get
Array
(
    [0] => ReflectionMethod Object
        (
            [name] => say
            [class] => Person3
        )

    [1] => ReflectionMethod Object
        (
            [name] => __set
            [class] => Person3
        )

    [2] => ReflectionMethod Object
        (
            [name] => __get
            [class] => Person3
        )

)
------------------分隔線-----------------
對象屬性的關聯數組:
Array
(
    [name] => Tom
    [gender] => male
    [age] => 24
)
對象屬性列表所屬的類:
Person3類屬性:
Array
(
    [name] =>
    [gender] =>
)
類的方法名組成的數組:
Array
(
    [0] => say
    [1] => __set
    [2] => __get
)



1.3 還原類的原型

既然上面已經可以使用反射來獲取對象的屬性和方法了,那麼再進一步,獲取方法和屬性的訪問權限,那麼就可以根據對象來獲取類的原型了:
PHP手冊中關於反射API的有很多,可以說,反射完整的描述了一個類或者對象的原型。反射不僅可以用於類和對象,還可以用於函數、擴展模塊、異常等。

1.4 反射的invoke方法

invoke方法是個很實用的方法,用來執行一個反射的方法:
<?php
/**
 * 使用反射API的invoke方法來執行反射的方法
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2017/7/13
 * Time: 18:23
 */

class HelloWorld
{
    public function sayHelloTo($name) {
        return 'Hello ' . $name;
    }

}

// 獲取反射的方法
$method = new ReflectionMethod('HelloWorld', 'sayHelloTo');
/** 上面的代碼和下面註釋的代碼作用是一樣的   */
//$reflectionClass = new ReflectionClass('HelloWorld');
//$method = $reflectionClass->getMethod('sayHelloTo');

// 執行一個反射的方法
echo $method->invoke(new HelloWorld(), 'Mike');    // Hello Mike


其中的:
作用相當於:
$reflectionClass = new ReflectionClass('HelloWorld');
$method = $reflectionClass->getMethod('sayHelloTo');



1.5 動態代理

使用反射的invoke方法,可以實現簡單的動態代理:
運行:
方法前攔截記錄 Log
已經連接到數據庫member
方法後攔截
方法前攔截記錄 Log
已經連接到數據庫lottery
方法後攔截

這裏簡單說明一下,真正的操作類是MySql類,但SqlProxy類實現了根據動態傳入參數,代替實際的類MySql類的運行,並且在方法運行前後進行攔截,並且可以動態改變類中的方法和屬性,這就是簡單的動態代理。

1.6 反射的作用

反射的用處:
  • 用於文檔生成,因此可以用它對文件裏的類進行掃描,逐個生成描述文檔。
  • 用來做hook實現插件功能
  • 動態代理

在平常開發中,用到反射的地方很有限,主要有兩個地方,一個是對對象進行調試,另一個是獲取類的信息。而在MVC和插件開發中,使用反射很常見,但是反射的消耗也很大,在可以找到替代方案的情況下,不要濫用反射。

很多時候,善用反射能夠保持代碼的優雅和簡潔,但反射也會破壞類的封裝性,因爲反射可以使本不應該暴露的方法或者屬性被強制暴露了出來,這既是優點也是缺點。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章