内容简介:转载请注明文章出处:反射是编程语言的高级特性,能在运行时让代码有从一个简单的例子理解反射:人有五官四肢,但鲜有人清楚人体内部的经脉走向、骨骼构造。如果你修仙顺利,在丹田深处练出元婴,那么就通过元婴透析身体内部的构造。理解内部构造后,还可以让元婴指引体内真气在经脉的流向,早日修成正果。
转载请注明文章出处: https://tlanyan.me/php-review-reflection
PHP回顾系列目录
- PHP基础
- web请求
- cookie
- web响应
- session
- 数据库操作
- 加解密
- Composer
- 创建自己的Composer包
- 发送邮件
- IO
- 流
- Socket编程
- 多进程编程
- 执行流程及相关概念
- 协程
反射是编程语言的高级特性,能在运行时让代码有 感知代码 的能力。PHP自5起支持反射机制,其是各种OOP框架底层实现的重要支撑。
反射
从一个简单的例子理解反射:人有五官四肢,但鲜有人清楚人体内部的经脉走向、骨骼构造。如果你修仙顺利,在丹田深处练出元婴,那么就通过元婴透析身体内部的构造。理解内部构造后,还可以让元婴指引体内真气在经脉的流向,早日修成正果。
如其名,反射是(从镜子里)照出自身。我们写代码,告诉代码怎么运行,事件发生在编译期。代码运行期间,代码如何知道自己的结构以及能力呢?反射机制相当于代码的元婴,使代码能够感知自身结构,并可(部分)改变运行行为。
与运行时类型信息(Runtime Type Informatiion, RTTI)不同,反射重点在运行时检测、感知、改变自身的结构和行为。反射是元编程(metaprogramming)的重要组成部分。
反射API
反射不是语法分析,不操作表达式、代码语句。反射获取的是代码的结构,即函数、类这些构件的结构。PHP中的反射API均以 Reflection
开头(接口 Reflector
除外),重点在函数和类两种结构。而函数可以看成类的成员函数(多一个隐式的 this
参数)或者静态成员函数( public
类型),所以了解反射API可从类信息的 ReflectionClass
开始。
ReflectionClass
提供了以下获取类基本信息的接口:
-
getProperties
:获取成员变量/属性,返回一个ReflectionProperty
数组;ReflectionProperty
类中有对属性详细说明的API:是否默认属性(isDefault
),是否私有属性(isPrivate
)等。同时ReflectionClass
还提供获取特定类别属性的API:getDefaultProperties
,getStaticProperties
; -
getConstants
:获取类中定义的常量; -
getMethods
:获取类中定义的方法,返回一个ReflectionMethod
数组;ReflectionMethod
将在下文讲解; -
getInterfaces
:获取类实现的接口; -
getParentClass
:获取父类的ReflectionClass
实例。
在反射中,类、接口、特性不分家,所以 ReflectionClass
提供类型判定API: isInterface
、 isTrait
。
除了以上基本信息, ReflectionClass
(包括 ReflectionMethod/ReflectionFunction
)还提供了一些不可思议的能力:
-
getDocComment
:获取类的文档注释信息; -
getFilename
:获取类定义的文件; -
getStartLine
: 获取类定义的起始行号; -
getEndLine
: 获取类定义的结束行号; -
getModifiers
:获取类定义的修饰符,其意义名字可通过Reflection::getModifierNames
得到,例如:abstract,final。
如果说前述的类结构信息可以通过现有的API获取( method_exits/property_exits
等),上面列出的功能基本上只能通过反射API获取( PHP文件中定义的类并且知道定义文件 ,可以利用 token_get_all
得到相同结果,但是实现非常复杂)。这些行为发生在运行期间。由此可见反射API在分析类结构信息功能上的强大。
除了 ReflectionClass
, ReflectionMethod
和 ReflectionFunction
是另外反射中另外两个重要的类。函数( function
)定义在类外部,方法( method
)定义在类内部,两者其实同源,在反射API中有共同的父类: ReflectionFunctionAbstract
。 ReflectionFunctionAbstract
有两者的大部分API,并且基本上是最重要的API。其中最值得关注的是其参数信息的API: getParameters
。其获取函数的参数信息,返回一个 ReflectionParameter
数组。结合 getParameters
和 ReflectionParameter
,函数(方法)的结构基本上就清晰了。
API操作
知道人体构造和体内真气分布,你可以引导真气到手指,练成一阳指、六脉神剑、弹指神通、九阴白骨爪等;也可以让真气汇聚,冲破任督二脉,开辟洞天;还可以逆转全身经脉,练成蛤蟆功…内省的好处可见一斑。
反射让代码感知自身结构,有什么好处呢?反射API提供了三种在运行时对代码操作的能力:
- 设置访问控制权:
setAccessible
。可获取私有的方法/属性。 注意:setAccessible
只是让方法/成员变量可以invoke/getValue/setValue
,并不代表类定义的访问存取权限改变; - 调用函数/方法:
invoke/invokeArgs
。配合获取函数参数的API,可以安全的传参和调用函数,call_user_func(_array)
的增强版; - 不依赖构造函数生成实例:
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_exits
、 call_user_func
这些函数应该可以退休。但是考虑到兼容、使用便利、运行效率等因素,许多框架仍然依赖这些API。
感谢阅读,欢迎指正!
参考
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Go语言反射之反射调用
- Go语言反射之类型反射
- Go语言反射之值反射
- 模块讲解----反射 (基于web路由的反射)
- 装饰器与元数据反射(4)元数据反射
- .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
新媒体文案创作与传播
秋叶、叶小鱼、勾俊伟 / 人民邮电出版社 / 2017-4 / 39.80元
《新媒体文案创作与传播》共分三篇。第1篇是新媒体文案基础篇,主要讲述了新媒体文案的基本概念、新媒体文案的岗位要求和职业能力素养;第二篇是新媒体文案创意实务篇,主要讲述了新媒体文案的创作思路、新媒体文案的写作技巧、爆款新媒体文案的打造、新媒体销售文案的写作、新媒体对文案传播的新要求、新媒体品-牌文案的写作,以及不同媒介的特征及发布形式;第三篇为新媒体文案相关技能补充,主要讲述的是策划能力。 《新媒体......一起来看看 《新媒体文案创作与传播》 这本书的介绍吧!