PHP中的反射API就像Java中的java.lang.reflect套件一樣。它由一系列可以分析屬性、方法和類別的內建類別組成。它在某些方面和物件函數相似,例如get_class_vars(),但是更加靈活,可以提供更多資訊。
反射API也可與PHP最新的物件導向特性一起運作,如存取控制、介面和抽象類別。舊的類別函數則不太容易與這些新特性一起使用。看過框架源碼的朋友應該對PHP的反射機制有一定的了解,像是依賴注入,物件池,類別加載,一些設計模式等等,都用到了反射機制。
反射API的部分類別
使用反射API這些類,可以獲得在執行時間存取物件、函數和腳本中的擴充功能的資訊.透過這些資訊可以用來分析類別或建構框架。
類別 | # 描述 |
Reflection | 為類別的摘要資訊提供靜態函數export() |
ReflectionClass | 類別資訊與工具 |
ReflectionMethod | 類別方法資訊與工具 |
ReflectionParameter | 方法參數資訊 |
ReflectionProperty | 類別屬性資訊 |
ReflectionFunction | 函數資訊與工具 |
ReflectionExtension | #PHP擴充資訊 |
ReflectionException | 錯誤類別 |
取得類別的資訊
我們在工作中使用過一些用於檢查類別屬性的函數,例如:get_class_methods、getProduct等。這些方法對獲取詳細類別資訊有很大的限制。
我們可以透過反射API類別:Reflection 和ReflectionClass 提供的靜態方法export 來獲取類別的相關信息, export 可以提供類別的幾乎所有的信息,包括屬性和方法的存取控制狀態、每個方法需要的參數以及每個方法在腳本文件中的位置。 這兩個工具類, export 靜態方法輸出結果是一致的,只是使用方式不同。
首先,建立一個簡單的類別
<?php class Student { public $name; protected $age; private $sex; public function __construct($name, $age, $sex) { $this->setName($name); $this->setAge($age); $this->setSex($sex); } public function setName($name) { $this->name = $name; } protected function setAge($age) { $this->age = $age; } private function setSex($sex) { $this->sex = $sex; } }
#使用ReflectionClass::export() 取得類別資訊
ReflectionClass::export('Student');
列印結果:
Class [ class Student ] { @@ D:\wamp\www\test2.php 3-29 - Constants [0] { } - Static properties [0] { } - Static methods [0] { } - Properties [3] { Property [ public $name ] Property [ protected $age ] Property [ private $sex ] } - Methods [4] { Method [ public method __construct ] { @@ D:\wamp\www\test2.php 8 - 13 - Parameters [3] { Parameter #0 [ $name ] Parameter #1 [ $age ] Parameter #2 [ $sex ] } } Method [ public method setName ] { @@ D:\wamp\www\test2.php 15 - 18 - Parameters [1] { Parameter #0 [ $name ] } } Method [ protected method setAge ] { @@ D:\wamp\www\test2.php 20 - 23 - Parameters [1] { Parameter #0 [ $age ] } } Method [ private method setSex ] { @@ D:\wamp\www\test2.php 25 - 28 - Parameters [1] { Parameter #0 [ $sex ] } } } }
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::isIterateable — 检查是否可迭代(iterateable) ReflectionClass::isSubclassOf — 检查是否为一个子类 ReflectionClass::isTrait — 返回了是否为一个 trait ReflectionClass::isUserDefined — 检查是否由用户定义的 ReflectionClass::newInstance — 从指定的参数创建一个新的类实例 ReflectionClass::newInstanceArgs — 从给出的参数创建一个新的类实例。 ReflectionClass::newInstanceWithoutConstructor — 创建一个新的类实例而不调用它的构造函数 ReflectionClass::setStaticPropertyValue — 设置静态属性的值 ReflectionClass::__toString — 返回 ReflectionClass 对象字符串的表示形式。
使用Reflection::export() 取得類別資訊
$prodClass = new ReflectionClass('Student'); Reflection::export($prodClass);
列印結果
Class [ class Student ] { @@ D:\wamp\www\test2.php 3-29 - Constants [0] { } - Static properties [0] { } - Static methods [0] { } - Properties [3] { Property [ public $name ] Property [ protected $age ] Property [ private $sex ] } - Methods [4] { Method [ public method __construct ] { @@ D:\wamp\www\test2.php 8 - 13 - Parameters [3] { Parameter #0 [ $name ] Parameter #1 [ $age ] Parameter #2 [ $sex ] } } Method [ public method setName ] { @@ D:\wamp\www\test2.php 15 - 18 - Parameters [1] { Parameter #0 [ $name ] } } Method [ protected method setAge ] { @@ D:\wamp\www\test2.php 20 - 23 - Parameters [1] { Parameter #0 [ $age ] } } Method [ private method setSex ] { @@ D:\wamp\www\test2.php 25 - 28 - Parameters [1] { Parameter #0 [ $sex ] } } } }
建立ReflectionClass物件後,就可以使用Reflection 工具類別輸出Student 類別的相關資訊。 Reflection::export() 可以格式化並輸出任何實作 Reflector 介面的類別的實例。
檢查類別
前面我們了解的 ReflectionClass 工具類,知道此類提供了很多的工具方法用於取得類別的資訊。例如,我們可以取得Student 類別的類型,是否可以實例化
工具函數
function classData(ReflectionClass $class) { $details = ''; $name = $class->getName(); // 返回要检查的类名 if ($class->isUserDefined()) { // 检查类是否由用户定义 $details .= "$name is user defined" . PHP_EOL; } if ($class->isInternal()) { // 检查类是否由扩展或核心在内部定义 $details .= "$name is built-in" . PHP_EOL; } if ($class->isInterface()) { // 检查类是否是一个接口 $details .= "$name is interface" . PHP_EOL; } if ($class->isAbstract()) { // 检查类是否是抽象类 $details .= "$name is an abstract class" . PHP_EOL; } if ($class->isFinal()) { // 检查类是否声明为 final $details .= "$name is a final class" . PHP_EOL; } if ($class->isInstantiable()) { // 检查类是否可实例化 $details .= "$name can be instantiated" . PHP_EOL; } else { $details .= "$name can not be instantiated" . PHP_EOL; } return $details; } $prodClass = new ReflectionClass('Student'); print classData($prodClass);
列印結果
Student is user defined Student can be instantiated
除了取得類別的相關信息,還可以取得ReflectionClass物件提供自訂類別所在的檔案名稱及檔案中類別的起始和終止行等相關原始程式碼資訊。
function getClassSource(ReflectionClass $class) { $path = $class->getFileName(); // 获取类文件的绝对路径 $lines = @file($path); // 获得由文件中所有行组成的数组 $from = $class->getStartLine(); // 提供类的起始行 $to = $class->getEndLine(); // 提供类的终止行 $len = $to - $from + 1; return implode(array_slice($lines, $from - 1, $len)); } $prodClass = new ReflectionClass('Student'); var_dump(getClassSource($prodClass));
列印結果
string 'class Student { public $name; protected $age; private $sex; public function __construct($name, $age, $sex) { $this->setName($name); $this->setAge($age); $this->setSex($sex); } public function setName($name) { $this->name = $name; } protected function setAge($age) { $this->age = $age; } private function setSex($sex) { $this->sex = $sex; } } ' (length=486)
我們看到 getClassSource 接受一個 ReflectionClass 物件作為它的參數,並傳回對應類別的原始程式碼。該函數忽略了錯誤處理,在實際中應該要檢查參數和結果程式碼!
檢查方法
類似於檢查類,ReflectionMethod 物件可以用來檢查類別中的方法。
取得 ReflectionMethod 物件的方法有兩種:
第一種是透過 ReflectionClass::getMethods() 取得ReflectionMethod 物件的陣列,這種方式的好處就是不用事先知道方法名,會傳回類別中所有方法的 ReflectionMethod 物件。
第二種是直接使用 ReflectionMethod 類別實例化對象,這種方式只能取得一個類別方法對象,需要事先知道方法名稱。
ReflectionMethod 物件的工具方法:
ReflectionMethod::__construct — ReflectionMethod 的建構子
ReflectionMethod::export — 輸出一個回呼方法
ReflectionMethod::getClosure —傳回一個動態建立的方法呼叫接口,譯者註:可以使用這個回傳值直接呼叫非公開方法。
ReflectionMethod::getDeclaringClass — 取得反射函數呼叫參數的類別表達
ReflectionMethod::getModifiers — 取得方法的修飾符
ReflectionMethod::getPrototype — 傳回方法原型(如果存在)
ReflectionMethod:: invoke — Invoke
ReflectionMethod::invokeArgs — 帶參數執行
ReflectionMethod::isAbstract — 判斷方法是否為抽象方法
ReflectionMethod::isConstructor — 判斷方法是否是建構方法
Reflection::isDestructor —Method判斷方法是否為析構方法
ReflectionMethod::isFinal — 判斷方法是否定義final
ReflectionMethod::isPrivate — 判斷方法是否為私有方法
ReflectionMethod::isProtected — 判斷方法是否為保護方法(protected )
ReflectionMethod::isPublic — 判斷方法是否為公開方法
ReflectionMethod::isStatic — 判斷方法是否為靜態方法
ReflectionMethod::setAccessible — 設定方法是否存取
ReflectionMethod::__toString — 傳回反射方法物件的字串表達式
ReflectionClass::getMethods()
我們可以透過ReflectionClass::getMethods() 來取得ReflectionMethod 物件的陣列。
$prodClass = new ReflectionClass('Student'); $methods = $prodClass->getMethods(); var_dump($methods);
列印結果
array (size=4) 0 => & object(ReflectionMethod)[2] public 'name' => string '__construct' (length=11) public 'class' => string 'Student' (length=7) 1 => & object(ReflectionMethod)[3] public 'name' => string 'setName' (length=7) public 'class' => string 'Student' (length=7) 2 => & object(ReflectionMethod)[4] public 'name' => string 'setAge' (length=6) public 'class' => string 'Student' (length=7) 3 => & object(ReflectionMethod)[5] public 'name' => string 'setSex' (length=6) public 'class' => string 'Student' (length=7)
可以看到我們取得了Student 的 ReflectionMethod 物件數組,每個元素是一個對象,其中有兩個公共的屬性,name 為方法名,class 為所屬類。我們可以呼叫物件方法來取得方法的資訊。
ReflectionMethod
直接使用 ReflectionMethod 類別取得類別方法有關資訊
$method = new ReflectionMethod('Student', 'setName'); var_dump($method);
列印結果
object(ReflectionMethod)[1] public 'name' => string 'setName' (length=7) public 'class' => string 'Student' (length=7)
注意
#在PHP5中,如果被检查的方法只返回对象(即使对象是通过引用赋值或传递的),那么 ReflectionMethod::retursReference() 不会返回 true。只有当被检测的方法已经被明确声明返回引用(在方法名前面有&符号)时,ReflectionMethod::returnsReference() 才返回 true。
检查方法参数
在PHP5中,声明类方法时可以限制参数中对象的类型,因此检查方法的参数变得非常必要。
类似于检查方法,ReflectionParameter 对象可以用于检查类中的方法,该对象可以告诉你参数的名称,变量是否可以按引用传递,还可以告诉你参数类型提示和方法是否接受空值作为参数。
获得 ReflectionParameter 对象的方法有同样两种,这和获取 ReflectionMethod 对象非常类似:
第一种是通过 ReflectionMethod::getParameters() 方法返回 ReflectionParameter 对象数组,这种方法可以获取到一个方法的全部参数对象。
第二种是直接使用 ReflectionParameter 类实例化获取对象,这种方法只能获取到单一参数的对象。
ReflectionParameter 对象的工具方法:
ReflectionParameter::allowsNull — Checks if null is allowed ReflectionParameter::canBePassedByValue — Returns whether this parameter can be passed by value ReflectionParameter::__clone — Clone ReflectionParameter::__construct — Construct ReflectionParameter::export — Exports ReflectionParameter::getClass — Get the type hinted class ReflectionParameter::getDeclaringClass — Gets declaring class ReflectionParameter::getDeclaringFunction — Gets declaring function ReflectionParameter::getDefaultValue — Gets default parameter value ReflectionParameter::getDefaultValueConstantName — Returns the default value's constant name if default value is constant or null ReflectionParameter::getName — Gets parameter name ReflectionParameter::getPosition — Gets parameter position ReflectionParameter::getType — Gets a parameter's type ReflectionParameter::hasType — Checks if parameter has a type ReflectionParameter::isArray — Checks if parameter expects an array ReflectionParameter::isCallable — Returns whether parameter MUST be callable ReflectionParameter::isDefaultValueAvailable — Checks if a default value is available ReflectionParameter::isDefaultValueConstant — Returns whether the default value of this parameter is constant ReflectionParameter::isOptional — Checks if optional ReflectionParameter::isPassedByReference — Checks if passed by reference ReflectionParameter::isVariadic — Checks if the parameter is variadic ReflectionParameter::__toString — To string
ReflectionMethod::getParameters()
同获取方法,此方法会返回一个数组,包含方法每个参数的 ReflectionParameter 对象
$method = new ReflectionMethod('Student', 'setName'); $params = $method->getParameters(); var_dump($params);
打印结果
array (size=1) 0 => & object(ReflectionParameter)[2] public 'name' => string 'name' (length=4)
ReflectionParameter
我们来了解一下这种方式,为了更好的理解,我修改一下 Student 类的 setName方法,增加两个参数 a, b
... public function setName($name, $a, $b) { $this->name = $name; } ...
首先我们看一下 ReflectionParameter 类的构造方法
public ReflectionParameter::__construct ( string $function , string $parameter )
可以看到该类实例化时接收两个参数:
$function:当需要获取函数为公共函数时只需传函数名称即可。当该函数是某个类方法时,需要传递一个数组,格式为:array('class', 'function')。
$parameter:这个参数可以传递两种,第一种为参数名(无$符号),第二种为参数索引。注意:无论是参数名还是索引,该参数都必须存在,否则会报错。
下面举例:
$params = new ReflectionParameter(array('Student', 'setName'), 1); var_dump($params);
打印结果
object(ReflectionParameter)[1] public 'name' => string 'a' (length=1)
我们再定义一个函数测试一下
function foo($a, $b, $c) { } $reflect = new ReflectionParameter('foo', 'c'); var_dump($reflect);
打印结果
object(ReflectionParameter)[2] public 'name' => string 'c' (length=1)
结语
php的反射API功能非常的强大,它可以将一个类的详细信息获取出来。我们可以通过反射API编写个类来动态调用Module对象,该类可以自由加载第三方插件并集成进已有的系统。而不需要把第三方的代码硬编码进原有的代码中。虽然实际开发中使用反射情况比较少,但了解反射API对工作中对代码结构的了解和开发业务模式帮助还是非常大的。此篇博文断断续续的写了很久(主要就是懒!),如有错误与不足欢迎指正,建议!!
相关教程推荐:《PHP教程》
以上是深入了解PHP反射API!的詳細內容。更多資訊請關注PHP中文網其他相關文章!