標籤:blog http io ar os 使用 java for on
*
反射是操縱物件導向範型中元模型的API,其功能十分強大,可協助我們構建複雜,可擴充的應用。其用途如:自動載入外掛程式,自動產生文檔,甚至可用來擴充PHP語言。php反射api由若干類組成,可協助我們用來訪問程式的中繼資料或者同相關的注釋互動。藉助反射我們可以擷取諸如類實現了那些方法,建立一個類的執行個體(不同於用new建立),調用一個方法(也不同於常規調用),傳遞參數,動態調用類的靜態方法。
*
**
反射api是php內建的oop技術擴充,包括一些類,異常和介面,綜合使用他們可用來協助我們分析其它類,介面,方法,屬性,方法和擴充。這些oop擴充被稱為反射,位於php源碼/ext/reflection目錄下。
可以使用反射api自省反射api本身(這可能就是反射最初的意思,自己“看”自己):
<?phpReflection::export(new ReflectionExtension(‘reflection‘));?>
幾乎所有的反射api都實現了reflector介面,所有實現該介面的類都有一個export方法,該方法列印出參數對象的相關資訊。
使用get_declared_classes()擷取所有php內建類,get_declared_interfaces();
get_defined_functions();get_defined_vars(); get_defined_constants();可擷取php介面,方法,變數,常量資訊。
反射初探:
<?php//定義一個自訂類class MyTestClass{ public function testFunc($para0=‘defaultValue0‘){ }}//接下來反射它foreach(get_declared_classes() as $class){ //執行個體化一個反射類 $reflectionClass = new ReflectionClass($class); //如果該類是自訂類 if($reflectionClass->isUserDefined()){ //匯出該類資訊 Reflection::export($reflectionClass); }}?>
以上片段執行個體如何查看自訂類的基本資料。
描述資料的資料被稱為中繼資料,用反射擷取的資訊就是中繼資料資訊,這些資訊用來描述類,介面方法等等。(元---》就是原始之意,比如元模型就是描述模型的模型,比如UML元模型就是描述UML結構的模型),中繼資料進一步可分為硬中繼資料(hard matadata)和軟中繼資料(soft metadata),前者由編譯代碼匯出,如類名字,方法,參數等。
後者是人為加入的資料,如phpDoc塊,php中的屬性等。
現在商業軟體很多都是基於外掛程式架構的,比如eclipse,和visual studio,netbeans等一些著名IDE都是基於外掛程式的GUI應用。第三方或本方開發外掛程式時,必須匯入定義好的相關介面,然後實現這些介面,最後把實現的包放在指定目錄下,宿主應用程式在啟動時自動檢測所有的外掛程式實現,並載入它們。如果我們自己想實現這樣的架構也是可能的。
<?php//先定義UI介面interface IPlugin {//擷取外掛程式的名字public static function getName();//要顯示的功能表項目function getMenuItems();//要顯示的文章function getArticles();//要顯示的導覽列function getSideBars();}//一下是對外掛程式介面的實現class SomePlugin implements IPlugin {public function getMenuItems() {//返回功能表項目return null;}public function getArticles() {//返回我們的文章return null;}public function getSideBars() {//我們有一個導覽列return array(‘SideBarItem‘);}//返回外掛程式名public static function getName(){return "SomePlugin";}}?>
php中也有使用外掛程式的解決方案,不像eclipse。
使用我們的外掛程式:1.先使用get_declared_classes()擷取所有已載入類。2.遍曆所有類,判斷其是否實現了我們自訂的外掛程式介面IPlugin。3.擷取所有的外掛程式實現。4.在宿主應用中與外掛程式互動
下面這個方法協助我們找到實現了外掛程式介面的所有類:
function findPlugins() {$plugins = array();foreach(get_declared_classes() as $class) {$reflectionClass = new ReflectionClass($class);//判斷一個類是否實現了IPlugin介面if($reflectionClass->implementsInterface(‘IPlugin‘)) {$plugins[] = $reflectionClass;}}return $plugins;}
注意到所有的外掛程式實現是作為反射類執行個體返回的,而不是類名本身,或是類的執行個體。因為如果使用反射來調用方法還需要一些條件判斷。
判斷一個類是否實現了某個方法使用反射類的hasMethod()方法。
接下來我們把所有的外掛程式功能表項目放在一個菜單上。
function integratePlugInMenus() {$menu = array();//遍曆所有的外掛程式實現foreach(findPlugins() as $plugin) {//判斷外掛程式是否實現了getMenuItems方法if($plugin->hasMethod(‘getMenuItems‘)) {/*執行個體化一個方法執行個體(注意當你將類和方法看成概念時,它們就可以有執行個體,就像“人”這個概念一樣),該方法返回的是ReflectionMethod的執行個體*/$reflectionMethod = $plugin->getMethod(‘getMenuItems‘);//如果方法是靜態if($reflectionMethod->isStatic()) {//調用靜態方法,注意參數是null而不是一個反射類執行個體$items = $reflectionMethod->invoke(null);} else {//如果方法不是靜態,則先執行個體化一個反射類執行個體所代表的類的執行個體。$pluginInstance = $plugin->newInstance();//使用反射api來調用一個方法,參數是通過反射執行個體化的對象引用$items = $reflectionMethod->invoke($pluginInstance);}//合并所有的外掛程式功能表項目為一個菜單。$menu = array_merge($menu, $items);}}return $menu;}
這裡主要用到的反射方法執行個體的方法調用:
public mixed invoke(stdclass object, mixed args=null);
請一定搞清楚我們常規方法的調用是這種形式:$objRef->someMethod($argList...);
因為使用了反射,這時你在想調用一個方法時形式變為:
$reflectionMethodRef->invoke($reflectionClassRef,$argList...);
如果使用反射調用方法,我們必須執行個體化一個反射方法的執行個體,如果是執行個體方法還要有一個執行個體的引用,可能還需傳遞必要的參數。當調用一個靜態方法時,顯式傳入null作為第一參數。
對外掛程式類實現的其他方法有類似的處理邏輯,這裡不再敷述。
以下是我的一個簡單測試:
<?php/*** 定義一個外掛程式介面* */interface IPlugIn{ /** * getSidebars() * * @return 返回側導覽列 */ public function getSidebars(); /** * GetName() * * @return 返回類名 */ public static function GetName();}/*下面是對外掛程式的實現,其實應該放在不同的檔案中,甚至是不同的包中*/class MyPlugIn implements IPlugIn{ public function getSidebars() { //構造自己的導覽列 $sideBars = ‘<div><ul > <li><a href="">m1</a> </li> <li><a href="">m2</a> </li> </ul> </div>‘; return $sideBars; } public static function GetName() { return ‘MyPlugIn‘; }}//第二個外掛程式實現;class MyPlugIn2 implements IPlugIn{ public function getSidebars() { //構造自己的導覽列 $sideBars = ‘<div><ul > <li><a href="">mm1</a> </li> <li><a href="">mm2</a> </li> </ul> </div>‘; return $sideBars; } public static function GetName() { return ‘MyPlugIn2‘; }}//在宿主程式中使用外掛程式class HostApp{ public function initAll() { // 初始化各個部分 echo "yiqing95."; $this->renderAll(); } //渲染GUI格部分 function renderAll(){ $rsltSidebars="<table>"; foreach($this->integrateSidebarsOfPlugin() as $sidebarItem){ $rsltSidebars.="<tr><td>$sidebarItem</td></tr>"; } $rsltSidebars.="</table>"; echo $rsltSidebars; } /*載入所有的外掛程式實現:*/ protected function findPlugins() { $plugins = array(); foreach (get_declared_classes() as $class) { $reflectionClass = new ReflectionClass($class); if ($reflectionClass->implementsInterface(‘IPlugin‘)) { $plugins[] = $reflectionClass; } } return $plugins; } /**載入組裝所有外掛程式實現***/ protected function integrateSidebarsOfPlugin() { $sidebars = array(); foreach ($this->findPlugins() as $plugin) { if ($plugin->hasMethod(‘getSidebars‘)) { $reflectionMethod = $plugin->getMethod(‘getSidebars‘); if ($reflectionMethod->isStatic()) { $items = $reflectionMethod->invoke(null); } else { $pluginInstance = $plugin->newInstance(); $items = $reflectionMethod->invoke($pluginInstance) ; } } //$sidebars = array_merge($sidebars, $items); $sidebars[]=$items; } return $sidebars; } }//運行程式:$entryClass =new HostApp();$entryClass->initAll();?>
****
××××
$reflectionClass = new ReflectionClass("IPlugIn");
echo $reflectionClass-> getDocComment();
這段代碼可以協助我們擷取類的文檔注釋,一旦我們擷取了類的注釋內容我們就可以擴充我們的類功能,比如先擷取注釋,然後分析注釋使用docblock tokenizer 『pecl擴充』,或使用內建的Tokenizer類又或者使用Regex,字串函數來解析注釋文檔,你可以在注釋中加入任何東西,包括指令,在使用反射調用前可判斷這些通過注釋傳遞的指令或資料:
<?php
//"分析相關的注釋資料"
analyse($reflectionClass-> getDocComment());//analyse是自己定義的!!!
//根據分析的結果來執行方法,或者傳遞參數等
if(xxxx){
$reflectionMethod->invoke($pluginInstance) ;
}
?>
因為注釋畢竟是字串,可以使用任何字串解析技術,提取有用的資訊,再根據這些資訊來調用方法,就是說程式的邏輯不光可由方法實現決定,還可能由注釋決定(前提是你使用了反射,注釋格式嚴格有要求)。
反射api和其他類一樣可被繼承擴充,所以我們可以為這些api添加自己的功能。結合自訂注釋標記。就是以@開頭的東東,標註(Java中稱為annotation),.net中稱為屬性attribute(或稱為特性)。然後擴充Reflection類,就可以實現強大的擴充功能了。
值得一提的是Factory 方法設計模式(GOF之一),也常使用反射來執行個體化對象,下面是樣本性質的偽碼:
Class XXXFactory{function getInstance($className){ $reflectionClass =new ReflectionClass($className); return $reflectionClass->newInstance(); }//使用介面的那個類實現,可能來自設定檔function getInstance(){$pathOfConfig = "xxx/xx/XXXImplement.php";$className= Config->getItem($pathOfClass,‘SomeClassName‘);return $this->getInstance($className); }}
轉載:http://blog.csdn.net/siren0203/article/details/5994571
PHP API反射執行個體