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时间不正确的解决方法
Apr 09 PHP
解析MySql与Java的时间类型
Jun 22 PHP
PHP错误和异长常处理总结
Mar 06 PHP
PHP将两个关联数组合并函数提高函数效率
Mar 18 PHP
Thinkphp使用mongodb数据库实现多条件查询方法
Jun 26 PHP
laravel容器延迟加载以及auth扩展详解
Mar 02 PHP
php实现图片上传、剪切功能
May 07 PHP
Yii2数据库操作常用方法小结
May 04 PHP
PHP观察者模式原理与简单实现方法示例
Aug 25 PHP
PHP设计模式之工厂模式定义与用法详解
Apr 03 PHP
PHP通过get方法获得form表单数据方法总结
Sep 12 PHP
php把文件设置为插件的技巧方法
Feb 03 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 木马攻击防御技巧
2009/06/13 PHP
xss防御之php利用httponly防xss攻击
2014/03/21 PHP
codeigniter中实现一次性加载多个view的方法
2015/03/20 PHP
php利用gd库为图片添加水印
2016/11/09 PHP
通过身份证号得到出生日期和性别的js代码
2009/11/23 Javascript
基于jQuery实现下拉收缩(展开与折叠)特效
2012/12/25 Javascript
取消选中单选框radio的三种方式示例介绍
2013/12/23 Javascript
jQuery中:nth-child选择器用法实例
2014/12/31 Javascript
jQuery实现复选框成对选择及对应取消的方法
2015/03/03 Javascript
jQuery实现的精美平滑二级下拉菜单效果代码
2016/03/28 Javascript
Boostrap基础教程之JavaScript插件篇
2016/09/08 Javascript
js 数据存储和DOM编程
2017/02/09 Javascript
Bootstrap进度条实现代码解析
2017/03/07 Javascript
微信小程序中子页面向父页面传值实例详解
2017/03/20 Javascript
关于Bootstrap按钮组件消除黄框的方法
2017/05/19 Javascript
微信小程序数据存储与取值详解
2018/01/30 Javascript
Vue-Router模式和钩子的用法
2018/02/28 Javascript
Spring boot 和Vue开发中CORS跨域问题解决
2018/09/05 Javascript
JavaScript基于用户照片姓名生成海报
2020/05/29 Javascript
JavaScript this指向相关原理及实例解析
2020/07/10 Javascript
Python递归遍历列表及输出的实现方法
2015/05/19 Python
对python3 urllib包与http包的使用详解
2018/05/10 Python
python pandas消除空值和空格以及 Nan数据替换方法
2018/10/30 Python
django开发post接口简单案例,获取参数值的方法
2018/12/11 Python
用python写测试数据文件过程解析
2019/09/25 Python
K近邻法(KNN)相关知识总结以及如何用python实现
2021/01/28 Python
施华洛世奇英国官网:SWAROVSKI英国
2017/03/13 全球购物
Swisse官方海外旗舰店:澳大利亚销量领先,自然健康品牌
2017/12/15 全球购物
贝佳斯官方网站:Borghese
2020/05/08 全球购物
中科创达面试题
2016/12/28 面试题
结婚保证书范文
2014/04/29 职场文书
房屋维修申请报告
2015/05/18 职场文书
社区志愿服务活动感想
2015/08/07 职场文书
解析在浏览器地址栏输入一个URL后发生了什么
2021/06/21 Servers
JVM钩子函数的使用场景详解
2021/08/23 Java/Android
JavaWeb实现显示mysql数据库数据
2022/03/19 Java/Android