PHP通过反射动态加载第三方类和获得类源码的实例


Posted in PHP onNovember 27, 2015

使用反射动态加载第三方类

用反射加载第三方类用处在于:
使用XML或其他配文件配置要加载的类,从而和系统源代码分离。
对加载的类进行类检查,是加载的类符合自己定义的结构。

<?php
  abstract class Module {  #核心Module类库
    function baseFunc() {
      echo "I am baseFunc";
    }
    
    abstract function execute();
  }
  
  class ModuleRunner {
    private $configData = array(  #模拟xml配置,动态配置需要加载的Module
      "PersonModule" => array("person" => "bob"),
      "FtpModule" => array("host" => "example.com", "user" => "anon")
    );
    
    private $modules = array();
    
    function init() {  #初始化ModuleRunner,加载配置中的Module
      $parent = new ReflectionClass("Module");
      foreach($this->configData as $moduleName => $params) {  #检查配置中的Module是否合法
        $moduleClass = new ReflectionClass($moduleName);
        if(! $moduleClass->isSubclassOf($parent)) {  #检查是否是Module的子类型
          throw new Exception("unknown type : {$moduleName}");
        }
        $module = $moduleClass->newInstance();
        foreach($moduleClass->getMethods() as $method) {  #检查配置中的函数的参数格式是否正确
          $this->handleMothod($module, $method, $params);
        }
        array_push($this->modules, $module);  #加载Module
      }
    }
    
    private function handleMothod(Module $module, ReflectionMethod $method, $params) {  #检查Module中的方法参数是

否和传入的$params名字相同,并且具有set方法
  

$name = $method->getName();
      $args = $method->getParameters();
    
      if(count($args) != 1 || substr($name, 0, 3) != "set") {  #如果没有配置中的类的方法的参数个数不为1,或者方法名前3个字母不为set,返回false
        return false;
      }
      
      $property = strtolower(substr($name, 3));
      if(!isset($params[$property])) {  #如果方法名后三个字母与配置中的参数名不同,返回false
        return false;
      }
      
      $argClass = $args[0]->getClass();  #获取参数的类型
      if(empty($argClass)) {
        $method->invoke($module, $params[$property]);  #参数无类型限制则直接调用set方法
      } else {
        $method->invoke($module, $argClass->newInstance($params[$property]));  #有类型限制则新建一个实例并调用set方法
      }
    }
    
    public function getModules() {
      return $this->modules;
    }
  }
  
  class Person {  #第三方类
    public $name;
    
    function __construct($name) {
      $this->name = $name;
    }
  }
  
  class FtpModule extends Module {  #用户自定义第三方Module
    private $host = "default host";
    private $user = "default user";
    
    function setHost($host) {
      $this->host = $host;
    }
    
    function setUser($user) {
      $this->user = $user;
    }
    
    function execute() {
      echo "{$this->user} user {$this->host}";
    }
  }
  
  class PersonModule extends Module {  #用户自定义第三方Module
    private $person;
  
    function setPerson(Person $person) {
      $this->person = $person;
    }
    
    function execute() {
      if(isset($person)) {
        echo "I am {$this->person->name}";
      } else {
        echo "I am no user";
      }
    }
  }
  
  $modRunner = new ModuleRunner();
  $modRunner->init();
  var_dump($modRunner);
?>

输出

object(ModuleRunner)#1 (2) { ["configData":"ModuleRunner":private]=> array(2) { ["PersonModule"]=> array(1) { ["person"]=> string(3) "bob" } ["FtpModule"]=> array(2) { ["host"]=> string(11) "example.com" ["user"]=> string(4) "anon" } } ["modules":"ModuleRunner":private]=> array(2) { [0]=> object(PersonModule)#4 (1) { ["person":"PersonModule":private]=> object(Person)#10 (1) { ["name"]=> string(3) "bob" } } [1]=> object(FtpModule)#3 (2) { ["host":"FtpModule":private]=> string(11) "example.com" ["user":"FtpModule":private]=> string(4) "anon" } } }

通过反射获得类源码

<?php
  function getSource(ReflectionClass $ref) {
    $path = $ref->getFileName();  #获取脚本文件文件名
    $file = file($path); #file()方法获取文件内容,并将内容保存在一个数组中,数组每个元素保存一行内容
    $start = $ref->getStartLine();  #获取类在脚本中的第一行行号
    $end = $ref->getEndLine();  #获取类在脚本中最后一行的行号
    $source = implode(array_slice($file, $start - 1, $end - $start + 1));  #拼装类源码
    
    var_dump($source);
  }

  class Person {
    public $age;
    private $name;
    
    function say() {
      echo "yes";
    }
  }
  
  $ref = new ReflectionClass("Person");
  getSource($ref);
?>
PHP 相关文章推荐
PHP弹出提示框并跳转到新页面即重定向到新页面
Jan 24 PHP
PHP对接微信公众平台消息接口开发流程教程
Mar 25 PHP
thinkphp中html:list标签传递多个参数实例
Oct 30 PHP
thinkphp四种url访问方式详解
Nov 28 PHP
WebQQ最新登陆协议的用法
Dec 22 PHP
php从数组中随机选择若干不重复元素的方法
Mar 14 PHP
php简单日历函数
Oct 28 PHP
PHP实现Google plus的好友拖拽分组效果
Oct 21 PHP
php制作基于xml的RSS订阅源功能示例
Feb 08 PHP
PHP并发查询MySQL的实例代码
Aug 09 PHP
PHP实现负载均衡session共享redis缓存操作示例
Aug 22 PHP
CentOS7系统搭建LAMP及更新PHP版本操作详解
Mar 26 PHP
基于php实现随机合并数组并排序(原排序)
Nov 26 #PHP
PHP随手笔记整理之PHP脚本和JAVA连接mysql数据库
Nov 25 #PHP
php实现遍历多维数组的方法
Nov 25 #PHP
WordPress迁移时一些常见问题的解决方法整理
Nov 24 #PHP
基础的WordPress插件制作教程
Nov 24 #PHP
优化WordPress的Google字体以加速国内服务器上的运行
Nov 24 #PHP
在WordPress中实现评论头像的自定义默认和延迟加载
Nov 24 #PHP
You might like
php简单socket服务器客户端代码实例
2015/05/18 PHP
php中替换字符串函数strtr()和str_repalce()的用法与区别
2016/11/25 PHP
this[] 指的是什么内容 讨论
2007/03/24 Javascript
seaJs的模块定义和模块加载浅析
2014/06/06 Javascript
jquery中$each()方法的使用指南
2015/04/30 Javascript
基于jquery实现图片相关操作(重绘、获取尺寸、调整大小、缩放)
2015/12/25 Javascript
ui-router中使用ocLazyLoad和resolve的具体方法
2017/10/18 Javascript
JavaScript的数据类型转换原则(干货)
2018/03/15 Javascript
Vue实现侧边菜单栏手风琴效果实例代码
2018/05/31 Javascript
JS中注入eval, Function等系统函数截获动态代码
2019/04/03 Javascript
JavaScript表格隔行变色和Tab标签页特效示例【附jQuery版】
2019/07/11 jQuery
微信小程序商品详情页底部弹出框
2019/11/22 Javascript
vue过滤器实现日期格式化的案例分析
2020/07/02 Javascript
[47:02]2018DOTA2亚洲邀请赛3月29日 小组赛B组 VP VS paiN
2018/03/30 DOTA
Python中处理字符串的相关的len()方法的使用简介
2015/05/19 Python
在Mac OS上搭建Python的开发环境
2015/12/24 Python
python3获取当前文件的上一级目录实例
2018/04/26 Python
Python Requests库基本用法示例
2018/08/20 Python
Python面向对象程序设计类变量与成员变量、类方法与成员方法用法分析
2019/04/12 Python
Python安装依赖(包)模块方法详解
2020/02/14 Python
前端H5 Video常见使用场景简介
2020/08/21 HTML / CSS
手工制作的豪华英式沙发和沙发床:Willow & Hall
2019/05/03 全球购物
Yahoo的PHP面试题
2014/05/26 面试题
经典安踏广告词
2014/03/21 职场文书
建房协议书
2014/04/11 职场文书
安康杯竞赛活动总结
2014/05/05 职场文书
公司委托书范本5篇
2014/09/20 职场文书
对党的十八届四中全会的期盼
2014/10/17 职场文书
本溪水洞导游词
2015/02/11 职场文书
科技活动总结范文
2015/05/11 职场文书
工资证明格式模板
2015/06/12 职场文书
获奖感言范文
2015/07/31 职场文书
小学语文新课改心得体会
2016/01/22 职场文书
2019年描写人生经典诗句大全
2019/07/08 职场文书
教你用python实现一个无界面的小型图书管理系统
2021/05/21 Python
Python 使用 Frame tkraise() 方法在 Tkinter 应用程序中的Frame之间切换
2022/04/24 Python