详解PHP反射API

PHP中的反射API就像Java中的java.lang.reflect包一样。它由一系列可以分析属性、方法和类的内置类组成。它在某些方面和对象函数相似,比如get_class_vars(),但是更加灵活,而且可以提供更多信息。反射API也可与PHP最新的面向对象特性一起工作,如访问控制、接口和抽象类。旧的类函数则不太容易与这些新特性一起使用。看过框架源码的朋友应该对PHP的反射机制有一定的了解,像是依赖注入,对象池,类加载,一些设计模式等等,都用到了反射机制。

反射API的部分类

使用反射API这些类,可以获得在运行时访问对象、函数和脚本中的扩展的信息。通过这些信息可以用来分析类或者构建框架。

描         述
Reflection为类的摘要信息提供静态函数export()
ReflectionClass类信息和工具
ReflectionMethod类方法信息和工具
ReflectionParameter方法参数信息
ReflectionProperty类属性信息
ReflectionFunction函数信息和工具
ReflectionExtensionPHP扩展信息
ReflectionException错误类

获取类的信息

我们在工作中使用过一些用于检查类属性的函数,例如:get_class_methods、getProduct等。这些方法对获取详细类信息有很大的局限性。

我们可以通过反射API类:Reflection 和 ReflectionClass 提供的静态方法 export 来获取类的相关信息, export 可以提供类的几乎所有的信息,包括属性和方法的访问控制状态、每个方法需要的参数以及每个方法在脚本文档中的位置。这两个工具类, export 静态方法输出结果是一致的,只是使用方式不同。

首先,构建一个简单的类

<?phpclass Student {public    $name;protected $age;private   $sex;public function __construct($name, $age, $sex){$this->setName($name);$this->setAge($age);$this->setSex($sex);}public function setName($name){$this->name = $name;}protected function setAge($age){$this->age = $age;}private function setSex($sex){$this->sex = $sex;}
}

使用 ReflectionClass::export() 获取类信息

ReflectionClass::export('Student');

打印结果:

 ReflectionClass::export() 输出

ReflectionClass类提供了非常多的工具方法,官方手册给的列表如下:

 ReflectionClass

使用 Reflection::export() 获取类信息 

$prodClass = new ReflectionClass('Student');
Reflection::export($prodClass);

打印结果

 Reflection::export() 输出

创建 ReflectionClass对象后,就可以使用 Reflection 工具类输出 Student 类的相关信息。Reflection::export() 可以格式化和输出任何实现 Reflector 接口的类的实例。

检查类

前面我们了解的 ReflectionClass 工具类,知道此类提供了很多的工具方法用于获取类的信息。例如,我们可以获取到 Student 类的类型,是否可以实例化

工具函数

function classData(ReflectionClass $class) {$details = '';$name = $class->getName();          // 返回要检查的类名if ($class->isUserDefined()) {      // 检查类是否由用户定义$details .= "$name is user defined" . PHP_EOL;}if ($class->isInternal()) {         // 检查类是否由扩展或核心在内部定义$details .= "$name is built-in" . PHP_EOL;}if ($class->isInterface()) {        // 检查类是否是一个接口$details .= "$name is interface" . PHP_EOL;}if ($class->isAbstract()) {         // 检查类是否是抽象类$details .= "$name is an abstract class" . PHP_EOL;}if ($class->isFinal()) {            // 检查类是否声明为 final$details .= "$name is a final class" . PHP_EOL;}if ($class->isInstantiable()) {     // 检查类是否可实例化$details .= "$name can be instantiated" . PHP_EOL;} else {$details .= "$name can not be instantiated" . PHP_EOL;}return $details;
}$prodClass = new ReflectionClass('Student');
print classData($prodClass);

打印结果

Student is user defined
Student can be instantiated

除了获取类的相关信息,还可以获取 ReflectionClass 对象提供自定义类所在的文件名及文件中类的起始和终止行等相关源代码信息。

function getClassSource(ReflectionClass $class) {$path  = $class->getFileName();  // 获取类文件的绝对路径$lines = @file($path);           // 获得由文件中所有行组成的数组$from  = $class->getStartLine(); // 提供类的起始行$to    = $class->getEndLine();   // 提供类的终止行$len   = $to - $from + 1;return implode(array_slice($lines, $from - 1, $len));
}$prodClass = new ReflectionClass('Student');
var_dump(getClassSource($prodClass));打印结果
string 'class Student {public    $name;protected $age;private   $sex;public function __construct($name, $age, $sex){$this->setName($name);$this->setAge($age);$this->setSex($sex);}public function setName($name){$this->name = $name;}protected function setAge($age){$this->age = $age;}private function setSex($sex){$this->sex = $sex;}
}
' (length=486)

我们看到 getClassSource 接受一个 ReflectionClass 对象作为它的参数,并返回相应类的源代码。该函数忽略了错误处理,在实际中应该要检查参数和结果代码!

检查方法

类似于检查类,ReflectionMethod 对象可以用于检查类中的方法。

获得 ReflectionMethod 对象的方法有两种:

第一种是通过 ReflectionClass::getMethods() 获得 ReflectionMethod 对象的数组,这种方式的好处是不用提前知道方法名,会返回类中所有方法的 ReflectionMethod  对象。

第二种是直接使用 ReflectionMethod  类实例化对象,这种方式只能获取一个类方法对象,需要提前知道方法名。

ReflectionMethod 对象的工具方法:

 ReflectionMethod

ReflectionClass::getMethods()

我们可以通过 ReflectionClass::getMethods() 获得 ReflectionMethod 对象的数组。

$prodClass = new ReflectionClass('Student');
$methods = $prodClass->getMethods();
var_dump($methods);

打印结果

array (size=4)0 => &object(ReflectionMethod)[2]public 'name' => string '__construct' (length=11)public 'class' => string 'Student' (length=7)1 => &object(ReflectionMethod)[3]public 'name' => string 'setName' (length=7)public 'class' => string 'Student' (length=7)2 => &object(ReflectionMethod)[4]public 'name' => string 'setAge' (length=6)public 'class' => string 'Student' (length=7)3 => &object(ReflectionMethod)[5]public 'name' => string 'setSex' (length=6)public 'class' => string 'Student' (length=7)

可以看到我们获取到了 Student 的 ReflectionMethod 对象数组,每个元素是一个对象,其中有两个公共的属性,name 为方法名,class 为所属类。我们可以调用对象方法来获取方法的信息。

ReflectionMethod

直接使用 ReflectionMethod 类获取类方法有关信息

$method = new ReflectionMethod('Student', 'setName');
var_dump($method);

打印结果

object(ReflectionMethod)[1]public 'name' => string 'setName' (length=7)public 'class' => string 'Student' (length=7)

注意

在PHP5中,如果被检查的方法只返回对象(即使对象是通过引用赋值或传递的),那么 ReflectionMethod::retursReference() 不会返回 true。只有当被检测的方法已经被明确声明返回引用(在方法名前面有&符号)时,ReflectionMethod::returnsReference() 才返回 true。

检查方法参数

在PHP5中,声明类方法时可以限制参数中对象的类型,因此检查方法的参数变得非常必要。

类似于检查方法,ReflectionParameter 对象可以用于检查类中的方法,该对象可以告诉你参数的名称,变量是否可以按引用传递,还可以告诉你参数类型提示和方法是否接受空值作为参数。

获得 ReflectionParameter 对象的方法有同样两种,这和获取 ReflectionMethod 对象非常类似:

第一种是通过 ReflectionMethod::getParameters() 方法返回 ReflectionParameter 对象数组,这种方法可以获取到一个方法的全部参数对象。

第二种是直接使用 ReflectionParameter  类实例化获取对象,这种方法只能获取到单一参数的对象。

ReflectionParameter 对象的工具方法:

 ReflectionParameter

ReflectionMethod::getParameters()

同获取方法,此方法会返回一个数组,包含方法每个参数的 ReflectionParameter 对象

$method = new ReflectionMethod('Student', 'setName');
$params = $method->getParameters();
var_dump($params);

打印结果

array (size=1)0 => &object(ReflectionParameter)[2]public 'name' => string 'name' (length=4)

ReflectionParameter

我们来了解一下这种方式,为了更好的理解,我修改一下 Student 类的 setName方法,增加两个参数 a, b

...public function setName($name, $a, $b){$this->name = $name;}
...

首先我们看一下 ReflectionParameter 类的构造方法

public ReflectionParameter::__construct ( string $function , string $parameter )

可以看到该类实例化时接收两个参数:

$function:当需要获取函数为公共函数时只需传函数名称即可。当该函数是某个类方法时,需要传递一个数组,格式为:array('class', 'function')。

$parameter:这个参数可以传递两种,第一种为参数名(无$符号),第二种为参数索引。注意:无论是参数名还是索引,该参数都必须存在,否则会报错。

下面举例:

$params = new ReflectionParameter(array('Student', 'setName'), 1);
var_dump($params);

打印结果

object(ReflectionParameter)[1]public 'name' => string 'a' (length=1)

我们再定义一个函数测试一下

function foo($a, $b, $c) { }
$reflect = new ReflectionParameter('foo', 'c');
var_dump($reflect);

打印结果

object(ReflectionParameter)[2]public 'name' => string 'c' (length=1)

结语

php的反射API功能非常的强大,它可以将一个类的详细信息获取出来。我们可以通过反射API编写个类来动态调用Module对象,该类可以自由加载第三方插件并集成进已有的系统。而不需要把第三方的代码硬编码进原有的代码中。虽然实际开发中使用反射情况比较少,但了解反射API对工作中对代码结构的了解和开发业务模式帮助还是非常大的。此篇博文断断续续的写了很久(主要就是懒!),如有错误与不足欢迎指正,建议!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/54101.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

腾讯云-宝塔添加MySQL数据库

1. 数据库菜单 2. 添加数据库 3. 数据库添加成功 4. 上传数据库文件 5. 导入数据库文件 6. 开启数据库权限 7. 添加安全组 (宝塔/腾讯云) 8. Navicat 连接成功

Vue [Day3]

Vue生命周期 生命周期四个阶段 生命周期函数&#xff08;钩子函数&#xff09; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale…

常见的设计模式(超详细)

文章目录 单例模式饿汉式单例模式懒汉式单例模式双重检索单例模式 工厂模式简单工厂模式工厂&#xff08;方法&#xff09;模式抽象工厂模式 原型模式代理模式 单例模式 确保一个类只有一个实例&#xff0c;并且自行实例化并向整个系统提供这个实例。 饿汉式单例模式 饿汉式单…

升级到mybatis-plus,系统启动的一些问题

在分表后mybatis-plus删除操作失效等问题处理 mybatis-plus 旧系统重构遇到的种种问题 在这三篇文章中&#xff0c;我花了近1个月时间重构了28个微服务&#xff0c;当中遇到的一些问题&#xff0c;但是发布到pretest环境&#xff0c;却还有启动问题&#xff0c;看来系统重构不是…

k8s之Helm安装

一、最快安装–官网提供的脚本–默认获取最新版本 cd /usr/local/src/ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 chmod 700 get_helm.sh ./get_helm.shhelm search hub wordpresssource <(helm completion bash) h…

【Linux操作系统】相关问题和知识点总结~

【Linux操作系统】相关问题和知识点总结~&#x1f60e; 前言&#x1f64c;在Linux中&#xff0c;查看CPU使用效率top命令mpstat指令sar指令vmstat指令 如何查看Linux的内核版本grep指令&#xff08;用于在文件内容中&#xff0c;查找满足条件的内容&#xff09;如何批量删除当前…

Django Rest_Framework(三)

文章目录 1. 认证Authentication2. 权限Permissions使用提供的权限举例自定义权限 3. 限流Throttling基本使用可选限流类 4. 过滤Filtering5. 排序Ordering6. 分页Pagination可选分页器 7. 异常处理 ExceptionsREST framework定义的异常 8. 自动生成接口文档coreapi安装依赖设置…

uni-app:分页实现多选功能

效果 代码解析 一、标签-列表 <view class"item_all" v-for"(item, index) in info" :key"index"><view class"position parameter-info text-over" :class"{checked_parameter: item.checked}" :data-id"i…

【设计模式】单例模式

什么是单例模式&#xff1f; 保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点 单例模式的应用场景 1.整个程序的运行中只允许有一个类的实例&#xff1b; 2.需要频繁实例化然后销毁的对象。 3.创建对象时耗时过多或者耗资源过多&#xff0c;但又经常用到…

RabbitMQ(一) - 基本结构、SpringBoot整合RabbitMQ、工作队列、发布订阅、直接、主题交换机模式

RabbitMQ结构 Publisher &#xff1a; 生产者 Queue: 存储消息的容器队列&#xff1b; Consumer:消费者 Connection&#xff1a;消费者与消息服务的TCP连接 Channel:信道&#xff0c;是TCP里面的虚拟连接。例如&#xff1a;电缆相当于TCP&#xff0c;信道是一条独立光纤束&…

如何隐藏开源流媒体EasyPlayer.js视频H.265播放器的实时录像按钮?

目前我们TSINGSEE青犀视频所有的视频监控平台&#xff0c;集成的都是EasyPlayer.js版播放器&#xff0c;它属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;包括WebSocket-FLV、HTTP-FLV&#xff0c;HLS&#xff08;m3u8&#x…

通讯协议032——全网独有的OPC HDA知识一之聚合(一)

本文简单介绍OPC HDA规范的基本概念&#xff0c;更多通信资源请登录网信智汇(wangxinzhihui)。 本节旨在详细说明HDA聚合的要求和性能。其目的是使HDA聚合标准化&#xff0c;以便HDA客户端能够可靠地预测聚合计算的结果并理解其含义。如果用户需要聚合中的自定义功能&#xff…