详解PHP反射API

news2024/11/15 8:30:20

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 静态方法输出结果是一致的,只是使用方式不同。

首先,构建一个简单的类

<?php

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;
    }
}

使用 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.coloradmin.cn/o/838963.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

掌握 JVM 的参数及配置

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ JVM&#xff08;Java虚拟机&#xff09;是Java编程语言的核心组件之一&#xff0c;它负责执行Java程序&#xff0c;并提供一系列参数和配置选项&#xff0c;可以调整Java程…

探秘企业DevOps一体化平台建设终极形态丨IDCF

笔者从事为企业提供研发效能改进解决方案相关工作十几年&#xff0c;为国内上百家企业提供过DevOps咨询及解决方案落地解决方案&#xff0c;涉及行业包括&#xff1a;金融、通信、制造、互联网、快销等多种行业。 DevOps的核心是研发效能改进&#xff0c;效能的提升离不开强大…

Linux基本开发工具(一)

文章目录 Linux基本开发工具&#xff08;一&#xff09;Linux安装和卸载软件Linux 软件包管理器 yum关于sudo命令关于yum源的换源问题 vim编辑器的使用vim三种模式&#xff08;常见&#xff09;vim的基本操作vim配置 Linux基本开发工具&#xff08;一&#xff09; Linux安装和…

Dubbo中使用netty

技术主题 netty在Dubbo中的使用,主要集中在网络通信上, 技术原理 Dubbo是什么 高性能、轻量级的开源java的RPC框架,提供三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。 Dubbo的传输结构 1、魔数标识符(四个字节),用于区分不同…

机器人开发--兴颂雷达介绍

机器人开发--兴颂雷达介绍 1 介绍2 使用手册参考 1 介绍 佛山市兴颂机器人科技有限公司&#xff08;Hinson&#xff09;是一家集研发、设计、生产、销售机器人(AGV)导航核心零部件、并提供整体运动控制方案的自主创新型国家高新技术企业。 2 使用手册 兴颂激光雷达使…

HDFS中的sequence file

sequence file序列化文件 介绍优缺点格式未压缩格式基于record压缩格式基于block压缩格式 介绍 sequence file是hadoop提供的一种二进制文件存储格式一条数据称之为record&#xff08;记录&#xff09;&#xff0c;底层直接以<key, value>键值对形式序列化到文件中 优…

【flink】开启savepoint

先启动一个任务 flink run -c com.yang.flink.CDCJob test-cdc.jar开启savepoint 命令&#xff1a; flink savepoint JobID 文件地址 flink savepoint e929a11d79bdc5e6f140f2cfb92e1335 file:///workspace/flinkSavepoints/backend这样就开启好了 操作中的错误 详细信…

HTTP——八、确认访问用户身份的认证

HTTP 一、何为认证二、BASIC认证BASIC认证的认证步骤 三、DIGEST认证DIGEST认证的认证步骤 四、SSL客户端认证1、SSL 客户端认证的认证步骤2、SSL 客户端认证采用双因素认证3、SSL 客户端认证必要的费用 五、基于表单认证1、认证多半为基于表单认证2、Session 管理及 Cookie 应…

【ONE·Linux || 基础IO(二)】

总言 文件系统与动静态库相关介绍。 文章目录 总言2、文件系统2.1、背景知识2.2、磁盘管理2.2.1、磁盘文件系统图2.2.2、inode与文件名 2.3、软硬链接 3、动静态库3.1、站在编写库的人的角度&#xff1a;如何写一个库&#xff1f;3.1.1、静态库制作3.1.3、动态库制作 3.2、站在…

初识MySQL数据库之用户管理

目录 一、用户管理 二、用户 1. 用户信息 2. 创建用户 3. 用户登录测试 4. 删除用户 5. 设置用户远端登录 6. 修改密码 6.1 修改当前用户的密码 6.2 root用户修改指定用户的密码 三、权限 1. 数据库中的各个权限含义 2. 给用户授权 3. 查看用户拥有权限 4. 授权…

VSCode---通过ctrl+鼠标滚动改变字体大小

打开设置然后在右边输editor.mouseWheelZoo勾选即可实现鼠标滚动改变字体大小 4.这种设置的字体大小是固定的

Wordpress升级版本后插件和主题常见出错及处理方法整理【持续更新】

Wordpress报错怎么解决&#xff1f; 一般常用的排查方法&#xff1a; 暂时禁用所有插件&#xff1b;将主题更改为默认主题&#xff1b; 修改wp-config.php文件&#xff1b;更新固定链接设置&#xff0c;确保设置正确&#xff1b;检查.htaccess文件是否存在且是否可写&#xf…

IL汇编实现两数相加输出结果

话说前面没有实现IL汇编2数相加&#xff1b;鼓捣了一下&#xff0c;实现的代码如下&#xff1b; .assembly extern mscorlib {}.assembly Test{.ver 1:0:1:0}.module test.exe.method static void main() cil managed{.maxstack 8.entrypoint.locals init (int32 V_0, int3…

数据集成、类的派生树、算子、软件核化。

数据集成、类的派生树、算子、软件核化TOC 数据集成&#xff1a;数据集成首先需要对数据进行分类、组织、排序&#xff0c;然后按照一定的规则合成、展示数据&#xff08;可以是生成式网页数据&#xff09;。对于元宇宙、大数据、大模型、基于搜索的计算、数据集成都是提高效率…

SpringBoot中Redis报错:NOAUTH Authentication required

1、问题 org.springframework.dao.InvalidDataAccessApiUsageException: NOAUTH Authentication required.; nested exception is redis.clients.jedis.exceptions.JedisDataException: NOAUTH Authentication required. … 2、解决 如果提供了密码还没解决&#xff0c;那可能是…

2021-06-16 Multisim74LS161S设计13进制计数器!

缘由求&#xff01;74LS161S设计13进制计数器&#xff01;-其他-CSDN问答

从URL取值传给后端

从URL传值给后端 http://127.0.0.1:8080/blog_content.html?id8点击浏览文章详情&#xff0c;跳转至详情页面 从 url 中拿出文章 id&#xff0c;传给后端 首先拿到url然后判断是否有值&#xff0c;从问号后面取值params.split(&) 以 & 作为分割然后遍历字符数组 param…

SQL分类及通用语法数据类型(超详细版)

一、SQL分类 DDL: 数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库、表、字段&#xff09;DML: 数据操作语言&#xff0c;用来对数据库表中的数据进行增删改DQL: 数据查询语言&#xff0c;用来查询数据库中表的记录DCL: 数据控制语言&#xff0c;用来创建数据库…

在消费级GPU调试LLM的三种方法:梯度检查点,LoRA和量化

LLM的问题就是权重参数太大&#xff0c;无法在我们本地消费级GPU上进行调试&#xff0c;所以我们将介绍3种在训练过程中减少内存消耗&#xff0c;节省大量时间的方法:梯度检查点&#xff0c;LoRA和量化。 梯度检查点 梯度检查点是一种在神经网络训练过程中使动态计算只存储最…

瞄准产业应用,大模型加持的深兰科技AI虚拟数字人落地业务场景

伴随ChatGPT的问世&#xff0c;在技术与商业运作上都日渐发展成熟的AI数字人产业正持续升温。 目前的AI数字人不仅拥有超高“颜值”&#xff0c;同时还拥有更为丰富的、细腻的表情和动作。更有甚者&#xff0c;AI数字人已经具备自定义构建知识图谱、自主对话、不断学习成长的能…