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 相关文章推荐
BBS(php &amp; mysql)完整版(五)
Oct 09 PHP
php5.2.0内存管理改进
Jan 22 PHP
PHP之数组学习
May 29 PHP
php smarty 二级分类代码和模版循环例子
Jun 01 PHP
php获取通过http协议post提交过来xml数据及解析xml
Dec 16 PHP
关于二级目录拖拽排序的实现(源码示例下载)
Apr 26 PHP
使用php 获取时间今天明天昨天时间戳的详解
Jun 20 PHP
PHP中使用glob函数实现一句话删除某个目录下的所有文件
Jul 22 PHP
php中限制ip段访问、禁止ip提交表单的代码分享
Aug 22 PHP
php中json_encode UTF-8中文乱码的更好解决方法
Sep 28 PHP
php实现基于openssl的加密解密方法
Sep 30 PHP
PHP类的自动加载机制实现方法分析
Jan 10 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
Discuz!5的PHP代码高亮显示插件(黑暗中的舞者更新)
2007/01/29 PHP
PHP下使用CURL方式POST数据至API接口的代码
2013/02/14 PHP
php实现的zip文件内容比较类
2014/09/24 PHP
Thinkphp 框架扩展之驱动扩展实例分析
2020/04/27 PHP
JQuery调webservice实现邮箱验证(检测是否可用)
2013/05/21 Javascript
解析JavaScript中点号“.”的多义性
2013/12/02 Javascript
浅析jquery ajax异步调用方法中不能给全局变量赋值的原因及解决方法
2014/01/10 Javascript
jquery判断小数点两位和自动删除小数两位后的数字
2014/03/19 Javascript
js换图片效果可进行定时操作
2014/06/09 Javascript
写给小白的JavaScript引擎指南
2015/12/04 Javascript
老生常谈JavaScript数组的用法
2016/06/10 Javascript
Google 地图API Map()构造器详解
2016/08/06 Javascript
微信小程序 Flex布局详解
2016/10/09 Javascript
js窗口震动小程序分享
2016/11/28 Javascript
JavaScript数组去重的6个方法
2017/01/21 Javascript
使用jQuery和ajax代替iframe的方法(详解)
2017/04/12 jQuery
Bootstrap实现基于carousel.js框架的轮播图效果
2017/05/02 Javascript
彻底理解js面向对象之继承
2018/02/04 Javascript
Vue自定义toast组件的实例代码
2018/08/15 Javascript
angularJs在多个控制器中共享服务数据的方法
2018/09/30 Javascript
JS中的算法与数据结构之字典(Dictionary)实例详解
2019/08/20 Javascript
基于layui轮播图满屏是高度自适应的解决方法
2019/09/16 Javascript
Vue+tracking.js 实现前端人脸检测功能
2020/04/16 Javascript
一起深入理解js中的事件对象
2021/02/06 Javascript
[01:32]完美世界DOTA2联赛10月29日精彩集锦
2020/10/30 DOTA
Python使用cx_Oracle调用Oracle存储过程的方法示例
2017/10/07 Python
wx.CheckBox创建复选框控件并响应鼠标点击事件
2018/04/25 Python
Python OpenCV处理图像之图像直方图和反向投影
2018/07/10 Python
python动态进度条的实现代码
2019/07/03 Python
解决Numpy中sum函数求和结果维度的问题
2019/12/06 Python
python识别验证码图片实例详解
2020/02/17 Python
乐高西班牙官方商店:LEGO Shop ES
2019/12/01 全球购物
如何写一份好的自荐信
2014/01/02 职场文书
岳父生日宴会答谢词
2014/01/13 职场文书
党员个人总结范文
2015/02/14 职场文书
公司欠款证明
2015/06/24 职场文书