和Java一样PHP中也提供了一套完整的反射API,何为反射?以前我们是先写类,再在类中添加各种方法和属性,最后实例化一个类对象调用属性和方法。那有我们没有办法只通过这个实例对象获取到关于这个类的全部信息呢,包括有哪些方法和属性它们分别在多少行?答案就是反射。

几个常用的反射API的类

ReflectionClass

class People { protected $name = 'Foo'; protected $age = 18; public function __construct($name,$age) { $this->name = $name; $this->age = $age; } public function func1() { echo 'fun1'; } public function toString() { echo "Name: $this->name, Age: $this->age" . PHP_EOL; }}class Student extends People { public $major; public function __construct($name, $age, $major) { $this->major = $major; parent::__construct($name, $age); } public function toString() { echo "Name: $this->name, Age: $this->age, Major: $this->major" . PHP_EOL; }}$reflect = new ReflectionClass('Student');// 也可以传一个实例对象// $reflect = new ReflectionClass(new Student('Jason',21,'CS'));Reflection::export($reflect);

输出结果如下:

Class [  class Student extends People ] { @@ /…/demo1.php 18-31 - Constants [0] { } - Static properties [0] { } - Static methods [0] { } - Properties [3] { Property [  public $major ] Property [  protected $name ] Property [  protected $age ] } - Methods [2] { Method [  public method __construct ] { @@ /…/demo1.php 21 - 25 - Parameters [3] { Parameter #0 [  $name ] Parameter #1 [  $age ] Parameter #2 [  $major ] } } Method [  public method toString ] { @@ /…/demo1.php 27 - 30 } }}

可以看到,Reflection::export几乎提供了Student类的所有信息,包括属性和方法的访问控制状态,每个方法需要的参数以及每个方法在脚本中的位置,和var_dump相比,在使用var_dump前总是需要实例化一个对象,而且无法提供那么多详细信息。var_dump输出如下:

object(Student)#1 (3) { ["major"]=> string(2) "CS" ["name":protected]=> string(5) "Jason" ["age":protected]=> int(21)}

虽然var_dump和print_r是打印数据的利器,但对于类和函数,反射API提供了更高层次的功能。同时,ReflectionClass还提供其他有用的方法:

// 获取构造函数$reflect->getConstructor();object(ReflectionMethod)#2 (2) { ["name"]=> string(11) "__construct" ["class"]=> string(7) "Student"}// 获取类名$reflect->getName();string(7) "Student”// 判断接口类$reflect->isInterface(); // false// 判断抽象类$reflect->isAbstract(); // false// 类开始行$reflect->getStartLine();// 类结束行$reflect->getEndLine();

详细的可以看官方文档: https://www.php.net/manual/en/class.reflectionclass.php

ReflectionMethod

ReflectionMethod用于检查类中的方法,获得ReflectionMethod对象的方式有几种,

  1. 可以从ReflectionClass::getMethods()获得ReflectionMethod对象的数组
  2. 要得到特定的类方法,可以使用ReflectionClass::getMethod(),把函数名作为参数传过去
  3. 直接使用ReflectionMethod创建,new ReflectionMethod('Student','toString'),第一个参数是类名或类实例,第二个是要获取的函数名

同样,ReflectionMethod也提供了很多方法来检查类方法:

// 获取类名$reflect_method->getName()// 是不是公共的$reflect_method->isPublic()// 是不是私有的$reflect_method->isPrivate()// 类的开始行$reflect_method->getStartLine()// 类的结束行$reflect_method->getEndLine()

与get_class_methods()方法对比:

// ReflectionMethod::getParameters()array(3) { [0]=> object(ReflectionMethod)#2 (2) { ["name"]=> string(11) "__construct" ["class"]=> string(7) "Student" } [1]=> object(ReflectionMethod)#3 (2) { ["name"]=> string(8) "toString" ["class"]=> string(7) "Student" } [2]=> object(ReflectionMethod)#4 (2) { ["name"]=> string(5) "func1" ["class"]=> string(6) "People" }}// get_class_methods('Student')array(3) { [0]=> string(11) "__construct" [1]=> string(8) "toString" [2]=> string(5) "func1"}

从输出结果可以很明显看出,使用反射的方法输出的结果更详细,可以看到每个方法具体属于哪个类,而get_class_methods是能打印出方法名。

详细的可以看官方文档: https://www.php.net/manual/en/class.reflectionmethod.php

ReflectionParameter

PHP中,在声明类方法时我们可以限制参数中对象的类型,因此检查方法的参数变得很有必要,反射API提供了ReflectionParameter类来使用。和获得ReflectionMethod对象数组的方式一样,我们可以使用ReflectionMethod::getParameters()方法返回ReflectionParameter对象数组。

$method = $reflect->getMethod('__construct');array(3) { [0]=> object(ReflectionParameter)#3 (1) { ["name"]=> string(4) "name" } [1]=> object(ReflectionParameter)#4 (1) { ["name"]=> string(3) "age" } [2]=> object(ReflectionParameter)#5 (1) { ["name"]=> string(5) "major" }}

Student构造函数接受三个参数,我们可以知道具体的参数名,通过反射,我们还可以知道参数是否可以按引用传递,参数类型和是否接收空值作为参数等。

上面是我最近对PHP的反射api的简单学习。

发现一个有趣的package

Github上有个项目叫Roave/BetterReflection,是这个包提供的方法,我们可以更方便的调用PHP的反射,现在已经有600+star,地址如下:https://github.com/Roave/BetterReflection

BetterReflection

现在我们来简单学习下,这个包如何使用。

1. 安装

$ composer require roave/better-reflection

2. 使用

创建ReflectionClass

use RoaveBetterReflectionReflectionReflectionClass;$classInfo = ReflectionClass::createFromName(Student::class); // 通过类名创建// 或者$classInfo = ReflectionClass::createFromInstance(new Student('Daniel',18,'Math')); // 通过实例创建

上面的静态初始化方式就等于下面:

use RoaveBetterReflectionBetterReflection;$classInfo = (new BetterReflection)->classReflector()->reflect(Student::class);

我们查看静态构造器createFromName的实现,会发现也是先实例化一个BetterReflection再进行反射的:

public static function createFromName(string $className) : self{ return (new BetterReflection())->classReflector()->reflect($className);}

获取ReflectionMethod、ReflectionParameter和ReflectionProperty也是一样一样的:

use RoaveBetterReflectionReflectionReflectionMethod;use RoaveBetterReflectionReflectionReflectionParameter;use RoaveBetterReflectionReflectionReflectionProperty;$methodInfo = ReflectionMethod::createFromName(Student::class,'toString');$methodInfo = ReflectionMethod::createFromInstance($student,'toString');…

3. BetterReflection中的SourceLocator

SourceLocator的作用是根据不同的源码类型(有通过字符串传来的类、composer自动加载的类、一个指定文件名的类等), 得到LocatedSource对象的(用于AST),这个对象指向要反射的类,主要就以下几个方法:

class LocatedSource{ /** @var string */ private $source; /** @var string|null */ private $filename; /** * @throws InvalidArgumentException * @throws InvalidFileLocation */ public function __construct(string $source, ?string $filename) {…} public function getSource() : string {…} public function getFileName() : ?string {…} /** * Is the located source in PHP internals? */ public function isInternal() : bool {…} public function getExtensionName() : ?string {…} /** * Is the located source produced by eval() or function_create()? */ public function isEvaled() : bool {…}}

针对不同的情况,比如单个文件、源码字符串、闭包等,又衍生出了多种SourceLocator:

多种SourceLocator

  • ComposerSourceLocator: 这应该是最常用的了,它使用composer自带的autoload来定位
  • SingleFileSourceLocator: 通过指定文件名来定位
  • StringSourceLocator: 直接使用定义类的字符串
  • AutoloadSourceLocator: 使用PHP内置的autoloader来定位,默认会使用这个定位器
  • EvaledCodeSourceLocator: 用于eval()创建的代码
  • PhpInternalSourceLocator: 用于PHP原生代码
  • AnonymousClassObjectSourceLocator: 用于PHP中的匿名类
  • ClosureSourceLocator: 用于闭包
  • AggregateSourceLocator: 合并多个SourceLocator
  • FileIteratorSourceLocator: 用于可迭代的SplFileInfo实例数组
  • DirectoriesSourceLocator: 用于文件夹的情况,包括子文件夹

如何使用这这些Locator?

1. 使用composer的autoload加载

use RoaveBetterReflectionBetterReflection;use RoaveBetterReflectionReflectorClassReflector;use RoaveBetterReflectionSourceLocatorTypeComposerSourceLocator;$classLoader = require 'vendor/autoload.php';$astLocator = (new BetterReflection())->astLocator();$reflector = new ClassReflector(new ComposerSourceLocator($classLoader, $astLocator));$reflectionClass = $reflector->reflect('FooBarMyClass');echo $reflectionClass->getShortName(); // MyClassecho $reflectionClass->getName(); // FooBarMyClassecho $reflectionClass->getNamespaceName(); // FooBar

2. 使用定义类的字符串加载

use RoaveBetterReflectionBetterReflection;use RoaveBetterReflectionReflectorClassReflector;use RoaveBetterReflectionSourceLocatorTypeStringSourceLocator;$code = '<?php class Foo {};';$astLocator = (new BetterReflection())->astLocator();$reflector = new ClassReflector(new StringSourceLocator($code, $astLocator));$reflectionClass = $reflector->reflect('Foo');echo $reflectionClass->getShortName(); // Foo

3. 通过指定PHP文件名来加载

use RoaveBetterReflectionBetterReflection;use RoaveBetterReflectionReflectorClassReflector;use RoaveBetterReflectionSourceLocatorTypeSingleFileSourceLocator;$astLocator = (new BetterReflection())->astLocator();$reflector = new ClassReflector(new SingleFileSourceLocator('path/to/MyApp/MyClass.php', $astLocator));$reflectionClass = $reflector->reflect('MyAppMyClass');echo $reflectionClass->getShortName(); // MyClassecho $reflectionClass->getName(); // MyAppMyClassecho $reflectionClass->getNamespaceName(); // MyApp

这种方式虽然传的是一个文件,但是在SingleFileSourceLocator::createLocatedSource()方法中会使用file_get_contents转换成字符串。

protected function createLocatedSource(Identifier $identifier) : ?LocatedSource{ return new LocatedSource( file_get_contents($this->fileName), $this->fileName );}

其他的几个locator就不多介绍了。最后说一下如何反射一个闭包:

$myClosure = function () { echo "Hello world!";};$functionInfo = ReflectionFunction::createFromClosure($myClosure);$functionInfo->isClosure(); // true

php get 传循环出来的参数_简单学习PHP中的反射相关推荐

  1. python中函数的可变参数_简单谈谈Python中函数的可变参数

    前言 在Python中定义函数,可以用必选参数.默认参数.可变参数和关键字参数,这4种参数都可以一起使用,或者只用其中某些,但是请注意,参数定义的顺序必须是:必选参数.默认参数.可变参数和关键字参数. ...

  2. 循环神经网络 递归神经网络_了解递归神经网络中的注意力

    循环神经网络 递归神经网络 I recently started a new newsletter focus on AI education. TheSequence is a no-BS( mea ...

  3. 存储http请求返回参数_前端学习需要知道的 HTTP 知识(1/7)

    基础知识 场景 我们打开浏览器,输入网址(比如 https://www.bilibili.com/),然后我们就可以看到 b 站的 Web 页面,Web 页面当然不能凭空显示出来.根据 Web 浏览器 ...

  4. 迁移学习 迁移参数_迁移学习简介

    迁移学习 迁移参数 介绍 (Introduction) We as humans have the ability to transfer the knowledge gained in one ta ...

  5. c语言全局变量作为参数_在C / C ++中使用变量参数列表

    c语言全局变量作为参数 C/C++ provides a means to pass a variable number of arguments to a function.  This artic ...

  6. java获取web.xml 参数_解析web.xml中在Servlet中获取context-param和init-param内的参数

    web.xml里面可以定义两种参数:1.application范围内的参数,存放在servletcontext中,在web.xml中配置如下: context/param avalible durin ...

  7. python函数分几种_简单了解Python中的几种函数

    python是支持多种范型的语言,可以进行所谓函数式编程,其突出体现在有这么几个函数: filter.map.reduce.lambda.yield lambda lambda函数的使用方法:在lam ...

  8. python中len用法_简单介绍Python中的len()函数的使用

    简单介绍Python中的len()函数的使用 函数:len() 1:作用:返回字符串.列表.字典.元组等长度 2:语法:len(str) 3:参数:str:要计算的字符串.列表.字典.元组等 4:返回 ...

  9. php get 传循环出来的参数_PHP性能优化小技巧

    PHP性能优化小技巧: 1. foreach效率更高,尽量用foreach代替while和for循环. 2. 循环内部不要声明变量,尤其是对象这样的变量. 3. 在多重嵌套循环中,如有可能,应当将最长 ...

最新文章

  1. 【深入浅出MyBatis系列八】SQL自动生成插件
  2. LeetCode MySQL 1853. 转换日期格式(日期格式化)
  3. redis获取byte数组_《Redis深度历险》读书笔记
  4. 【最快人脸检测模型开源】libfacedetection开源
  5. HashMap 和 Hashtable 的同和不同
  6. 网易财报暗藏玄机,不经意间已编织出电商大网
  7. 微信网页开发 thinkphp5.0的try-catch和重定向
  8. 如果Mac无法连接到其他电脑共享怎么办?
  9. 利用SVD(Singular Value Decomposition)简化数据
  10. 我去,还在这样读写 excel 这也太低效了吧!
  11. 物联网MT2625芯片平台技术参考资料
  12. 湿淀粉 - 搜搜百科
  13. modelica语言学习心得
  14. PostgreSQL模糊查询
  15. 个人自媒体技术分享博客网站模板
  16. bwa mem 报错处理:[mem_sam_pe] paired reads have different names
  17. Illegal unquoted character ((CTRL-CHAR, code 9)): has to be escaped using backslash to be included i
  18. 使用python快速查看hdf5文件
  19. 学习剪辑的几个要考虑的地方
  20. spoolsv.exe占用cpu 100%的解决方法

热门文章

  1. UVA12541 LA6148 Birthdates【最值】
  2. Bailian3179 最长单词【字符串】
  3. CCF201403-1 相反数(解法二)(100分)(废除!!!)
  4. scala 偏函数与 map/collect
  5. 【证明】【一题多解】布尔不等式(union bound)的证明
  6. 辨异 —— 概率与统计
  7. Git 与 Github 基础(二)—— Git for Windows
  8. utilities(C++)——错误提示
  9. 计算机信息技术基础知识教案,计算机的基础知识
  10. python是干嘛的-python是什么?python可以用来干什么?