• PHP反射API


    PHP反射API

      近期忙着写项目,没有学习什么特别新的东西,所以好长时间没有更新博客。我们的项目用的是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 '

    ';
    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 }

    写的比较急,难免会有错误,还请大神们多多指正。
    转载请注明出处:http://www.cnblogs.com/zyf-zhaoyafei/p/4922893.html
    本博客同步更新到我的个人网站:www.zhaoyafei.cn
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    更多技术文章请搜索千锋PHP,做真实的自己,用良心做教育。
    互联网+时代,时刻要保持学习,携手千锋PHP,Dream It Possible。

  • 相关阅读:
    关于C#登录三层
    SQL 语句关于分页的写法
    C# 如何去掉button按钮的边框线
    20151220
    继承
    对象的旅行
    多态
    封装
    OO大原则
    javascript
  • 原文地址:https://www.cnblogs.com/lhbryant/p/6929039.html
Copyright © 2020-2023  润新知