PHP 反射
阅读目录
参考文档
官网:
https://www.php.net/manual/zh/book.reflection.php
https://www.php.net/manual/zh/class.reflection.php
https://www.qycn.com/xzx/article/405.html
Laravel 通过类的反射, 对类的私有属性赋值
TestController 类
namespace App\Http\Controllers\Services;use App\CommentModel;use App\Http\Controllers\Controller;class TestController extends Controller{ public function getList() { $data = ["good_id"=>1,"name"=>"商品名"]; $good = $this->ins(TwoTestController::class ,$data); dd($good); } public function ins($class , $data) { $classObj = new \ReflectionClass($class);// 反射 TwoTestController 类 $classMethods = $classObj->getMethods(\ReflectionMethod::IS_PUBLIC); // 获取公共方法 $class_instantce = $classObj->newInstance(); // 通过反射对象创建类的实例 foreach ($classMethods as $method){// echo $method->getName().PHP_EOL; // 获取公共方法的方法名 # 过滤出set开头的公共方法(我这里只有一个setGoodId方法) if (preg_match("/^set(\w+)/" ,$method->getName() ,$matches)) {// echo $matches[1].PHP_EOL; // 输出的是GoodId// echo $this->filterMethod($matches[1]).PHP_EOL; // 输出的是good_id $this->filterMethod($matches[1] ,$classObj ,$data, $class_instantce); } } return $class_instantce; } function filterMethod($name ,\ReflectionClass $classObj ,$data , &$class_instance){ // GoodId 转换成 Good_Id , 左边是小写才添加_ , aGoodId 转换成a_Good_Id $filter_name = strtolower(preg_replace("/(?<=[a-z])([A-Z])/","_$1" ,$name)); $props = $classObj->getProperties(\ReflectionProperty::IS_PRIVATE); //获取私有属性 foreach ($props as $prop){ if ($prop->getName() == $filter_name){ $method = $classObj->getMethod("set".$name); $args = $method->getParameters(); // 获取方法的参数 // 参数数量为1 , 并且数据中存在对应的值 if (count($args)==1 && isset($data[$filter_name])){ // 反射执行方法 $method->invoke($class_instance, $data[$filter_name]); } } } }}
TwoTestController 类
namespace App\Http\Controllers\Services;use Illuminate\Http\Request;use App\Http\Controllers\Controller;class TwoTestController extends Controller{ private $good_id; private $name; public function setGoodId($good_id) { $this->good_id = $good_id; } public function getGoodId() { return $this->good_id; } public function setName($name) { $this->name = $name; } public function getName() { return $this->name; }}
预览
PHP中的反射理解
从一个简单的例子理解反射:
人有五官四肢,但鲜有人清楚人体内部的经脉走向、骨骼构造。
如果你修仙顺利,在丹田深处练出元婴,那么就通过元婴透析身体内部的构造。
理解内部构造后,还可以让元婴指引体内真气在经脉的流向,早日修成正果。
如其名,反射是(从镜子里)照出自身。
我们写代码,告诉代码怎么运行,事件发生在编译期。
代码运行期间,代码如何知道自己的结构以及能力呢?
反射机制相当于代码的元婴,使代码能够感知自身结构,并可(部分)改变运行行为。
与运行时类型信息(Runtime Type Informatiion, RTTI)不同,反射重点在运行时检测、感知、改变自身的结构和行为。
反射是元编程(metaprogramming)的重要组成部分。
PHP反射API均以 Reflection 开头
反射不是语法分析,不操作表达式、代码语句。
反射获取的是代码的结构,即函数、类这些构件的结构。
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<?phpclass 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);PS E:\PDF\test> php .\foo.phpconstruct, instance id: 1object(Foo)#1 (0) {}PS E:\PDF\test>
# ReflectionClass 类报告了一个类的有关信息,建立Foo这个类的反射类$class = new ReflectionClass("Foo");# 获取类的构造函数$constructor = $class->getConstructor();// var_dump(ReflectionProperty::IS_PUBLIC);exit;int(1)// var_dump($constructor->getModifiers());// PS E:\PDF\test> php .\foo.php// int(4)// construct, instance id: 1// object(Foo)#3 (0) {// }// var_dump(ReflectionProperty::IS_PUBLIC & $constructor->getModifiers());exit;//0# ReflectionProperty::IS_PUBLIC,检查属性是否为公共# Gets the property modifiers,获取属性修饰符if ((ReflectionProperty::IS_PUBLIC & $constructor->getModifiers()) === 0) { # 设置访问控制权:setAccessible 可获取私有的方法/属性。 # 注意:setAccessible 只是让方法/成员变量可以 invoke/getValue/setValue,并不代表类定义的访问存取权限改变; $constructor->setAccessible(true);}# 创建一个新的类的实例而不调用它的构造函数。$instance2 = $class->newInstanceWithoutConstructor();# ReflectionMethod::invoke()函数是PHP中的内置函数,用于调用指定的反射方法并返回方法的结果。$constructor->invoke($instance2);var_dump($instance2);
# 脚本执行结果construct, instance id: 1object(Foo)#1 (0) {}construct, instance id: 2object(Foo)#4 (0) {}
我们成功的生成了两个实例,并调用构造函数完成对象初始化。
如果没有反射API,这几乎是不可能完成的工作。
反射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在功能上的差异
功能 | 函数式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 反射类 ReflectionClass
class fuc{ //定义一个类 public static function ec() { echo'我是一个类'; }}$class = new ReflectionClass('fuc'); //建立 fuc这个类的反射类echo $class; //输出这反射类//相当于实例化 fuc 类$fuc=$class->newInstance(); //执行 fuc 里的方法ec$fuc->ec();
PS E:\PDF\test> php .\foo.phpClass [ <user> class fuc ] { @@ E:\PDF\test\foo.php 3-11 - Constants [0] { } - Static properties [0] { } - Static methods [1] { Method [ <user> static public method ec ] { @@ E:\PDF\test\foo.php 6 - 9 } } - Properties [0] { } - Methods [0] { }}我是一个类PS E:\PDF\test>
示例1
class Person { public $datang = '123'; public $datang1 = '1234'; private function getName(){ echo $this->datang; } public function getName1(){ echo $this->datang1; } } //建立 Person这个类的反射类$class = new ReflectionClass('Person'); //打印所有属性名,包含private,protected,public$properties = $class->getProperties();foreach ($properties as $key => $value) { var_dump($value->getName());}//打印所有属性的注释并正则,包含private,protected,publicforeach($properties as $property) { $docblock = $property->getDocComment(); //只能打印多行注释 var_dump($docblock); preg_match('/ type\=([a-z_]*) /', $docblock, $matches); var_dump($matches);} //打印所有方法名foreach ($class->getMethods() as $key => $value) { var_dump($value->getName());} //相当于实例化Person 类$instance = $class->newInstanceArgs();//执行getName1方法,方法必须是public属性的,否则会报fatal error$instance->getName1(); //获取Person getName1方法$ec=$class->getmethod('getName1');//执行getName1方法,方法必须是public属性的,否则会报fatal error$ec->invoke($instance);
简单的学习反射
class Reflectionstudy{ private $name; public static function ref1() { $ref = new ReflectionClass('Redis'); //获得Redis的一个反射实例 $const = $ref->getConstants(); //返回所有常量名和值 echo "----------consts---" . PHP_EOL; array_walk($const, function ($val, $key) { echo $key, ':', $val, PHP_EOL; }); $props = $ref->getDefaultProperties(); //返回类中所有属性 echo "----------props---" . PHP_EOL; array_walk($props, function ($val, $key) { echo $key, ':', $val, PHP_EOL; }); $methods = $ref->getMethods(); //返回类中所有方法 echo "----------methods---" . PHP_EOL; array_walk($methods, function ($val) { echo $val, PHP_EOL; }); //当想获取具体的方案有哪些参数时,我们需要进一步反射 echo "----------params---" . PHP_EOL; echo $ref->getMethod('bitpos'); //传入方法名,会调用$reflectMetho d->__tostring() 返回可打印的数组 } public function getName() { return self::$name; } public function setName($v) { self::$name = $v; }}
1、用户获取对象相关信息 ReflectionObject
$ro = new ReflectionObject(new Reflectionstudy);print_r($ro->getMethods());
PS E:\PDF\test> php .\foo.phpArray( [0] => ReflectionMethod Object ( [name] => ref1 [class] => Reflectionstudy ) [1] => ReflectionMethod Object ( [name] => getName [class] => Reflectionstudy ) [2] => ReflectionMethod Object ( [name] => setName [class] => Reflectionstudy ))PS E:\PDF\test>
2、反射某个方法以及注释信息
$rm = new ReflectionMethod('Reflectionstudy','getName');echo $rm->isPublic(),PHP_EOL;//确认方法是否公有,返回BOOL值echo $rm->getDocComment(),PHP_EOL;//返回文档注释
PS E:\PDF\test> php .\foo.php1PS E:\PDF\test>
3、ReflectionParameter获取函数或方法参数的相关信息
$p = new ReflectionParameter(['Reflectionstudy','setName'],0);echo $p->getPosition(),PHP_EOL;//0,从左向右获得参数,以0开始echo $p->getName(),PHP_EOL;//v
PS E:\PDF\test> php .\foo.php0vPS E:\PDF\test>
4、获取类内属性注释
$p = new ReflectionProperty('Reflectionstudy','name');echo $p->getDocComment(),PHP_EOL;//获取类内属性注释
PS E:\PDF\test> php .\foo.phpPS E:\PDF\test>
5、反射类大致内容
//反射类大致内容Reflectionstudy::ref1();
反射的机制直接将 private 方法设置成外部可访问
class Foo { private function myPrivateMethod() { return 7; }}$method = new ReflectionMethod('Foo', 'myPrivateMethod');//该反射功能直接将原本是private权限的方法设置成可访问$method->setAccessible(true);echo $method->invoke(new Foo);// echos "7"
来源地址:https://blog.csdn.net/weiguang102/article/details/127535021
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341