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脚本的10个技巧(7)
Oct 09 PHP
php检测图片木马多进制编程实践
Apr 11 PHP
使用php判断服务器是否支持Gzip压缩功能
Sep 24 PHP
PHP字符串word末字符实现大小写互换的方法
Nov 10 PHP
PHP使用pcntl_fork实现多进程下载图片的方法
Dec 16 PHP
使用symfony命令创建项目的方法
Mar 17 PHP
thinkphp中的url跳转用法分析
Jul 12 PHP
CI框架封装的常用图像处理方法(缩略图,水印,旋转,上传等)
Nov 22 PHP
ThinkPHP 3.2.2实现事务操作的方法
May 05 PHP
PHP基于MySQLI函数封装的数据库连接工具类【定义与用法】
Aug 11 PHP
PHP生成二维码与识别二维码的方法详解【附源码下载】
Mar 07 PHP
php设计模式之策略模式实例分析【星际争霸游戏案例】
Mar 26 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获取文件夹内文件数的方法
2015/03/12 PHP
详解php中生成标准uuid(guid)的方法
2019/04/28 PHP
PHP中16个高危函数整理
2019/09/19 PHP
js前台分页显示后端JAVA数据响应
2013/03/18 Javascript
如何在一个页面显示多个百度地图
2013/04/07 Javascript
JQuery制作的放大效果的popup对话框(未添加任何jquery plugin)分享
2013/04/28 Javascript
extjs 时间范围选择自动判断的实现代码
2014/06/24 Javascript
Google官方支持的NodeJS访问API,提供后台登录授权
2014/07/29 NodeJs
深入学习JavaScript对象
2015/10/13 Javascript
jQuery+PHP+MySQL二级联动下拉菜单实例讲解
2015/10/27 Javascript
JavaScript希尔排序、快速排序、归并排序算法
2016/05/08 Javascript
jQuery内容过滤选择器用法示例
2016/09/09 Javascript
js控制台输出的方法(详解)
2016/11/26 Javascript
基于jQuery实现数字滚动效果
2017/01/16 Javascript
AngularJS 单选框及多选框的双向动态绑定
2017/04/20 Javascript
详解vue2.0+axios+mock+axios-mock+adapter实现登陆
2018/07/19 Javascript
Vue中通过Vue.extend动态创建实例的方法
2019/08/13 Javascript
JS检索下拉列表框中被选项目的索引号(selectedIndex)
2019/12/17 Javascript
Python兔子毒药问题实例分析
2015/03/05 Python
Python面向对象编程中的类和对象学习教程
2015/03/30 Python
详解Python安装scrapy的正确姿势
2018/06/26 Python
用Python和WordCloud绘制词云的实现方法(内附让字体清晰的秘笈)
2019/01/08 Python
python for和else语句趣谈
2019/07/02 Python
使用python socket分发大文件的实现方法
2019/07/08 Python
python如何实现递归转非递归
2021/02/25 Python
详解CSS3的box-shadow属性制作边框阴影效果的方法
2016/05/10 HTML / CSS
墨尔本复古时尚品牌:Dangerfield
2018/12/12 全球购物
ECOSUSI官网:女式皮革背包
2019/09/27 全球购物
哥伦比亚加拿大官网:Columbia Sportswear Canada
2020/09/07 全球购物
新闻专业大学生找工作的自我评价
2013/10/30 职场文书
师范毕业生求职信
2014/07/11 职场文书
2014年社区重阳节活动策划方案
2014/09/16 职场文书
总账会计岗位职责
2015/04/02 职场文书
罚款通知怎么写
2015/04/22 职场文书
2016年中学法制宣传日活动总结
2016/04/01 职场文书
Python写情书? 10行代码展示如何把情书写在她的照片里
2022/04/21 Python