PHP反射API (转)
http://www.cnblogs.com/zyf-zhaoyafei/p/4922893.html
近期忙着写项目,没有学习什么特别新的东西,所以好长时间没有更新博客。我们的项目用的是lumen,是基于laravel的一个轻量级框架,我看到里面用到了一些反射API机制来帮助动态加载需要的类、判断方法等,所以本篇文章就把在PHP中经常用到的反射API给大家分享一下吧,想学习反射API的同学可以看看。
说起反射ApI,自我感觉PHP中的反射ApI和java中的java.lang.reflect包差不多,都是由可以打印和分析类成员属性、方法的一组内置类组成的。可能你已经学习过对象函数比如:get_class_vars()但是使用反射API会更加的灵活、输出信息会更加详细。
首先我们需要知道,反射API不仅仅是用来检查类的,它本身包括一组类,用来完成各种功能:常用的类如下:
Reflection类 | 可以打印类的基本信息,(通过提供的静态export()函数) |
ReflectionMethod类 | 见名知意,打印类方法、得到方法的具体信息等 |
ReflectionClass类 | 用于得到类信息,比如得到类包含的方法,类本的属性,是不是抽象类等 |
ReflectionParameter类 | 显示参数的信息,可以动态得到已知方法的传参情况 |
ReflectionException类 | 用于显示错误信息 |
ReflectionExtension类 | 得到PHP的扩展信息,可以判断扩展是否存在等 |
传统的打印类信息与反射APi的区别
下面是一段我自己写的参数程序,用于演示反射的使用:
1 <?php
2
3 class Person
4 {
5 //成员属性
6 public $name;
7 public $age;
8
9 //构造方法
10 public function __construct($name, $age)
11 {
12 $this->name = $name;
13 $this->age = $age;
14 }
15
16 //成员方法
17 public function set_name($name)
18 {
19 $this->$name = $name;
20 }
21
22 public function get_name()
23 {
24 return $this->$name;
25 }
26
27 public function get_age()
28 {
29 return $this->$age;
30 }
31
32 public function get_user_info()
33 {
34 $info = '姓名:' . $this->name;
35 $info .= ' 年龄:' . $this->age;
36 return $info;
37 }
38 }
39
40 class Teacher extends Person
41 {
42 private $salary = 0;
43
44 public function __construct($name, $age, $salary)
45 {
46 parent::__construct($name, $age);
47 $this->salary = $salary;
48 }
49
50 public function get_salary()
51 {
52 return $this->$salary;
53 }
54
55 public function get_user_info()
56 {
57 $info = parent::get_user_info();
58 $info .= " 工资:" . $this->salary;
59 return $info;
60 }
61 }
62
63 class Student extends Person
64 {
65 private $score = 0;
66
67 public function __construct($name, $age, $score)
68 {
69 parent::__construct($name, $age);
70 $this->score = $score;
71 }
72
73 public function get_score()
74 {
75 return $this->score;
76 }
77
78 public function get_user_info()
79 {
80 $info = parent::get_user_info();
81 $info .= " 成绩:" . $this->score;
82 return $info;
83 }
84 }
85
86 header("Content-type:text/html;charset=utf8;");
87 $te_obj = new Teacher('李老师', '36', '2000');
88 $te_info = $te_obj->get_user_info();
89
90 $st_obj = new Student('小明', '13', '80');
91 $st_info = $st_obj->get_user_info();
我们先用var_dump();打印类的信息,如下所示,可以看出只是打印出类的简单信息,甚至连方法也没有,所以从这样的信息中看不出其他游泳的信息。
var_dump($te_obj);
1 object(Teacher)#1 (3) {
2 ["salary":"Teacher":private]=>
3 string(4) "2000"
4 ["name"]=>
5 string(9) "李老师"
6 ["age"]=>
7 string(2) "36"
8 }
Reflection::export($obj);
我们利用Reflection提供的内置方法export来打印信息,如下所示:
打印出的信息比较完整,包括成员属性,成员方法,类的基本信息,文件路径,方法信息,方法属性,传参情况,所在文件的行数等等。比较全面的展示了类的信息。可以看出var_dump()或者print_r只能显示类的简要信息,好多信息根本显示不出来,所以他们只能做简单的调试之用,反射Api则提供的类更多的信息,可以很好地帮助我们知道调用类的情况,这对写接口,特别是调用别人的接口提供了极大的便利。如果出了问题,也可以帮助调试。
1 object(Teacher)#1 (3) {
2 ["salary":"Teacher":private]=>
3 string(4) "2000"
4 ["name"]=>
5 string(9) "李老师"
6 ["age"]=>
7 string(2) "36"
8 }
9 Class [ class Person ] {
10 @@ /usr/local/www/phptest/oop/reflaction.php 3-38
11 - Constants [0] {
12 }
13 - Static properties [0] {
14 }
15 - Static methods [0] {
16 }
17 - Properties [2] {
18 Property [ public $name ]
19 Property [ public $age ]
20 }
21
22 - Methods [5] {
23 Method [ public method __construct ] {
24 @@ /usr/local/www/phptest/oop/reflaction.php 10 - 14
25
26 - Parameters [2] {
27 Parameter #0 [ $name ]
.....
反射API的具体使用:
看过框架源码的同学都知道框架都可以加载第三方的插件、类库等等。下面这个例子咱们借助反射APi简单实现这个功能,该例子原型是我从书上学习的,我理解后按照自己的思路写了一套:要实现的功能:用一个类去动态的遍历调用Property类对象,类可以自由的加载其他的类的方法,而不用吧类嵌入到已有的代码,也不用手动去调用类库的代码。
约定:每一个类要包含work方法,可以抽象出一个接口。可以把每个类的信息放在文件中,相当于各个类库信息,通过类保存的Property类库的对应对象,然后调用每个类库的work方法。
下面是基础代码:
1 /*属性接口*/
2 interface Property
3 {
4 function work();
5 }
6
7 class Person
8 {
9 public $name;
10 public function __construct($name)
11 {
12 $this->name = $name;
13 }
14 }
15
16 class StudentController implements Property
17 {
18 //set方法,但需要Person对象参数
19 public function setPerson(Person $obj_person)
20 {
21 echo 'Student ' . $obj_person->name;
22 }
23
24 //work方法简单实现
25 public function work()
26 {
27 echo 'student working!';
28 }
29 }
30
31 class EngineController implements Property
32 {
33 //set方法
34 public function setWeight($weight)
35 {
36 echo 'this is engine -> set weight';
37 }
38
39 public function setPrice($price)
40 {
41 echo "this is engine -> set price";
42 }
43
44 //work方法简单实现
45 public function work()
46 {
47 echo 'engine working!';
48 }
49 }
这里定义了两个相似类实现Property接口,同时都简单实现work()方法 StudentController类稍微不同,参数需要Person对象,同时我们可以使用文件来保存各个类的信息,我们也可以用成员属性代替。
1 class Run
2 {
3 public static $mod_arr = [];
4 public static $config = [
5 'StudentController' => [
6 'person' => 'xiao ming'
7 ],
8 'EngineController' => [
9 'weight' => '500kg',
10 'price' => '4000'
11 ]
12 ];
13
14 //加载初始化
15 public function __construct()
16 {
17 $config = self::$config;
18 //用于检查是不是实现类
19 $property = new ReflectionClass('Property');
20 foreach ($config as $class_name => $params) {
21 $class_reflect = new ReflectionClass($class_name);
22 if(!$class_reflect->isSubclassOf($property)) {//用isSubclassOf方法检查是否是这个对象
23 echo 'this is error';
24 continue;
25 }
26
27 //得到类的信息
28 $class_obj = $class_reflect->newInstance();
29 $class_method = $class_reflect->getMethods();
30
31 foreach ($class_method as $method_name) {
32 $this->handle_method($class_obj, $method_name, $params);
33 }
34 array_push(self::$mod_arr, $class_obj);
35 }
36 }
37
38 //处理方法调用
39 public function handle_method(Property $class_obj, ReflectionMethod $method_name, $params)
40 {
41 $m_name = $method_name->getName();
42 $args = $method_name->getParameters();
43
44 if(count($args) != 1 || substr($m_name, 0, 3) != 'set') {
45 return false;
46 }
47 //大小写转换,做容错处理
48 $property = strtolower(substr($m_name, 3));
49
50 if(!isset($params[$property])) {
51 return false;
52 }
53
54 $args_class = $args[0]->getClass();
55 echo '<pre>';
56 if(empty($args_class)) {
57 $method_name->invoke($class_obj, $params[$property]); //如果得到的类为空证明需要传递基础类型参数
58 } else {
59 $method_name->invoke($class_obj, $args_class->newInstance($params[$property])); //如果不为空说明需要传递真实对象
60 }
61 }
62 }
63
64 //程序开始
65 new Run();
到此程序结束,Run启动会自动调用构造方法,初始化要加载类库的其他成员属性,包括初始化和执行相应方法操作,这里只是完成了对应的set方法。其中$mod_arr属性保存了所有调用类的对象,每个对象包含数据,可以遍历包含的对象来以此调用work()方法。
程序只做辅助理解反射PAI用,各个功能没有完善,里面用到了好多反射API的类,方法,下面会有各个方法的总结。
反射API提供的常用类和函数:
下面提供的函数是常用的函数,不是全部,有的函数根本用不到,所以我们有往撒谎那个写,想看全部的可以到网上搜一下,比较多。提供的这组方法没有必要背下来,用到的时候可以查看。
1 1:Reflection
2 public static export(Reflector r [,bool return])//打印类或方法的详细信息
3 public static getModifierNames(int modifiers) //取得修饰符的名字
4
5 2:ReflectionMethod:
6 public static string export() //打印该方法的信息
7 public mixed invoke(stdclass object, mixed* args) //调用对应的方法
8 public mixed invokeArgs(stdclass object, array args)//调用对应的方法,传多参数
9 public bool isFinal() //方法是否为final
10 public bool isAbstract() //方法是否为abstract
11 public bool isPublic() //方法是否为public
12 public bool isPrivate() //方法是否为private
13 public bool isProtected() //方法是否为protected
14 public bool isStatic() //方法是否为static
15 public bool isConstructor() //方法是否为构造函数
17
18 3:ReflectionClass:
19 public static string export() //打印类的详细信息
20 public string getName() //取得类名或接口名
21 public bool isInternal() //类是否为系统内部类
22 public bool isUserDefined() //类是否为用户自定义类
23 public bool isInstantiable() //类是否被实例化过
24 public bool hasMethod(string name) //类是否有特定的方法
25 public bool hasProperty(string name)//类是否有特定的属性
26 public string getFileName() //获取定义该类的文件名,包括路径名
27 public int getStartLine() //获取定义该类的开始行
28 public int getEndLine() //获取定义该类的结束行
29 public string getDocComment() //获取该类的注释
30 public ReflectionMethod getConstructor() //取得该类的构造函数信息
31 public ReflectionMethod getMethod(string name) //取得该类的某个特定的方法信息
32 public ReflectionMethod[] getMethods() //取得该类的所有的方法信息
33 public ReflectionProperty getProperty(string name) //取得某个特定的属性信息
34 public ReflectionProperty[] getProperties() //取得该类的所有属性信息
35 public array getConstants() //取得该类所有常量信息
36 public mixed getConstant(string name) //取得该类特定常量信息
37 public ReflectionClass[] getInterfaces() //取得接口类信息
38 public bool isInterface() //测试该类是否为接口
39 public bool isAbstract() //测试该类是否为抽象类
40
41 4:ReflectionParameter:
42 public static string export() //导出该参数的详细信息
43 public string getName() //取得参数名
44 public bool isPassedByReference() //测试该参数是否通过引用传递参数
45 public ReflectionClass getClass() //若该参数为对象,返回该对象的类名
46 public bool isArray() //测试该参数是否为数组类型
47 public bool allowsNull() //测试该参数是否允许为空
48 public bool isOptional() //测试该参数是否为可选的,当有默认参数时可选
49 public bool isDefaultValueAvailable() //测试该参数是否为默认参数
50 public mixed getDefaultValue() //取得该参数的默认值
51
52 5:ReflectionExtension类
54 public static export() //导出该扩展的所有信息
55 public string getName() //取得该扩展的名字
56 public string getVersion() //取得该扩展的版本
57 public ReflectionFunction[] getFunctions() //取得该扩展的所有函数
58 public array getConstants() //取得该扩展的所有常量
59 public array getINIEntries() //取得与该扩展相关的,在php.ini中的指令信息
60 }
写的比较急,难免会有错误,还请大神们多多指正。
PHP反射API (转)的更多相关文章
- PHP 高级编程(2/5) - 反射API
PHP 5 具有完整的反射 API,添加了对类.接口.函数.方法和扩展进行反向工程的能力. 此外,反射 API 提供了方法来取出函数.类和方法中的文档注释.通过使用反射API可以分析其他的类.接口.方 ...
- 了解一下OOP的反射API
PHP5的类和对象函数并没有告诉我们类内部的所有一切,而只是报告了它们的公共成员.要充分了解一个类,需要知道其私有成员和保护成员,还要知道其方法所期望的参数 .对此,使用反射API. 1 查看自定义类 ...
- PHP反射API
近期忙着写项目,没有学习什么特别新的东西,所以好长时间没有更新博客.我们的项目用的是lumen,是基于laravel的一个轻量级框架,我看到里面用到了一些反射API机制来帮助动态加载需要的类.判断方法 ...
- 反射——反射API,使用反射创建数组
反射API Java.lang.Reflect库 ① Class类与Java.lang.Reflect类库一起对反射的概念进行支持. ② java.lang包下: a) Cla ...
- Java学习笔记--反射API
反射API 1.反射API的介绍 通过反射API可以获取Java程序在运行时刻的内部结构.比如Java类中包含的构造方法.域和方法等元素,并可以与这些元素进行交换. 按照 一般地面向对象的设计 ...
- 详解PHP反射API
PHP中的反射API就像Java中的java.lang.reflect包一样.它由一系列可以分析属性.方法和类的内置类组成.它在某些方面和对象函数相似,比如get_class_vars(),但是更加灵 ...
- Java反射API研究(1)——注解Annotation
注解在表面上的意思,只是标记一下这一部分,最好的注解就是代码自身.而在java上,由于注解的特殊性,可以通过反射API获取,这种特性使得注解被广泛应用于各大框架,用于配置内容,代替xml文件配置. 要 ...
- JDK1.7新特性(4):java语言动态性之反射API
直接通过一个代码示例来熟悉java中通过反射来对构造函数/域以及方法处理的相关API: package com.rampage.jdk7.chapter2; import java.lang.refl ...
- PHP面向对象深入研究之【了解类】与【反射API】
了解类 class_exists验证类是否存在 <?php // TaskRunner.php $classname = "Task"; $path = "task ...
- Atitit.跨语言反射api 兼容性提升与增强 java c#。Net php js
Atitit.跨语言反射api 兼容性提升与增强 java c#.Net php js 1. 什么是反射1 1.1. 反射提供的主要功能:1 1.2. 实现反射的过程:1 ...
随机推荐
- AFL Fuzz安装及完成一次简单的模糊测试
关于AFL fuzz AFL fuzz是一个模糊测试工具,它封装了一个GCC/CLang编译器,用于对被测代码重新编译的过程中进行插桩.插桩完毕后,AFL fuzz就可以给其编译过的代码输入不同的参数 ...
- sql server 能按照自己规定的字段顺序展示
工作中遇到,需要把sql 查询的按照指定的顺序显示 select plantname,cc_type,all_qty from VIEW_TEMP_DAY_CVT_CAP a where a.docd ...
- graphql-hooks hooks first 的graphql 客户端
graphql-hooks 是一个hooks first 的graphql 客户端,支持一一些特性 首类hooks api 比较小(5.3Kb) gzip 1.8 kb 完整支持ssr (通过grap ...
- 干货 | 10分钟带你全面掌握branch and bound(分支定界)算法-概念篇
00 前言 之前一直做启发式算法,最近突然对精确算法感兴趣了.但是这玩意儿说实话是真的难,刚好boss又叫我学学column generation求解VRP相关的内容.一看里面有好多知识需要重新把握, ...
- A. Vova and Train ( Codeforces Round #515 (Div. 3) )
题意:一条 L 长的路,一列车长在这条路的 l 到 r 之间,只有在 v 倍数时有灯,但是在 l 到 r 之间的灯是看不见的,问最大看见的灯的个数? 题解:L / v 表示总共的灯的个数, r / v ...
- 玩家属性同步优化-脏数据标记(位运算、数组、stl之bitset)
把大神的帖子中一部分摘抄出来,结合自己写的位运算代码和循环代码(数组遍历)进行性能测试分析并给出结果. 摘自: https://www.gameres.com/827195.html 本文适用于所有脏 ...
- Uncaught ReferenceError: Invalid left-hand side in assignment
Uncaught ReferenceError: Invalid left-hand side in assignment 今天在对个人资料页面增加当浏览别的页面之后第二次访问当前页面,之前填写的内容 ...
- jmeter5实现mysql数据库值提取--单sql提取
字段背景: 在进行接口测试或者压力测试过程中下文的请求需要用到上文请求的值,除了通过正则表达式的方式外,为了更准确的获得数据库值,我们可以直接从数据库提取 一.如何实现数据库的连接.此处不再赘述 点击 ...
- Spring Boot 配置文件 bootstrap vs application 到底有什么区别?
用过 Spring Boot 的都知道在 Spring Boot 中有以下两种配置文件 bootstrap (.yml 或者 .properties) application (.yml 或者 .pr ...
- 腾讯云手动搭建nginx+php-fpm并自启动
自己一点小爱好,搭建了一个小网站植物大战僵尸百科, 使用的是腾讯云,市场里的镜像不好用,所以自己手动搭建一波. centos 7 编译安装 php-7.2.11的步骤 在官网下载php-7.2.11的 ...