热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PHP反射基础知识回顾

这篇文章主要介绍了PHP反射的相关资料,帮助大家回顾和理解PHP的相关知识,感兴趣的朋友可以了解下

反射是编程语言的高级特性,能在运行时让代码有感知代码的能力。PHP自5起支持反射机制,其是各种OOP框架底层实现的重要支撑。

反射

从一个简单的例子理解反射:人有五官四肢,但鲜有人清楚人体内部的经脉走向、骨骼构造。如果你修仙顺利,在丹田深处练出元婴,那么就通过元婴透析身体内部的构造。理解内部构造后,还可以让元婴指引体内真气在经脉的流向,早日修成正果。

如其名,反射是(从镜子里)照出自身。我们写代码,告诉代码怎么运行,事件发生在编译期。代码运行期间,代码如何知道自己的结构以及能力呢?反射机制相当于代码的元婴,使代码能够感知自身结构,并可(部分)改变运行行为。

与运行时类型信息(Runtime Type Informatiion, RTTI)不同,反射重点在运行时检测、感知、改变自身的结构和行为。反射是元编程(metaprogramming)的重要组成部分。

PHP反射API

反射不是语法分析,不操作表达式、代码语句。反射获取的是代码的结构,即函数、类这些构件的结构。PHP中的反射API均以Reflection开头(接口Reflector除外),重点在函数和类两种结构。而函数可以看成类的成员函数(多一个隐式的this参数)或者静态成员函数(public类型),所以了解反射API可从类信息的ReflectionClass开始。

ReflectionClass提供了以下获取类基本信息的接口:

  1. getProperties:获取成员变量/属性,返回一个ReflectionProperty数组;ReflectionProperty类中有对属性详细说明的API:是否默认属性(isDefault),是否私有属性(isPrivate)等。同时ReflectionClass还提供获取特定类别属性的API:getDefaultPropertiesgetStaticProperties
  2. getConstants:获取类中定义的常量;
  3. getMethods:获取类中定义的方法,返回一个ReflectionMethod数组;ReflectionMethod将在下文讲解;
  4. getInterfaces:获取类实现的接口;
  5. getParentClass:获取父类的ReflectionClass实例。

在反射中,类、接口、特性不分家,所以ReflectionClass提供类型判定API:isInterfaceisTrait

除了以上基本信息,ReflectionClass(包括ReflectionMethod/ReflectionFunction)还提供了一些不可思议的能力:

  1. getDocComment:获取类的文档注释信息;
  2. getFilename:获取类定义的文件;
  3. getStartLine: 获取类定义的起始行号;
  4. getEndLine: 获取类定义的结束行号;
  5. getModifiers:获取类定义的修饰符,其意义名字可通过Reflection::getModifierNames得到,例如:abstract,final。

如果说前述的类结构信息可以通过现有的API获取(method_exits/property_exits等),上面列出的功能基本上只能通过反射API获取(PHP文件中定义的类并且知道定义文件,可以利用token_get_all得到相同结果,但是实现非常复杂)。这些行为发生在运行期间。由此可见反射API在分析类结构信息功能上的强大。

除了ReflectionClassReflectionMethodReflectionFunction是另外反射中另外两个重要的类。函数(function)定义在类外部,方法(method)定义在类内部,两者其实同源,在反射API中有共同的父类:ReflectionFunctionAbstractReflectionFunctionAbstract有两者的大部分API,并且基本上是最重要的API。其中最值得关注的是其参数信息的API:getParameters。其获取函数的参数信息,返回一个ReflectionParameter数组。结合getParametersReflectionParameter,函数(方法)的结构基本上就清晰了。

API操作

知道人体构造和体内真气分布,你可以引导真气到手指,练成一阳指、六脉神剑、弹指神通、九阴白骨爪等;也可以让真气汇聚,冲破任督二脉,开辟洞天;还可以逆转全身经脉,练成蛤蟆功…内省的好处可见一斑。

反射让代码感知自身结构,有什么好处呢?反射API提供了三种在运行时对代码操作的能力:

  1. 设置访问控制权:setAccessible。可获取私有的方法/属性。注意:setAccessible只是让方法/成员变量可以invoke/getValue/setValue,并不代表类定义的访问存取权限改变;
  2. 调用函数/方法:invoke/invokeArgs。配合获取函数参数的API,可以安全的传参和调用函数,call_user_func(_array)的增强版;
  3. 不依赖构造函数生成实例:newInstanceWithoutConstructor

以单例来说一下反射API的功能,单例类代码如下:

# foo.php
class Foo {
 private static $id;
 private static $instance;

 private function __construct() {
 ++ self::$id;
 fwrite(STDOUT, "construct, instance id: " . self::$id . "\n");
 }

 public static function getSingleton() {
 if (self::$instance === null) {
 self::$instance = new self();
 }
 return self::$instance;
 }
}

Foo类中,构造函数是私有,获取实例只能通过getSingleton方法,并且获取到的是单例。但在反射API加持下,能获取多个实例:

$instance1 = Foo::getSingleton();
var_dump($instance1);

$class = new ReflectionClass("Foo");
$cOnstructor= $class->getConstructor();
if ((ReflectionProperty::IS_PUBLIC & $constructor->getModifiers()) === 0) {
 $constructor->setAccessible(true);
}
$instance2 = $class->newInstanceWithoutConstructor();
$constructor->invoke($instance2);
var_dump($instance2);

# 脚本执行结果
construct, instance id: 1
object(Foo)#1 (0) {
}
construct, instance id: 2
object(Foo)#4 (0) {
}

我们成功的生成了两个实例,并调用构造函数完成对象初始化。如果没有反射API,这几乎是不可能完成的工作。

除了这三种操作,反射API几乎已无在运行时动态改变代码的行为。但作为动态语言,PHP内置了将数据转换成代码执行的能力(例如create_function/eval、动态函数名调用)。而PHP的好基友Javascript则可以随时在运行时改变任意函数的行为:

PHP作为最好的语言,理应能做到在运行时动态增减/改变函数定义。这就需要用到另一个PHP核心开发者“Dmitry Zenovich”打造的大杀器:runkit拓展。这部分内容不属于反射,加之本人了解不深,不再详述。

对比

整理一下反射API和函数式API在功能上的差异:

功能 函数式API 反射API
函数是否存在 function_exists ReflectionFunction
类是否存在 class_exits ReflectionClass
方法是否存在 method_exits ReflectionMethod
变量/属性是否存在 property_exits ReflectionProperty
获取类变量 get_class_vars ReflectionClass::getProperties
获取类方法 get_class_methods ReflectionClass::getMethods
获取类常量 ReflectionClass::RegetReflectionConstant(s)
获取函数/方法参数信息 ReflectionFunction/Method::getParameters
获取函数/方法返回值 ReflectionFunction/Method::getReturnType
类使用的特性 class_uses ReflectionClass::getTraits
获取父类 class_parents ReflectionClass::getParentClass
获取类实现的接口 class_implements ReflectionClass::getInterfaceNames
获取类所在名字空间 __NAMESPACE__ ReflectionClass::getNamespaceName
函数调用 call_user_func(_array) ReflectionMethod(Function)::invoke(Args)
获取类名 __CLASS__/::class ReflectionClass::getName
获取函数名 __METHOD__/__FUNCTION__ ReflectionFunction/Method::getName
获取类/常量/变量/方法修饰符 ReflectionClass/Constant/Property/Method::getModifiers
获取所在文件 __FILE__ ReflectionClass/Constant/Function/Method::getFileName
获取所在行(范围) ReflectionClass/Function/Method::getStartLine/getEndLine
获取文档 ReflectionClass/Function/Method::getDocComment
extension_loaded ReflectionZendExtension
拓展 get_loaded_extensions ReflectionExtension
get_extension_funcs

从上表可以看出反射API较函数式API能提供更全面的信息。还需要注意到__FILE__这类魔术常量是编译期的工作,不是运行时的能力。

同时给出RTTI的函数式API和反射API在功能上的差异:

功能 函数式API 反射API
类型判断 is_int/is_bool/is_array等
获取对象的类名 get_class ReflectionObject::getName
获取对象父类 get_parent_class ReflectionObject::getParentClass
类型/继承检测 instanceof/is_a/is_subclass_of ReflectionObject::isInstance/isSubclassOf
生成器 ReflectionGenerator

总结

本文对PHP中的反射机制做了简要总结,并与在运行时获取代码信息的函数式API做了对比。即使你token_get_all用得再熟练,preg_match等文本操作用得再顺手,反射API仍有其独到一面,值得了解。如本人之前博文“PHP中的重载”所言,有了反射,function_exits/class_exitscall_user_func这些函数应该可以退休。但是考虑到兼容、使用便利、运行效率等因素,许多框架仍然依赖这些API。

感谢阅读,欢迎指正!

以上就是PHP反射知识回顾的详细内容,更多关于PHP 反射的资料请关注其它相关文章!


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
  • GPT-3发布,动动手指就能自动生成代码的神器来了!
    近日,OpenAI发布了最新的NLP模型GPT-3,该模型在GitHub趋势榜上名列前茅。GPT-3使用的数据集容量达到45TB,参数个数高达1750亿,训练好的模型需要700G的硬盘空间来存储。一位开发者根据GPT-3模型上线了一个名为debuid的网站,用户只需用英语描述需求,前端代码就能自动生成。这个神奇的功能让许多程序员感到惊讶。去年,OpenAI在与世界冠军OG战队的表演赛中展示了他们的强化学习模型,在限定条件下以2:0完胜人类冠军。 ... [详细]
  • Android实战——jsoup实现网络爬虫,糗事百科项目的起步
    本文介绍了Android实战中使用jsoup实现网络爬虫的方法,以糗事百科项目为例。对于初学者来说,数据源的缺乏是做项目的最大烦恼之一。本文讲述了如何使用网络爬虫获取数据,并以糗事百科作为练手项目。同时,提到了使用jsoup需要结合前端基础知识,以及如果学过JS的话可以更轻松地使用该框架。 ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • 在开发中,有时候一个业务上要求的原子操作不仅仅包括数据库,还可能涉及外部接口或者消息队列。此时,传统的数据库事务无法满足需求。本文介绍了Java中如何利用java.lang.Runtime.addShutdownHook方法来保证业务线程的完整性。通过添加钩子,在程序退出时触发钩子,可以执行一些操作,如循环检查某个线程的状态,直到业务线程正常退出,再结束钩子程序。例子程序展示了如何利用钩子来保证业务线程的完整性。 ... [详细]
  • 20211101CleverTap参与度和分析工具功能平台学习/实践
    1.应用场景主要用于学习CleverTap的使用,该平台主要用于客户保留与参与平台.为客户提供价值.这里接触到的原因,是目前公司用到该平台的服务~2.学习操作 ... [详细]
  • 判断数组是否全为0_连续子数组的最大和的解题思路及代码方法一_动态规划
    本文介绍了判断数组是否全为0以及求解连续子数组的最大和的解题思路及代码方法一,即动态规划。通过动态规划的方法,可以找出连续子数组的最大和,具体思路是尽量选择正数的部分,遇到负数则不选择进去,遇到正数则保留并继续考察。本文给出了状态定义和状态转移方程,并提供了具体的代码实现。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 如何实现JDK版本的切换功能,解决开发环境冲突问题
    本文介绍了在开发过程中遇到JDK版本冲突的情况,以及如何通过修改环境变量实现JDK版本的切换功能,解决开发环境冲突的问题。通过合理的切换环境,可以更好地进行项目开发。同时,提醒读者注意不仅限于1.7和1.8版本的转换,还要适应不同项目和个人开发习惯的需求。 ... [详细]
author-avatar
龍城灬任少
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有