PHP DIY 系列------基礎篇:3. 反射

13sai發表於2020-02-18

反射,直觀理解就是根據到達地找到出發地和來源。 反射指在PHP執行狀態中,擴充套件分析PHP程式,匯出或提出關於類、方法、屬性、引數等的詳細資訊,包括註釋。這種動態獲取資訊以及動態呼叫物件方法的功能稱為反射API。

不妨先來看一個demo:

<?php
/**
 * Author: sai
 * Date: 2019/10/21
 * Time: 10:59
 */

function p($msg, $var)
{
    echo("<pre style='position:relative;
z-index:999;
padding:10px;
border-radius:5px;
background:#f5f5f5;
border:1px solid #aaa;
font-size:14px;
line-height:18px;
opacity:0.8;'>".$msg.":".print_r($var, true)."</pre>");
}


class Demo
{
    private $id;

    protected $name;

    public $skills = [];

    public function __construct($id, $name, $skills = [])
    {
        $this->id = $id;
        $this->name = $name;
        $this->skills = $skills;
    }

    public function getName()
    {
        return $this->name;
    }
    public function getSkill()
    {
        p('skill', $this->skills);
    }
}


$ref = new ReflectionClass('Demo');
if ($ref->isInstantiable()) {
    p('檢查類是否可例項化isInstantiable', null );
}
$constructor = $ref->getConstructor();
p('獲取建構函式getConstructor', $constructor);

$parameters = $constructor->getParameters();
foreach ($parameters as $param) {
    p('獲取引數getParameters', $param);
}

if ($ref->hasProperty('name')) {
    $attr = $ref->getProperty('name');
    p('獲取屬性getProperty', $attr);
}

$attributes = $ref->getProperties();
foreach ($attributes as $row) {
    p('獲取屬性列表getProperties', $row->getName() );
}

if ($ref->hasMethod('getSkill')) {
    $method = $ref->getMethod('getSkill');
    p('獲取方法getMethod', $method);
}

$methods = $ref->getMethods();
foreach ($methods as $row) {
    p('獲取方法列表getMethods', $row->getName());
}

$instance = $ref->newInstanceArgs([1, 'sai', ['php', 'js']]);
p('newInstanceArgs', $instance);

輸出:

檢查類是否可例項化isInstantiable:
獲取建構函式getConstructor:ReflectionMethod Object
(
    [name] => __construct
    [class] => Demo
)

獲取引數getParameters:ReflectionParameter Object
(
[name] => id
)
獲取引數getParameters:ReflectionParameter Object
(
[name] => name
)
獲取引數getParameters:ReflectionParameter Object
(
[name] => skills
)
獲取屬性getProperty:ReflectionProperty Object
(
[name] => name
[class] => Demo
)
獲取屬性列表getProperties:id
獲取屬性列表getProperties:name
獲取屬性列表getProperties:skills
獲取方法getMethod:ReflectionMethod Object
(
[name] => getSkill
[class] => Demo
)
獲取方法列表getMethods:__construct
獲取方法列表getMethods:getName
獲取方法列表getMethods:getSkill
newInstanceArgs:Demo Object
(
[idprivate] => 1
[name:protected] => sai
[skills] => Array
(
[0] => php
[1] => js
)

)

demo裡面就有使用了ReflectionClass類,當然ReflectionClass類不止於這些方法。

更多方法

ReflectionClass類還有更多方法:

方法 說明
ReflectionClass::__construct 初始化 ReflectionClass 類
ReflectionClass::export 匯出一個類
ReflectionClass::getConstant 獲取定義過的一個常量
ReflectionClass::getConstants 獲取一組常量
ReflectionClass::getConstructor 獲取類的建構函式
ReflectionClass::getDefaultProperties 獲取預設屬性
ReflectionClass::getDocComment 獲取文件註釋
ReflectionClass::getEndLine 獲取最後一行的行數
ReflectionClass::getExtension 根據已定義的類獲取所在擴充套件的 ReflectionExtension 物件
ReflectionClass::getExtensionName 獲取定義的類所在的擴充套件的名稱
ReflectionClass::getFileName 獲取定義類的檔名
ReflectionClass::getInterfaceNames 獲取介面(interface)名稱
ReflectionClass::getInterfaces 獲取介面
ReflectionClass::getMethod 獲取一個類方法的 ReflectionMethod。
ReflectionClass::getMethods 獲取方法的陣列
ReflectionClass::getModifiers 獲取類的修飾符
ReflectionClass::getName 獲取類名
ReflectionClass::getNamespaceName 獲取名稱空間的名稱
ReflectionClass::getParentClass 獲取父類
ReflectionClass::getProperties 獲取一組屬性
ReflectionClass::getProperty 獲取類的一個屬性的 ReflectionProperty
ReflectionClass::getReflectionConstant Gets a ReflectionClassConstant for a class’s constant
ReflectionClass::getReflectionConstants Gets class constants
ReflectionClass::getShortName 獲取短名
ReflectionClass::getStartLine 獲取起始行號
ReflectionClass::getStaticProperties 獲取靜態(static)屬性
ReflectionClass::getStaticPropertyValue 獲取靜態(static)屬性的值
ReflectionClass::getTraitAliases 返回 trait 別名的一個陣列
ReflectionClass::getTraitNames 返回這個類所使用 traits 的名稱的陣列
ReflectionClass::getTraits 返回這個類所使用的 traits 陣列
ReflectionClass::hasConstant 檢查常量是否已經定義
ReflectionClass::hasMethod 檢查方法是否已定義
ReflectionClass::hasProperty 檢查屬性是否已定義
ReflectionClass::implementsInterface 介面的實現
ReflectionClass::inNamespace 檢查是否位於名稱空間中
ReflectionClass::isAbstract 檢查類是否是抽象類(abstract)
ReflectionClass::isAnonymous 檢查類是否是匿名類
ReflectionClass::isCloneable 返回了一個類是否可複製
ReflectionClass::isFinal 檢查類是否宣告為 final
ReflectionClass::isInstance 檢查類的例項
ReflectionClass::isInstantiable 檢查類是否可例項化
ReflectionClass::isInterface 檢查類是否是一個介面(interface)
ReflectionClass::isInternal 檢查類是否由擴充套件或核心在內部定義
ReflectionClass::isIterable Check whether this class is iterable
ReflectionClass::isIterateable 檢查是否可迭代(iterateable)
ReflectionClass::isSubclassOf 檢查是否為一個子類
ReflectionClass::isTrait 返回了是否為一個 trait
ReflectionClass::isUserDefined 檢查是否由使用者定義的
ReflectionClass::newInstance 從指定的引數建立一個新的類例項
ReflectionClass::newInstanceArgs 從給出的引數建立一個新的類例項。
ReflectionClass::newInstanceWithoutConstructor 建立一個新的類例項而不呼叫它的建構函式
ReflectionClass::setStaticPropertyValue 設定靜態屬性的值
ReflectionClass::__toString 返回 ReflectionClass 物件字串的表示形式。

除去強大的ReflectionClass,還有Reflection、ReflectionClassConstant 、ReflectionMethod 、ReflectionFunctionAbstract等等。建議檢視手冊:

反射的實際應用

  1. 反射可以用於文件、檔案生成。可以用它對檔案裡的類進行掃描,逐個生成描述文件;
  2. 既然反射可以探知類的內部結構,那麼可以用它做hook實現外掛功能;
  3. 可以用於做動態代理,在未知或者不確定類名的情況下,動態生成和例項化一些類和執行方法;
  4. 依賴注入。對於多次繼承的類,我們可以通過多次反射探索到基類的結構,或者採用遞迴的形式反射,實現例項化所有繼承類,這也是PHP依賴注入的原理。

反射的優點

  1. 支援反射的語言提供了一些在低階語言中難以實現的執行時特性。
  2. 可以在一定程度上避免硬編碼,提供靈活性和通用性。
  3. 可以作為一個第一類物件發現並修改原始碼的結構(如程式碼塊、類、方法、協議等)。
  4. 可以在執行時像對待原始碼語句一樣計算符號語法的字串(類似JavaScript的eval()函式),進而可將跟class或function匹配的字串轉換成class或function的呼叫或引用。
  5. 可以建立一個新的語言位元組碼直譯器來給程式設計結構一個新的意義或用途。

反射的缺點

  1. 學習成本高。面向反射的程式設計需要較多的高階知識,包括框架、關係對映和物件互動,以利用更通用的程式碼執行
  2. 同樣因為反射的概念和語法都比較抽象,過多地濫用反射技術會使得程式碼難以被其他人讀懂,不利於合作與交流
  3. 反射在提高了程式碼靈活性的同時,犧牲了一點點執行效率,有一定的消耗
  4. 反射也會破壞類的封裝性,把本不該暴露的方法或屬性暴露了出來

在平時的開發中,我們用到反射其實不多,為什麼把它拿到這裡來說呢?一來是我們後面會使用到反射去實現Ioc容器,二來反射也是PHP核心功能之一,在我們流行的框架中十分常見,理解它是很有必要的。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

分享開發知識,歡迎交流。qq957042781

相關文章