反射,直觀理解就是根據到達地找到出發地和來源。 反射指在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等等。建議檢視手冊:
反射的實際應用
- 反射可以用於文件、檔案生成。可以用它對檔案裡的類進行掃描,逐個生成描述文件;
- 既然反射可以探知類的內部結構,那麼可以用它做hook實現外掛功能;
- 可以用於做動態代理,在未知或者不確定類名的情況下,動態生成和例項化一些類和執行方法;
- 依賴注入。對於多次繼承的類,我們可以通過多次反射探索到基類的結構,或者採用遞迴的形式反射,實現例項化所有繼承類,這也是PHP依賴注入的原理。
反射的優點
- 支援反射的語言提供了一些在低階語言中難以實現的執行時特性。
- 可以在一定程度上避免硬編碼,提供靈活性和通用性。
- 可以作為一個第一類物件發現並修改原始碼的結構(如程式碼塊、類、方法、協議等)。
- 可以在執行時像對待原始碼語句一樣計算符號語法的字串(類似JavaScript的eval()函式),進而可將跟class或function匹配的字串轉換成class或function的呼叫或引用。
- 可以建立一個新的語言位元組碼直譯器來給程式設計結構一個新的意義或用途。
反射的缺點
- 學習成本高。面向反射的程式設計需要較多的高階知識,包括框架、關係對映和物件互動,以利用更通用的程式碼執行
- 同樣因為反射的概念和語法都比較抽象,過多地濫用反射技術會使得程式碼難以被其他人讀懂,不利於合作與交流
- 反射在提高了程式碼靈活性的同時,犧牲了一點點執行效率,有一定的消耗
- 反射也會破壞類的封裝性,把本不該暴露的方法或屬性暴露了出來
在平時的開發中,我們用到反射其實不多,為什麼把它拿到這裡來說呢?一來是我們後面會使用到反射去實現Ioc容器,二來反射也是PHP核心功能之一,在我們流行的框架中十分常見,理解它是很有必要的。
本作品採用《CC 協議》,轉載必須註明作者和本文連結