PHP反射实际应用示例


Posted in PHP onApril 03, 2019

本文实例讲述了PHP反射实际应用。分享给大家供大家参考,具体如下:

1.自动生成文档

根据反射的分析类,接口,函数和方法的内部结构,方法和函数的参数,以及类的属性和方法,可以自动生成文档。

<?php
class Student
{
  const NORMAL = 1;
  const FORBIDDEN = 2;
  /**
   * 用户ID
   * @var 类型
   */
  public $id;
  /**
   * 获取id
   * @return int
   */
  public function getId()
  {
    return $this->id;
  }
  public function setId($id = 1)
  {
    $this->id = $id;
  }
}
$ref = new ReflectionClass('Student');
$doc = $ref->getDocComment();
echo $ref->getName() . ':' . getComment($ref) , "<br/>";
echo "属性列表:<br/>";
printf("%-15s%-10s%-40s<br/>", 'Name', 'Access', 'Comment');
$attr = $ref->getProperties();
foreach ($attr as $row) {
  printf("%-15s%-10s%-40s<br/>", $row->getName(), getAccess($row), getComment($row));
}
echo "常量列表:<br/>";
printf("%-15s%-10s<br/>", 'Name', 'Value');
$const = $ref->getConstants();
foreach ($const as $key => $val) {
  printf("%-15s%-10s<br/>", $key, $val);
}
echo "<br/><br/>";
echo "方法列表<br/>";
printf("%-15s%-10s%-30s%-40s<br/>", 'Name', 'Access', 'Params', 'Comment');
$methods = $ref->getMethods();
foreach ($methods as $row) {
  printf("%-15s%-10s%-30s%-40s<br/>", $row->getName(), getAccess($row), getParams($row), getComment($row));
}
// 获取权限
function getAccess($method)
{
  if ($method->isPublic()) {
    return 'Public';
  }
  if ($method->isProtected()) {
    return 'Protected';
  }
  if ($method->isPrivate()) {
    return 'Private';
  }
}
// 获取方法参数信息
function getParams($method)
{
  $str = '';
  $parameters = $method->getParameters();
  foreach ($parameters as $row) {
    $str .= $row->getName() . ',';
    if ($row->isDefaultValueAvailable()) {
      $str .= "Default: {$row->getDefaultValue()}";
    }
  }
  return $str ? $str : '';
}
// 获取注释
function getComment($var)
{
  $comment = $var->getDocComment();
  // 简单的获取了第一行的信息,这里可以自行扩展
  preg_match('/\* (.*) *?/', $comment, $res);
  return isset($res[1]) ? $res[1] : '';
}

输出结果:

Student:
属性列表:
Name Access Comment
id Public 用户ID
常量列表:
Name Value
NORMAL 1
FORBIDDEN 2
方法列表
Name Access Params Comment
getId Public 获取id
setId Public id,Default: 1

2.实现 MVC 架构

现在好多框架都是 MVC 的架构,根据路由信息定位控制器($controller) 和方法($method) 的名称,之后使用反射实现自动调用。

$class = new ReflectionClass(ucfirst($controller) . 'Controller');
$controller = $class->newInstance();
if ($class->hasMethod($method)) {
  $method = $class->getMethod($method);
  $method->invokeArgs($controller, $arguments);
} else {
  throw new Exception("{$controller} controller method {$method} not exists!");
}

3.实现单元测试

一般情况下我们会对函数和类进行测试,判断其是否能够按我们预期返回结果,我们可以用反射实现一个简单通用的类测试用例。

<?php
class Calc
{
  public function plus($a, $b)
  {
    return $a + $b;
  }
  public function minus($a, $b)
  {
    return $a - $b;
  }
}
function testEqual($method, $assert, $data)
{
  $arr = explode('@', $method);
  $class = $arr[0];
  $method = $arr[1];
  $ref = new ReflectionClass($class);
  if ($ref->hasMethod($method)) {
    $method = $ref->getMethod($method);
    $res = $method->invokeArgs(new $class, $data);
    if($res === $assert){
      echo "测试结果正确";
    };
  }
}
testEqual('Calc@plus', 3, [1, 2]);
echo "<br/>";
testEqual('Calc@minus', -1, [1, 2]);

这是类的测试方法,也可以利用反射实现函数的测试方法。

<?php
function title($title, $name)
{
  return sprintf("%s. %s\r\n", $title, $name);
}
$function = new ReflectionFunction('title');
echo $function->invokeArgs(array('Dr', 'Phil'));
?>

这里只是我简单写的一个测试用例,PHPUnit 单元测试框架很大程度上依赖了 Reflection 的特性,可以了解下。

4.配合 DI 容器解决依赖

Laravel 等许多框架都是使用 Reflection 解决依赖注入问题,具体可查看 Laravel 源码进行分析。

下面我们代码简单实现一个 DI 容器演示 Reflection 解决依赖注入问题。

<?php
class DI
{
  protected static $data = [];
  public function __set($k, $v)
  {
    self::$data[$k] = $v;
  }
  public function __get($k)
  {
    return $this->bulid(self::$data[$k]);
  }
  // 获取实例
  public function bulid($className)
  {
    // 如果是匿名函数,直接执行,并返回结果
    if ($className instanceof Closure) {
      return $className($this);
    }
    // 已经是实例化对象的话,直接返回
    if(is_object($className)) {
      return $className;
    }
    // 如果是类的话,使用反射加载
    $ref = new ReflectionClass($className);
    // 监测类是否可实例化
    if (!$ref->isInstantiable()) {
      throw new Exception('class' . $className . ' not find');
    }
    // 获取构造函数
    $construtor = $ref->getConstructor();
    // 无构造函数,直接实例化返回
    if (is_null($construtor)) {
      return new $className;
    }
    // 获取构造函数参数
    $params = $construtor->getParameters();
    // 解析构造函数
    $dependencies = $this->getDependecies($params);
    // 创建新实例
    return $ref->newInstanceArgs($dependencies);
  }
  // 分析参数,如果参数中出现依赖类,递归实例化
  public function getDependecies($params)
  {
    $data = [];
    foreach($params as $param)
    {
      $tmp = $param->getClass();
      if (is_null($tmp)) {
        $data[] = $this->setDefault($param);
      } else {
        $data[] = $this->bulid($tmp->name);
      }
    }
    return $data;
  }
  // 设置默认值
  public function setDefault($param)
  {
    if ($param->isDefaultValueAvailable()) {
      return $param->getDefaultValue();
    }
    throw new Exception('no default value!');
  }
}
class Demo
{
  public function __construct(Calc $calc)
  {
    echo $calc->plus(1, 2);
  }
}
class Calc
{
  public function plus($a, $b)
  {
    return $a + $b;
  }
  public function minus($a, $b)
  {
    return $a - $b;
  }
}
$di = new DI();
$di->calc = 'Calc';
$di->demo = 'Demo';
$di->demo;//输出结果为3

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
php $_SERVER当前完整url的写法
Nov 12 PHP
PHP 采集程序原理分析篇
Mar 05 PHP
UCenter 批量添加用户的php代码
Jul 17 PHP
解析关于java,php以及html的所有文件编码与乱码的处理方法汇总
Jun 24 PHP
ThinkPHP3.1查询语言详解
Jun 19 PHP
php版本的cron定时任务执行器使用实例
Aug 19 PHP
ThinkPHP验证码和分页实例教程
Aug 22 PHP
php给图片添加文字水印方法汇总
Aug 27 PHP
Laravel 实现密码重置功能
Feb 23 PHP
PHP简单实现正则匹配省市区的方法
Apr 13 PHP
PHP实现基于3DES算法加密解密字符串示例
Aug 24 PHP
PHP PDOStatement::rowCount讲解
Feb 01 PHP
ThinkPHP3.2.3框架实现执行原生SQL语句的方法示例
Apr 03 #PHP
从ThinkPHP3.2.3过渡到ThinkPHP5.0学习笔记图文详解
Apr 03 #PHP
PHP快速排序算法实现的原理及代码详解
Apr 03 #PHP
Laravel5.7框架安装与使用学习笔记图文详解
Apr 02 #PHP
Laravel访问出错提示:`Warning: require(/vendor/autoload.php): failed to open stream: No such file or di解决方法
Apr 02 #PHP
Laravel框架运行出错提示RuntimeException No application encryption key has been specified.解决方法
Apr 02 #PHP
Swoole实现异步投递task任务案例详解
Apr 02 #PHP
You might like
用PHP实现小写金额转换大写金额的代码(精确到分)
2012/01/10 PHP
PHP设置一边执行一边输出结果的代码
2013/09/30 PHP
php中mail函数发送邮件失败的解决方法
2014/12/24 PHP
PHP递归调用数组值并用其执行指定函数的方法
2015/04/01 PHP
typecho插件编写教程(三):保存配置
2015/05/28 PHP
PHP中$_SERVER使用说明
2015/07/05 PHP
记录一次排查PHP脚本执行卡住的问题
2016/12/27 PHP
php中Swoole的热更新实现代码实例
2021/03/04 PHP
Jquery进度条插件 Progress Bar小问题解决
2011/07/12 Javascript
jquery实现清新实用的网页菜单效果
2015/08/28 Javascript
js数组常用操作方法小结(增加,删除,合并,分割等)
2016/08/02 Javascript
微信小程序 教程之wxapp视图容器 scroll-view
2016/10/19 Javascript
js实现表格筛选功能
2017/01/18 Javascript
手把手教你把nodejs部署到linux上跑出hello world
2017/06/19 NodeJs
vue不通过路由直接获取url中参数的方法示例
2017/08/24 Javascript
Angular5集成eventbus的示例代码
2018/07/19 Javascript
Webpack 4如何动态切割JS注入文件名详解
2019/07/09 Javascript
微信小程序全局变量改变监听的实现方法
2019/07/15 Javascript
解决layui使用layui-icon出现默认图标的问题
2019/09/11 Javascript
使用typescript快速开发一个cli的实现示例
2020/12/09 Javascript
js基于canvas实现时钟组件
2021/02/07 Javascript
python实现简单中文词频统计示例
2017/11/08 Python
Python读取分割压缩TXT文本文件实例
2020/02/14 Python
Python 日期与时间转换的方法
2020/08/01 Python
美国户外运动商店:Sun & Ski
2018/08/23 全球购物
CNC数控操作工岗位职责
2013/11/19 职场文书
喷漆工的岗位职责
2014/03/17 职场文书
大学班级计划书
2014/04/29 职场文书
在职党员进社区活动总结
2014/07/05 职场文书
乡镇群众路线教育实践活动整改措施
2014/10/04 职场文书
反对四风自我剖析材料
2014/10/07 职场文书
学校政风行风评议心得体会
2014/10/21 职场文书
2014年档案室工作总结
2014/12/01 职场文书
2016年大学自主招生自荐信范文
2015/03/24 职场文书
《语言的突破》读后感3篇
2019/12/12 职场文书
MySQL中TIMESTAMP类型返回日期时间数据中带有T的解决
2022/12/24 MySQL