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新手上路(十二)
Oct 09 PHP
php数组函数序列之array_intersect() 返回两个或多个数组的交集数组
Nov 10 PHP
php的chr和ord函数实现字符加减乘除运算实现代码
Dec 05 PHP
PHP setTime 设置当前时间的代码
Aug 27 PHP
php将字符串转化成date存入数据库的两种方式
Apr 28 PHP
windows下安装php的memcache模块的方法
Apr 07 PHP
PHP四种基本排序算法示例
Apr 09 PHP
PHP 返回13位时间戳的实现代码
May 13 PHP
php字符串比较函数用法小结(strcmp,strcasecmp,strnatcmp及strnatcasecmp)
Jul 18 PHP
PHP使用PDO调用mssql存储过程的方法示例
Oct 07 PHP
自定义Laravel (monolog)日志位置,并增加请求ID的实现
Oct 17 PHP
thinkphp5.1框架模板赋值与变量输出示例
May 25 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 调用远程url的六种方法小结
2009/11/02 PHP
PHP分页效率终结版(推荐)
2013/07/01 PHP
ThinkPHP3.1新特性之动态设置自动完成和自动验证示例
2014/06/19 PHP
浅析THINKPHP的addAll支持的最大数据量
2015/02/03 PHP
PHP框架Laravel的小技巧两则
2015/02/10 PHP
PHP 实现公历日期与农历日期的互转换
2017/09/13 PHP
Laravel 队列使用的实现
2019/01/08 PHP
javascript 解决表单仍然提交即使监听处理函数返回false
2010/03/14 Javascript
javascript (用setTimeout而非setInterval)
2011/12/28 Javascript
JavaScript高级程序设计 阅读笔记(四) ECMAScript中的类型转换
2012/02/27 Javascript
Web跨浏览器进程通信(Web跨域)
2013/04/17 Javascript
js实现广告漂浮效果的小例子
2013/07/02 Javascript
写JQuery插件的基本知识
2013/11/25 Javascript
Vue.js组件tabs实现选项卡切换效果
2016/12/01 Javascript
微信小程序开发经验总结(推荐)
2017/01/11 Javascript
微信小程序 图片加载(本地,网路)实例详解
2017/03/10 Javascript
vue单页应用中如何使用jquery的方法示例
2017/07/27 jQuery
get  post jsonp三种数据交互形式实例详解
2017/08/25 Javascript
在 Node.js 中使用原生 ES 模块方法解析
2017/09/19 Javascript
mui上拉加载更多下拉刷新数据的封装过程
2017/11/03 Javascript
使用vue开发移动端管理后台的注意事项
2019/03/07 Javascript
在Python中处理XML的教程
2015/04/29 Python
python基础教程之匿名函数lambda
2017/01/17 Python
python中defaultdict的用法详解
2017/06/07 Python
django文档学习之applications使用详解
2018/01/29 Python
解决python 未发现数据源名称并且未指定默认驱动程序的问题
2018/12/07 Python
Python PyQt5 Pycharm 环境搭建及配置详解(图文教程)
2019/07/16 Python
浅谈tensorflow中张量的提取值和赋值
2020/01/19 Python
HTML5 语音搜索只需一句代码
2013/01/03 HTML / CSS
美国知名户外用品畅销中心:Sierra Trading Post
2016/07/19 全球购物
Fnac西班牙官网:法国文化和电子产品零售商
2021/03/14 全球购物
2014年大学生职业规划书:未来不是梦,只要勇敢冲!
2014/09/22 职场文书
2014大学辅导员工作总结
2014/12/02 职场文书
2015年感恩母亲节的演讲稿
2015/03/18 职场文书
python 如何执行控制台命令与操作剪切板
2021/05/20 Python
使用Ajax实现无刷新上传文件
2022/04/12 Javascript