說起反射ApI,自我感覺PHP中的反射ApI和java中的java.lang.reflect包差不多,都是由可以列印和分析類成員屬性、方法的一組內建類組成的。可能你已經學習過對象函數比如:get_class_vars()但是使用反射API會更加的靈活、輸出資訊會更加詳細。
首先我們需要知道,反射API不僅僅是用來檢查類的,它本身包括一組類,用來完成各種功能:常用的類如下:
| Reflection類 |
可以列印類的基本資料,(通過提供的靜態export()函數) |
| ReflectionMethod類 |
見名知意,列印類方法、得到方法的具體資訊等 |
| ReflectionClass類 |
用於得到類資訊,比如得到類包含的方法,類本的屬性,是不是抽象類別等 |
| ReflectionParameter類 |
顯示參數的資訊,可以動態得到已知方法的傳參情況 |
| ReflectionException類 |
用於顯示錯誤資訊 |
| ReflectionExtension類 |
得到PHP的擴充資訊,可以判斷擴充是否存在等 |
傳統的列印類資訊與反射APi的區別
下面是一段我自己寫的參數程式,用於示範反射的使用:
<?phpclass Person{ //成員屬性 public $name; public $age; //構造方法 public function construct($name, $age) { $this->name = $name; $this->age = $age; } //成員方法 public function set_name($name) { $this->$name = $name; } public function get_name() { return $this->$name; } public function get_age() { return $this->$age; } public function get_user_info() { $info = '姓名:' . $this->name; $info .= ' 年齡:' . $this->age; return $info; }}class Teacher extends Person{ private $salary = 0; public function construct($name, $age, $salary) { parent::construct($name, $age); $this->salary = $salary; } public function get_salary() { return $this->$salary; } public function get_user_info() { $info = parent::get_user_info(); $info .= " 工資:" . $this->salary; return $info; }}class Student extends Person{ private $score = 0; public function construct($name, $age, $score) { parent::construct($name, $age); $this->score = $score; } public function get_score() { return $this->score; } public function get_user_info() { $info = parent::get_user_info(); $info .= " 成績:" . $this->score; return $info; }}header("Content-type:text/html;charset=utf8;");$te_obj = new Teacher('李老師', '36', '2000');$te_info = $te_obj->get_user_info();$st_obj = new Student('小明', '13', '80');$st_info = $st_obj->get_user_info();
我們先用var_dump();列印類的資訊,如下所示,可以看出只是列印出類的簡單資訊,甚至連方法也沒有,所以從這樣的資訊中看不出其他遊泳的資訊。
var_dump($te_obj);
object(Teacher)#1 (3) { ["salary":"Teacher":private]=> string(4) "2000" ["name"]=> string(9) "李老師" ["age"]=> string(2) "36"}
Reflection::export($obj);
我們利用Reflection提供的內建方法export來列印資訊,如下所示:
列印出的資訊比較完整,包括成員屬性,成員方法,類的基本資料,檔案路徑,方法資訊,方法屬性,傳參情況,所在檔案的行數等等。比較全面的展示了類的資訊。可以看出var_dump()或者print_r只能顯示類的簡要資訊,好多資訊根本顯示不出來,所以他們只能做簡單的調試之用,反射Api則提供的類更多的資訊,可以很好地協助我們知道調用類的情況,這對寫介面,特別是調用別人的介面提供了極大的便利。如果出了問題,也可以協助調試。
object(Teacher)#1 (3) { ["salary":"Teacher":private]=> string(4) "2000" ["name"]=> string(9) "李老師" ["age"]=> string(2) "36"}Class [ class Person ] { @@ /usr/local/www/phptest/oop/reflaction.php 3-38 - Constants [0] { } - Static properties [0] { } - Static methods [0] { } - Properties [2] { Property [ public $name ] Property [ public $age ] } - Methods [5] { Method [ public method construct ] { @@ /usr/local/www/phptest/oop/reflaction.php 10 - 14 - Parameters [2] { Parameter #0 [ $name ].....
反射API的具體使用:
看過架構源碼的同學都知道架構都可以載入第三方的外掛程式、類庫等等。下面這個例子咱們藉助反射APi簡單實現這個功能,該例子原型是我從書上學習的,我理解後按照自己的思路寫了一套:要實現的功能:用一個類去動態遍曆調用Property類對象,類可以自由的載入其他的類的方法,而不用吧類嵌入到已有的代碼,也不用手動去調用類庫的代碼。
約定:每一個類要包含work方法,可以抽象出一個介面。可以把每個類的資訊放在檔案中,相當於各個類庫資訊,通過類儲存的Property類庫的對應對象,然後調用每個類庫的work方法。
下面是基礎代碼:
/*屬性介面*/interface Property{ function work();}class Person{ public $name; public function construct($name) { $this->name = $name; }}class StudentController implements Property{ //set方法,但需要Person對象參數 public function setPerson(Person $obj_person) { echo 'Student ' . $obj_person->name; } //work方法簡單實現 public function work() { echo 'student working!'; }}class EngineController implements Property{ //set方法 public function setWeight($weight) { echo 'this is engine -> set weight'; } public function setPrice($price) { echo "this is engine -> set price"; } //work方法簡單實現 public function work() { echo 'engine working!'; }}
這裡定義了兩個相似類實現Property介面,同時都簡單實現work()方法 StudentController類稍微不同,參數需要Person對象,同時我們可以使用檔案來儲存各個類的資訊,我們也可以用成員屬性代替。
class Run{ public static $mod_arr = []; public static $config = [ 'StudentController' => [ 'person' => 'xiao ming' ], 'EngineController' => [ 'weight' => '500kg', 'price' => '4000' ] ]; //載入初始化 public function construct() { $config = self::$config; //用於檢查是不是實作類別 $property = new ReflectionClass('Property'); foreach ($config as $class_name => $params) { $class_reflect = new ReflectionClass($class_name); if(!$class_reflect->isSubclassOf($property)) {//用isSubclassOf方法檢查是否是這個對象 echo 'this is error'; continue; } //得到類的資訊 $class_obj = $class_reflect->newInstance(); $class_method = $class_reflect->getMethods(); foreach ($class_method as $method_name) { $this->handle_method($class_obj, $method_name, $params); } array_push(self::$mod_arr, $class_obj); } } //處理方法調用 public function handle_method(Property $class_obj, ReflectionMethod $method_name, $params) { $m_name = $method_name->getName(); $args = $method_name->getParameters(); if(count($args) != 1 || substr($m_name, 0, 3) != 'set') { return false; } //大小寫轉換,做容錯處理 $property = strtolower(substr($m_name, 3)); if(!isset($params[$property])) { return false; } $args_class = $args[0]->getClass(); echo '<pre>'; if(empty($args_class)) { $method_name->invoke($class_obj, $params[$property]); //如果得到的類為空白證明需要傳遞基礎型別參數 } else { $method_name->invoke($class_obj, $args_class->newInstance($params[$property])); //如果不為空白說明需要傳遞真實對象 } }}//程式開始new Run();
到此程式結束,Run啟動會自動調用構造方法,初始化要載入類庫的其他成員屬性,包括初始化和執行相應方法操作,這裡只是完成了對應的set方法。其中$mod_arr屬性儲存了所有調用類的對象,每個對象包含資料,可以遍曆包含的對象來以此調用work()方法。
程式只做輔助理解反射PAI用,各個功能沒有完善,裡面用到了好多反射API的類,方法,下面會有各個方法的總結。
反射API提供的常用類和函數:
下面提供的函數是常用的函數,不是全部,有的函數根本用不到,所以我們有往撒謊那個寫,想看全部的可以到網上搜一下,比較多。提供的這組方法沒有必要背下來,用到的時候可以查看。
1:Reflection public static export(Reflector r [,bool return])//列印類或方法的詳細資料 public static getModifierNames(int modifiers) //取得修飾符的名字2:ReflectionMethod: public static string export() //列印該方法的資訊 public mixed invoke(stdclass object, mixed* args) //調用對應的方法 public mixed invokeArgs(stdclass object, array args)//調用對應的方法,傳多參數 public bool isFinal() //方法是否為final public bool isAbstract() //方法是否為abstract public bool isPublic() //方法是否為public public bool isPrivate() //方法是否為private public bool isProtected() //方法是否為protected public bool isStatic() //方法是否為static public bool isConstructor() //方法是否為建構函式3:ReflectionClass: public static string export() //列印類的詳細資料 public string getName() //取得類名或介面名 public bool isInternal() //類是否為系統內部類 public bool isUserDefined() //類是否為使用者自訂類 public bool isInstantiable() //類是否被執行個體化過 public bool hasMethod(string name) //類是否有特定的方法 public bool hasProperty(string name)//類是否有特定的屬性 public string getFileName() //擷取定義該類的檔案名稱,包括路徑名 public int getStartLine() //擷取定義該類的開始行 public int getEndLine() //擷取定義該類的結束行 public string getDocComment() //擷取該類的注釋 public ReflectionMethod getConstructor() //取得該類的建構函式資訊 public ReflectionMethod getMethod(string name) //取得該類的某個特定的方法資訊 public ReflectionMethod[] getMethods() //取得該類的所有的方法資訊 public ReflectionProperty getProperty(string name) //取得某個特定的屬性資訊 public ReflectionProperty[] getProperties() //取得該類的所有屬性資訊 public array getConstants() //取得該類所有常量資訊 public mixed getConstant(string name) //取得該類特定常量資訊 public ReflectionClass[] getInterfaces() //取得介面類資訊 public bool isInterface() //測試該類是否為介面 public bool isAbstract() //測試該類是否為抽象類別4:ReflectionParameter: public static string export() //匯出該參數的詳細資料 public string getName() //取得參數名 public bool isPassedByReference() //測試該參數是否通過引用傳遞參數 public ReflectionClass getClass() //若該參數為對象,返回該對象的類名 public bool isArray() //測試該參數是否為數群組類型 public bool allowsNull() //測試該參數是否允許為空白 public bool isOptional() //測試該參數是否為可選的,當有預設參數時可選 public bool isDefaultValueAvailable() //測試該參數是否為預設參數 public mixed getDefaultValue() //取得該參數的預設值5:ReflectionExtension類 public static export() //匯出該擴充的所有資訊 public string getName() //取得該擴充的名字 public string getVersion() //取得該擴充的版本 public ReflectionFunction[] getFunctions() //取得該擴充的所有函數 public array getConstants() //取得該擴充的所有常量 public array getINIEntries() //取得與該擴充相關的,在php.ini中的指令資訊}