<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>最优良人 &#187; 反射机制</title>
	<atom:link href="http://www.zui88.com/view-tag/%e5%8f%8d%e5%b0%84%e6%9c%ba%e5%88%b6/feed" rel="self" type="application/rss+xml" />
	<link>http://www.zui88.com/blog</link>
	<description>中山php&#124;最优网络</description>
	<lastBuildDate>Mon, 13 May 2013 04:56:43 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.4</generator>
		<item>
		<title>什么是php反射机制以及利用php反射机制实现可插拔可扩展的插件架构</title>
		<link>http://www.zui88.com/blog/view-205.html</link>
		<comments>http://www.zui88.com/blog/view-205.html#comments</comments>
		<pubDate>Thu, 25 Aug 2011 16:06:34 +0000</pubDate>
		<dc:creator>lin</dc:creator>
				<category><![CDATA[后端程序]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[反射机制]]></category>

		<guid isPermaLink="false">http://www.zui88.com/blog/?p=205</guid>
		<description><![CDATA[反射是什么？ 它是指在PHP运行状态中，扩展分析PHP程序，导出或提取出关于类、方法、属性、参数等的详细信息，包括注释。这种动态获取的信息以及动态调用对象的方法的功能称为反射API。 反射是操纵面向对象范型中元模型的API，其功能十分强大，可帮助我们构建复杂，可扩展的应用。其用途如：自动加载插件，自动生成文档，甚至可用来扩充PHP语言。php反射api由若干类组成，可帮助我们用来访问程序的元数据或者同相关的注释交互。借助反射我们可以获取诸如类实现了那些方法，创建一个类的实例（不同于用new创建），调用一个方法（也不同于常规调用），传递参数，动态调用类的静态方法。 * ** 反射api是php内建的oop技术扩展，包括一些类，异常和接口，综合使用他们可用来帮助我们分析其它类，接口，方法，属性，方法和扩展。这些oop扩展被称为反射，位于php源码/ext/reflection目录下。 可以使用反射api自省反射api本身（这可能就是反射最初的意思，自己"看"自己）： &#60;?php Reflection::export(new ReflectionExtension('reflection')); ?&#62; 几乎所有的反射api都实现了reflector接口，所有实现该接口的类都有一个export方法，该方法打印出参数对象的相关信息。 使用get_declared_classes()获取所有php内置类，get_declared_interfaces(); get_defined_functions(); get_defined_vars(); get_defined_constants();可获取php接口，方法，变量，常量信息。 ** *** 反射初探： &#60;?php //定义一个自定义类 class MyTestClass{ public function testFunc($para0='defaultValue0'){ } } //接下来反射它 foreach(get_declared_classes() as $class){ //实例化一个反射类 $reflectionClass = new ReflectionClass($class); //如果该类是自定义类 if($reflectionClass-&#62;isUserDefined()){ //导出该类信息 Reflection::export($reflectionClass); } } ?&#62; 以上片段实例如何查看自定义类的基本信息。 描述数据的数据被称为元数据，用反射获取的信息就是元数据信息，这些信息用来描述类，接口方法等等。（元---》就是原始之意，比如元模型就是描述模型的模型，比如UML元模型就是描述UML结构的模型），元数据进一步可分为硬元数据（hard matadata）和软元数据（soft metadata）,前者由编译代码导出，如类名字，方法，参数等。 后者是人为加入的数据，如phpDoc块，php中的属性等。 *** **** 现在商业软件很多都是基于插件架构的，比如eclipse，和visual studio，netbeans等一些著名IDE都是基于插件的GUI应用。第三方或本方开发插件时，必须导入定义好的相关接口，然后实现这些接口，最后把实现的包放在指定目录下，宿主应用程序在启动时自动检测所有的插件实现，并加载它们。如果我们自己想实现这样的架构也是可能的。 &#60;?php //先定义UI接口 [...]]]></description>
			<content:encoded><![CDATA[<p>反射是什么？<br />
它是指在PHP运行状态中，扩展分析PHP程序，导出或提取出关于类、方法、属性、参数等的详细信息，包括注释。这种动态获取的信息以及动态调用对象的方法的功能称为反射API。</p>
<p>反射是操纵面向对象范型中元模型的API，其功能十分强大，可帮助我们构建复杂，可扩展的应用。其用途如：自动加载插件，自动生成文档，甚至可用来扩充PHP语言。php反射api由若干类组成，可帮助我们用来访问程序的元数据或者同相关的注释交互。借助反射我们可以获取诸如类实现了那些方法，创建一个类的实例（不同于用new创建），调用一个方法（也不同于常规调用），传递参数，动态调用类的静态方法。<br />
*<br />
**<br />
反射api是php内建的oop技术扩展，包括一些类，异常和接口，综合使用他们可用来帮助我们分析其它类，接口，方法，属性，方法和扩展。这些oop扩展被称为反射，位于php源码/ext/reflection目录下。</p>
<p>可以使用反射api自省反射api本身（这可能就是反射最初的意思，自己"看"自己）：<br />
&lt;?php<br />
Reflection::export(new ReflectionExtension('reflection'));<br />
?&gt;<br />
几乎所有的反射api都实现了reflector接口，所有实现该接口的类都有一个export方法，该方法打印出参数对象的相关信息。<br />
使用get_declared_classes()获取所有php内置类，get_declared_interfaces();<br />
get_defined_functions();<br />
get_defined_vars(); get_defined_constants();可获取php接口，方法，变量，常量信息。</p>
<p>**<br />
***<br />
反射初探：<br />
&lt;?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-&gt;isUserDefined()){<br />
      //导出该类信息<br />
        Reflection::export($reflectionClass);<br />
    }<br />
}<br />
?&gt;<br />
以上片段实例如何查看自定义类的基本信息。<br />
描述数据的数据被称为元数据，用反射获取的信息就是元数据信息，这些信息用来描述类，接口方法等等。（元---》就是原始之意，比如元模型就是描述模型的模型，比如UML元模型就是描述UML结构的模型），元数据进一步可分为硬元数据（hard matadata）和软元数据（soft metadata）,前者由编译代码导出，如类名字，方法，参数等。<br />
后者是人为加入的数据，如phpDoc块，php中的属性等。<br />
***<br />
****<br />
现在商业软件很多都是基于插件架构的，比如eclipse，和visual studio，netbeans等一些著名IDE都是基于插件的GUI应用。第三方或本方开发插件时，必须导入定义好的相关接口，然后实现这些接口，最后把实现的包放在指定目录下，宿主应用程序在启动时自动检测所有的插件实现，并加载它们。如果我们自己想实现这样的架构也是可能的。<br />
&lt;?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 />
?&gt;<br />
php中也有使用插件的解决方案，不像eclipse。</p>
<p>使用我们的插件：1.先使用get_declared_classes()获取所有已加载类。2.遍历所有类，判断其是否实现了我们自定义的插件接口IPlugin。3.获取所有的插件实现。4.在宿主应用中与插件交互<br />
下面这个方法帮助我们找到实现了插件接口的所有类：<br />
function findPlugins() {<br />
$plugins = array();<br />
foreach(get_declared_classes() as $class) {<br />
$reflectionClass = new ReflectionClass($class);<br />
//判断一个类是否实现了IPlugin接口<br />
if($reflectionClass-&gt;implementsInterface('IPlugin')) {<br />
$plugins[] = $reflectionClass;<br />
}<br />
}<br />
return $plugins;<br />
}<br />
注意到所有的插件实现是作为反射类实例返回的，而不是类名本身，或是类的实例。因为如果使用反射来调用方法还需要一些条件判断。</p>
<p>判断一个类是否实现了某个方法使用反射类的hasMethod（）方法。<br />
接下来我们把所有的插件菜单项放在一个菜单上。<br />
function integratePlugInMenus() {<br />
$menu = array();<br />
//遍历所有的插件实现<br />
foreach(findPlugins() as $plugin) {<br />
//判断插件是否实现了getMenuItems方法<br />
if($plugin-&gt;hasMethod('getMenuItems')) {<br />
/*实例化一个方法实例（注意当你将类和方法看成概念时，它们就可以有实例，就像"人"这个概念一样），该方法返回的是ReflectionMethod的实例*/<br />
$reflectionMethod = $plugin-&gt;getMethod('getMenuItems');<br />
//如果方法是静态的<br />
if($reflectionMethod-&gt;isStatic()) {<br />
//调用静态方法，注意参数是null而不是一个反射类实例<br />
$items = $reflectionMethod-&gt;invoke(null);<br />
} else {<br />
//如果方法不是静态的，则先实例化一个反射类实例所代表的类的实例。<br />
$pluginInstance = $plugin-&gt;newInstance();<br />
//使用反射api来调用一个方法，参数是通过反射实例化的对象引用<br />
$items = $reflectionMethod-&gt;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-&gt;someMethod($argList...);<br />
因为使用了反射，这时你在想调用一个方法时形式变为：<br />
$reflectionMethodRef-&gt;invoke($reflectionClassRef,$argList...);<br />
如果使用反射调用方法，我们必须实例化一个反射方法的实例，如果是实例方法还要有一个实例的引用，可能还需传递必要的参数。当调用一个静态方法时，显式传入null作为第一参数。<br />
对插件类实现的其他方法有类似的处理逻辑，这里不再敷述。<br />
以下是我的一个简单测试：<br />
&lt;?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 />
}</p>
<p>/*下面是对插件的实现，其实应该放在不同的文件中，甚至是不同的包中*/<br />
class MyPlugIn implements IPlugIn<br />
{<br />
    public function getSidebars()<br />
    {<br />
        //构造自己的导航栏<br />
        $sideBars = '&lt;div&gt;&lt;ul &gt;<br />
            &lt;li&gt;&lt;a href=""&gt;m1&lt;/a&gt;<br />
                                &lt;/li&gt;<br />
                 &lt;li&gt;&lt;a href=""&gt;m2&lt;/a&gt;<br />
                                &lt;/li&gt;<br />
               &lt;/ul&gt;<br />
               &lt;/div&gt;';<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 = '&lt;div&gt;&lt;ul &gt;<br />
            &lt;li&gt;&lt;a href=""&gt;mm1&lt;/a&gt;<br />
                                &lt;/li&gt;<br />
                 &lt;li&gt;&lt;a href=""&gt;mm2&lt;/a&gt;<br />
                                &lt;/li&gt;<br />
               &lt;/ul&gt;<br />
               &lt;/div&gt;';<br />
        return $sideBars;<br />
    }<br />
    public static function GetName()<br />
    {<br />
        return 'MyPlugIn2';<br />
    }<br />
}</p>
<p>//在宿主程序中使用插件<br />
class HostApp<br />
{</p>
<p>   public function initAll()<br />
    {<br />
        // 初始化各个部分<br />
        echo "yiqing95.";<br />
     $this-&gt;renderAll();<br />
    }<br />
    //渲染GUI格部分<br />
    function renderAll(){<br />
        $rsltSidebars="&lt;table&gt;";<br />
        foreach($this-&gt;integrateSidebarsOfPlugin() as $sidebarItem){<br />
            $rsltSidebars.="&lt;tr&gt;&lt;td&gt;$sidebarItem&lt;/td&gt;&lt;/tr&gt;";<br />
        }<br />
        $rsltSidebars.="&lt;/table&gt;";</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-&gt;implementsInterface('IPlugin')) {<br />
                $plugins[] = $reflectionClass;<br />
            }<br />
        }<br />
        return $plugins;<br />
    }<br />
    /**加载组装所有插件实现***/<br />
   protected function integrateSidebarsOfPlugin()<br />
    {<br />
        $sidebars = array();<br />
        foreach ($this-&gt;findPlugins() as $plugin) {<br />
            if ($plugin-&gt;hasMethod('getSidebars')) {<br />
                $reflectionMethod = $plugin-&gt;getMethod('getSidebars');<br />
                if ($reflectionMethod-&gt;isStatic()) {<br />
                    $items = $reflectionMethod-&gt;invoke(null);<br />
                } else {<br />
                    $pluginInstance = $plugin-&gt;newInstance();<br />
                    $items = $reflectionMethod-&gt;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-&gt;initAll();<br />
?&gt;<br />
****<br />
××××<br />
$reflectionClass = new ReflectionClass("IPlugIn");<br />
echo $reflectionClass-&gt; getDocComment();<br />
这段代码可以帮助我们获取类的文档注释，一旦我们获取了类的注释内容我们就可以扩展我们的类功能，比如先获取注释，然后分析注释使用docblock tokenizer 『pecl扩展』，或使用自带的Tokenizer类又或者使用正则表达式，字符串函数来解析注释文档，你可以在注释中加入任何东西，包括指令，在使用反射调用前可判断这些通过注释传递的指令或数据：<br />
&lt;?php<br />
//"分析相关的注释数据"<br />
analyse($reflectionClass-&gt; getDocComment());//analyse是自己定义的！！！<br />
//根据分析的结果来执行方法，或者传递参数等<br />
if(xxxx){<br />
$reflectionMethod-&gt;invoke($pluginInstance) ;<br />
}<br />
?&gt;<br />
因为注释毕竟是字符串，可以使用任何字符串解析技术，提取有用的信息，再根据这些信息来调用方法，就是说程序的逻辑不光可由方法实现决定，还可能由注释决定（前提是你使用了反射，注释格式严格有要求）。<br />
××××<br />
*****<br />
反射api和其他类一样可被继承扩展，所以我们可以为这些api添加自己的功能。结合自定义注释标记。就是以@开头的东东，标注（Java中称为annotation），.net中称为属性attribute（或称为特性）。然后扩展Reflection类，就可以实现强大的扩展功能了。<br />
值得一提的是工厂方法设计模式（GOF之一），也常使用反射来实例化对象，下面是示例性质的伪码：<br />
Class XXXFactory{<br />
function getInstance($className){<br />
   $reflectionClass =new ReflectionClass($className);<br />
   return $reflectionClass-&gt;newInstance();<br />
}<br />
//使用接口的那个类实现，可能来自配置文件<br />
function getInstance(){<br />
$pathOfConfig = "xxx/xx/XXXImplement.php";<br />
$className= Config-&gt;getItem($pathOfClass,'SomeClassName');<br />
return $this-&gt;getInstance($className);<br />
}<br />
}<br />
*****</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zui88.com/blog/view-205.html/feed</wfw:commentRss>
		<slash:comments>406</slash:comments>
		</item>
	</channel>
</rss>
