• PHP API反射实例


    *
    反射是操纵面向对象范型中元模型的API,其功能十分强大,可帮助我们构建复杂,可扩展的应用。其用途如:自动加载插件,自动生成文档,甚至可用来扩充PHP语言。php反射api由若干类组成,可帮助我们用来访问程序的元数据或者同相关的注释交互。借助反射我们可以获取诸如类实现了那些方法,创建一个类的实例(不同于用new创建),调用一个方法(也不同于常规调用),传递参数,动态调用类的静态方法。
    *
    **
    反射api是php内建的oop技术扩展,包括一些类,异常和接口,综合使用他们可用来帮助我们分析其它类,接口,方法,属性,方法和扩展。这些oop扩展被称为反射,位于php源码/ext/reflection目录下。
    可以使用反射api自省反射api本身(这可能就是反射最初的意思,自己“看”自己):

    <?php
    Reflection::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类又或者使用正则表达式,字符串函数来解析注释文档,你可以在注释中加入任何东西,包括指令,在使用反射调用前可判断这些通过注释传递的指令或数据:
    <?php
    //"分析相关的注释数据"
    analyse($reflectionClass-> getDocComment());//analyse是自己定义的!!!
    //根据分析的结果来执行方法,或者传递参数等
    if(xxxx){
    $reflectionMethod->invoke($pluginInstance) ;
    }
    ?>
    因为注释毕竟是字符串,可以使用任何字符串解析技术,提取有用的信息,再根据这些信息来调用方法,就是说程序的逻辑不光可由方法实现决定,还可能由注释决定(前提是你使用了反射,注释格式严格有要求)。

    反射api和其他类一样可被继承扩展,所以我们可以为这些api添加自己的功能。结合自定义注释标记。就是以@开头的东东,标注(Java中称为annotation),.net中称为属性attribute(或称为特性)。然后扩展Reflection类,就可以实现强大的扩展功能了。
    值得一提的是工厂方法设计模式(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
  • 相关阅读:
    [置顶] Docker学习总结(3)——Docker实战之入门以及Dockerfile(三)
    [置顶] Docker学习总结(2)——Docker实战之入门以及Dockerfile(二)
    Vue-Router中History模式【华为云分享】
    Linux系统通过FTP进行文档基本操作【华为云分享】
    窥探日志的秘密【华为云分享】
    弹性负载均衡:负载无限,均衡有道【华为云分享】
    不给糖果就捣乱,用Python绘制有趣的万圣节南瓜怪【华为云分享】
    机器学习笔记(八)---- 神经网络【华为云分享】
    还在为运维烦恼?体验云上运维服务,提意见赢好礼!【华为云分享】
    Vue+ElementUI项目使用webpack输出MPA【华为云分享】
  • 原文地址:https://www.cnblogs.com/logy/p/4142964.html
Copyright © 2020-2023  润新知