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 相关文章推荐
使用数据库保存session的方法
Oct 09 PHP
PHP不用第三变量交换2个变量的值的解决方法
Jun 02 PHP
js限制checkbox勾选的个数以及php获取多个checkbbox的方法深入解析
Jul 18 PHP
php实现检查文章是否被百度收录
Jan 27 PHP
php获取网页上所有链接的方法
Apr 03 PHP
Zend Framework生成验证码并实现验证码验证功能(附demo源码下载)
Mar 22 PHP
Yii2 RESTful中api的使用及开发实例详解
Jul 06 PHP
微信利用PHP创建自定义菜单的方法
Aug 01 PHP
PHP Callable强制指定回调类型的方法
Aug 30 PHP
CI框架表单验证实例详解
Nov 21 PHP
PHP工厂模式简单实现方法示例
May 23 PHP
解决laravel资源加载路径设置的问题
Oct 14 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数组输出html表格的方法
2014/02/24 PHP
PHP实现的操作数组类库定义与用法示例
2019/05/24 PHP
PHP实现关键字搜索后描红功能示例
2019/07/03 PHP
window.open()弹出居中的窗口
2007/02/01 Javascript
Javascript 面向对象 重载
2010/05/13 Javascript
js 复制或插入Html的实现方法小结
2010/05/19 Javascript
jQuery EasyUI API 中文文档 - ComboBox组合框
2011/10/07 Javascript
javascript ready和load事件的区别示例介绍
2013/08/30 Javascript
JavaScript之IE的fireEvent方法详细解析
2013/11/20 Javascript
JS应用正则表达式转换大小写示例
2014/09/18 Javascript
jqGrid表格应用之新增与删除数据附源码下载
2015/12/02 Javascript
jQuery+css实现的换页标签栏效果
2016/01/27 Javascript
Windows 系统下设置Nodejs NPM全局路径
2016/04/26 NodeJs
AngularJS Ajax详解及示例代码
2016/08/17 Javascript
JS实现动画兼容性的transition和transform实例分析
2016/12/13 Javascript
node.JS md5加密中文与php结果不一致的解决方法
2017/05/05 Javascript
Web开发使用Angular实现用户密码强度判别的方法
2017/09/27 Javascript
jQuery+ajax读取json数据并按照价格排序示例
2018/03/28 jQuery
微信小程序云开发实现云数据库读写权限
2019/05/17 Javascript
Vue项目总结之webpack常规打包优化方案
2019/06/06 Javascript
Vue+Koa2+mongoose写一个像素绘板的实现方法
2019/09/10 Javascript
15 分钟掌握vue-next响应式原理
2019/10/13 Javascript
vue实现折线图 可按时间查询
2020/08/21 Javascript
Python学习笔记_数据排序方法
2014/05/22 Python
举例区分Python中的浅复制与深复制
2015/07/02 Python
Mac 上切换Python多版本
2017/06/17 Python
解决nohup重定向python输出到文件不成功的问题
2018/05/11 Python
Python 窗体(tkinter)按钮 位置实例
2019/06/13 Python
澳洲女装时尚在线:Blue Bungalow
2018/05/05 全球购物
自我评价正确写法范文
2013/12/10 职场文书
实习单位鉴定评语
2014/04/26 职场文书
2015国庆节66周年标语
2015/07/30 职场文书
pytorch 预训练模型读取修改相关参数的填坑问题
2021/06/05 Python
教你用Python matplotlib库制作简单的动画
2021/06/11 Python
python保存图片的四个常用方法
2022/02/28 Python
Windows Server 2019 配置远程控制以及管理方法
2022/04/28 Servers