PHP API反射執行個體

來源:互聯網
上載者:User

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.