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的一个完整SMTP类(解决邮件服务器需要验证时的问题)
Oct 09 PHP
vBulletin Forum 2.3.xx SQL Injection
Oct 09 PHP
织梦模板标记简介
Mar 11 PHP
关于PHP内存溢出问题的解决方法
Jun 25 PHP
PHP输出英文时间日期的安全方法(RFC 1123格式)
Jun 13 PHP
PHP实现的sqlite数据库连接类
Dec 12 PHP
必须收藏的php实用代码片段
Feb 02 PHP
php格式化json函数示例代码
May 12 PHP
Yii针对添加行的增删改查操作示例
Oct 18 PHP
iOS自定义提示弹出框实现类似UIAlertView的效果
Nov 16 PHP
PHP实现对数组分页处理实例详解
Feb 07 PHP
PHP中单例模式的使用场景与使用方法讲解
Mar 18 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中进行身份认证
2006/10/09 PHP
php版微信支付api.mch.weixin.qq.com域名解析慢原因与解决方法
2016/10/12 PHP
浅谈laravel 5.6 安装 windows上使用composer的安装过程
2019/10/18 PHP
JavaScript 学习笔记(七)字符串的连接
2009/12/31 Javascript
学习javascript,实现插入排序实现代码
2011/07/31 Javascript
javascript截图 jQuery插件imgAreaSelect使用详解
2016/05/04 Javascript
JavaScript toUpperCase()方法使用详解
2016/08/26 Javascript
微信小程序 Toast自定义实例详解
2017/01/20 Javascript
jQuery动态移除和添加背景图片的方法详解
2017/03/07 Javascript
Vue 2.0 服务端渲染入门介绍
2017/03/29 Javascript
jquery实现提示语淡入效果
2017/05/05 jQuery
jQuery绑定事件方法及区别(bind,click,on,live,one)
2017/08/14 jQuery
浅谈angular4 ng-content 中隐藏的内容
2017/08/18 Javascript
webpack vue项目开发环境局域网访问方法
2018/03/20 Javascript
nodejs之koa2请求示例(GET,POST)
2018/08/07 NodeJs
[38:27]完美世界DOTA2联赛PWL S2 Forest vs FTD.C 第二场 11.26
2020/11/30 DOTA
Python赋值语句后逗号的作用分析
2015/06/08 Python
python基于隐马尔可夫模型实现中文拼音输入
2016/04/01 Python
Python并发之多进程的方法实例代码
2018/08/15 Python
浅谈python下tiff图像的读取和保存方法
2018/12/04 Python
实例详解Matlab 与 Python 的区别
2019/04/26 Python
python读写csv文件并增加行列的实例代码
2019/08/01 Python
Django自带日志 settings.py文件配置方法
2019/08/30 Python
pycharm远程连接vagrant虚拟机中mariadb数据库
2020/06/05 Python
荟萃全球保健品:维他购
2018/05/09 全球购物
Gtech官方网站:地毯清洁器、吸尘器及园艺设备
2018/05/23 全球购物
美国第二大连锁药店:Rite Aid
2019/04/03 全球购物
施华洛世奇波兰官网:SWAROVSKI波兰
2019/06/18 全球购物
乐高瑞士官方商店:LEGO CH
2020/08/16 全球购物
Linux如何压缩可执行文件
2013/10/21 面试题
2014年四风个人对照检查及整改措施
2014/10/28 职场文书
股东出资协议书
2016/03/21 职场文书
2019暑假学生安全口号
2019/06/27 职场文书
上帝为你开了一扇窗之Tkinter常用函数详解
2021/06/02 Python
DE1103使用报告
2022/04/05 无线电
Beekeeper Studio开源数据库管理工具比Navicat更炫酷
2022/06/21 数据库