手寫PHP API框架(三)之反射介紹

2023-02-10 22:01:06

上一篇《》文章中我們介紹了Composer的安裝使用,這一文我們來介紹一下有關反射的概念介紹。

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

不妨先來看一個demo:

<?php


function p($msg, $var)
{
    echo($msg.":".print_r($var, true)).PHP_EOL.PHP_EOL;
}


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);
登入後複製

輸出:

➜  php git:(main) php reflect.php 

檢查類是否可範例化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
(
    [id:Demo:private] => 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::getReflectionConstantGets a ReflectionClassConstant for a class's constant
ReflectionClass::getReflectionConstantsGets 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::isIterableCheck 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等等。建議檢視手冊:

反射的實際應用

  • 反射可以用於檔案、檔案生成。可以用它對檔案裡的類進行掃描,逐個生成描述檔案;

  • 既然反射可以探知類的內部結構,那麼可以用它做hook實現外掛功能;

  • 可以用於做動態代理,在未知或者不確定類名的情況下,動態生成和範例化一些類和執行方法;

  • 依賴注入。對於多次繼承的類,我們可以通過多次反射探索到基礎類別的結構,或者採用遞迴的形式反射,實現範例化所有繼承類,這也是PHP依賴注入的原理。

反射的優點

  • 支援反射的語言提供了一些在低階語言中難以實現的執行時特性。

  • 可以在一定程度上避免寫死,提供靈活性和通用性。

  • 可以作為一個第一類物件發現並修改原始碼的結構(如程式碼塊、類、方法、協定等)。

  • 可以在執行時像對待原始碼語句一樣計算符號語法的字串(類似JavaScript的eval()函數),進而可將跟class或function匹配的字串轉換成class或function的呼叫或參照。

  • 可以建立一個新的語言位元組碼直譯器來給程式設計結構一個新的意義或用途。

反射的缺點

  • 學習成本高。面向反射的程式設計需要較多的高階知識,包括框架、關係對映和物件互動,以利用更通用的程式碼執行

  • 同樣因為反射的概念和語法都比較抽象,過多地濫用反射技術會使得程式碼難以被其他人讀懂,不利於合作與交流

  • 反射在提高了程式碼靈活性的同時,犧牲了一點點執行效率,有一定的消耗

  • 反射也會破壞類的封裝性,把本不該暴露的方法或屬性暴露了出來

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

這一節是比較獨立的,在後面的章節中我們會使用它。

推薦學習:《》

以上就是手寫PHP API框架(三)之反射介紹的詳細內容,更多請關注TW511.COM其它相關文章!