若转载教程,请注明出自SW-X框架官方文档

1、类的反射机制

反射是什么?
它是指在PHP运行状态中,扩展分析PHP程序,导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。
这种动态获取的信息以及动态调用对象的方法的功能称为反射API。
反射是操纵面向对象范型中元模型的API,其功能十分强大,可帮助我们构建复杂,可扩展的应用。
其用途如:自动加载插件,自动生成文档,甚至可用来扩充PHP语言。
PHP反射api由若干类组成,可帮助我们用来访问程序的元数据或者同相关的注释交互。
借助反射我们可以获取诸如类实现了那些方法,创建一个类的实例(不同于用new创建),调用一个方法(也不同于常规调用),传递参数,动态调用类的静态方法。
反射api是PHP内建的面向对象技术的扩展,包括一些类,异常和接口,综合使用他们可用来帮助我们分析其它类,接口,方法,属性,方法和扩展。
这些面向对象的扩展被称之为反射。
在Class反射机制中,用得比较多的是ReflectionClass类 和 ReflectionMethod类。
所有Reflectio类可以参考官方手册:http://php.net/manual/zh/book.reflection.php。

A、ReflectionClass类的主要作用

ReflectionClass类是PHP系统自带提供的一个Class,该类主要用于打印出指定类的内部结构,类似于var_dump()函数的功能,但使用ReflectionClass类,反射出的对象结构,要比var_dump()函数打印出来的内容要丰富很多。
通过ReflectionClass类,我们可以获得对象结构的以下信息:

  1. 1、获得类的成员属性列表
  2. 2、获得类的成员函数列表
  3. 3、检测类是否为final或者abstract
  4. 4、检测类是否含有某个成员函数

等诸多功能。

B、通过ReflectionClass类实例化自定义类

ReflectionClass类的返回值比较特殊,在该类实例化时,需要参入一个自定义类的名称,用于获得该类的内部结构,
同时,如果我们需要实例化自定义类的话,可以使用ReflectionClass的newInstanceArgs()成员函数,
该方法的传参是一个数组,并将其转换为变量,传入自定义类的构造函数中。
详细使用案例如下:

<?php
# 自定义类
class Test {
    public function __construct($name){
        echo $name;
    }
}
# 使用ReflectionClass类
$class = new ReflectionClass('Test');
# 使用newInstanceArgs()成员函数实例化自定义类
$class->newInstanceArgs(['name'=>'你妹']);

C、通过ReflectionClass类获得成员属性名称

如果我们需要获得自定义类的内部的成员属性名称时,可以使用ReflectionClass的getProperties()成员函数,
该函数的返回值是一个数组:

<?php
# 自定义类
class Test {
    public    $name;
    private   $id;
    protected $appid;
}
# 使用ReflectionClass类
$class = new ReflectionClass('Test');
# 使用getProperties()成员函数,获得自定义类的成员属性名称
$properties = $class->getProperties();
foreach ($properties as $property) {
    echo $property->getName().'<br/>';
}

通过运行上面的代码,细心的朋友可能已经发现了,
上面分别有public、private、protected三种控制权限的成员属性,
但getProperties()成员函数都直接将其获得了,但并没有返回这些成员属性所对应的控制权限分别是什么?
如果只想获取得指定权限的成员属性,getProperties()成员函数就要额外传递一个参数:

$res = $class->getProperties(参数);

可用的参数列表如下:

1、ReflectionProperty::IS_STATIC,获得静态成员属性名称
2、ReflectionProperty::IS_PUBLIC,获得public权限成员属性名称
3、ReflectionProperty::IS_PROTECTED,获得protected权限成员属性名称
4、ReflectionProperty::IS_PRIVATE,获得private权限成员属性名称

范围查询使用 | 符号进行分割(相当于OR)
多条件重新使用 && 符号进行分割(相当于AND)
具体使用代码如下:

<?php
# 自定义类
class Test {
    public    $name;
    private   $id;
    protected $appid;
}
# 使用ReflectionClass类
$class = new ReflectionClass('Test');
# 获得public权限的成员属性名称
$properties = $class->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($properties as $property) {
    echo 'public:'.$property->getName().'<br/>';
}

D、通过ReflectionClass类获得成员属性对应的注释

ReflectionClass类获得成员属性对应的注释内容是有许多限制的,其中有一条特别重要,只能使用:

/**
* 注释内容
*/

这样的注释风格,同时注释必须紧贴上成员属性上一行中。
在对使用getProperties()成员函数获得成员属性名次后,再对应使用->getDocComment()成员函数,
就能获得其对应的注释内容,案例代码如下:

<?php
# 自定义类
class Test {
    /**
     * 草拟妹
     */
    public    $name;
    private   $id;
    protected $appid;
}
# 使用ReflectionClass类
$class = new ReflectionClass('Test');
# 获得public权限的成员属性名称
$properties = $class->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($properties as $property) {
    # 获得成员属性对应的注释内容
    # 注意:这里是对$property变量使用getDocComment()成员函数
    $docblock = $property->getDocComment();
    var_dump($docblock);
}

E、通过ReflectionClass类获得成员函数名称

如果我们需要获得自定义类的内部的成员函数名称时,可以使用ReflectionClass的getMethods()成员函数,
该函数的返回值是一个数组:

<?php
# 自定义类
class Test {
    public function A() {}
    private function B() {}
    protected function C() {}
}
# 使用ReflectionClass类
$class = new ReflectionClass('Test');
# 获得成员函数
$methods = $class->getMethods();
foreach ($methods as $method) {
    echo '方法名称:'.$method->name.'<br/>';
}

通过运行上面的代码,细心的朋友可能已经发现了,
上面分别有public、private、protected三种控制权限的成员函数,
但getMethods()成员函数都直接将其获得了,但并没有返回这些成员函数所对应的控制权限分别是什么?
如果只想获取得指定权限的成员函数,那么getMethods()成员函数就要额外传递一个参数:

$res = $class->getMethods(参数);

可用的参数列表如下:

1、ReflectionMethod::IS_STATIC,获得静态成员函数名称
2、ReflectionMethod::IS_PUBLIC,获得public权限成员函数名称
3、ReflectionMethod::IS_PROTECTED,获得protected权限成员函数名称
4、ReflectionMethod::IS_PRIVATE,获得private权限成员函数名称
5、ReflectionMethod::IS_ABSTRACT,获得抽象的成员函数名称
6、ReflectionMethod::IS_FINAL,获得唯一的成员函数名称

范围查询使用 | 符号进行分割(相当于OR)
多条件重新使用 && 符号进行分割(相当于AND)
具体使用代码如下:

<?php
# 自定义类
class Test {
    public function A() {}
    private function B() {}
    protected function C() {}
}
# 使用ReflectionClass类
$class = new ReflectionClass('Test');
# 获得成员函数
$methods = $class->getMethods();
foreach ($methods as $method) {
    echo '类名:'.$method->class.' ';
    echo '方法名称:'.$method->name.'<br/>';
}

F、通过ReflectionClass类获得成员函数对应的注释

ReflectionClass类获得成员函数对应的注释内容是有许多限制的,其中有一条特别重要,只能使用:

/**
* 注释内容
*/

这样的注释风格,同时注释必须紧贴上成员函数上一行中。
在对使用getDocComment()成员函数获得成员函数名之后,再对应使用->getDocComment()成员函数,与成员属性用法一致,
就能获得其对应的注释内容,案例代码如下:

<?php
# 自定义类
class Test {
    /**
     * 尼妹的函数
     * @param string $name 打死你
     * @return void
     */
    public function A() {}
}
# 使用ReflectionClass类
$class = new ReflectionClass('Test');
# 获得成员函数名称
$methods = $class->getMethods();
foreach ($methods as $method) {
    # 获得成员函数对应的注释内容
    var_dump($method->getDocComment());
}

G、成员属性 和 成员函数的注释风格

鉴于反射机制只能获得到

/**
* 注释内容
*/

这样的注释方式,所以不管是成员函数还是成员属性,都应该统一使用这种注释风格。
详细参考如下:

<?php
class Test {
    /**
     * 名称
    */
    public $name;
    /**
     * 年龄
    */
    private $age;
    /**
     * 尼妹的函数
     * @param string $name 打死你
     * @return void
    */
    public function A() {}
}

H、成员属性、成员函数和Class类的命名规范

不管是成员属性还是成员函数:
public、protected类型的,使用小驼峰命名法,例如:$orderType;
private类型的,使用下划线_开头,加小驼峰命名法,例如:$_orderType;
什么是驼峰命名?
就是当变量名或函数名是由一个或多个单词连结在一起,而构成的唯一识别字时,第一个单词以小写字母开始;
第二个单词的首字母大写或每一个单词的首字母都采用大写字母,例如:$myFirstName、$myLastName,这样的变量名看上去就像骆驼峰一样此起彼伏,故得名。
小驼峰法:除第一个单词之外,其他单词首字母大写。
大驼峰法:相比小驼峰法,大驼峰法把第一个单词的首字母也大写了
而Class类一般都是使用大驼峰命名法。
详细参考如下:

<?php
class TestApi {
    /**
     * 订单类型
    */
    public $orderType;
    /**
     * 订单ID号
    */
    private $_orderId;
    /**
     * 尼妹的函数
     * @param string $name 打死你
     * @return void
    */
    public function orderBack() {}
}

I、成员函数注释中的标记元说明

在成员函数的注释中,我们通常会看见两个标记元,就是:@param@return
而除了这2个标记元外,PHP中通常还存在以下这些标记元信息, 他们通常代表着IT界的注释规范:

标记元 描述
@access 该标记元用于指明的权限:private、public或proteced
@author 指明作者
@copyright 指明版权信息
@version 指明版本信息
@deprecated 指明不用或者废弃的关键字,例如说明这个方法在哪个版本之后即将弃用
@global 指明在此成员函数中引用了哪些全局变量
@license 相当于html标签中的,首先是URL,接着是要显示的内容
例如百度
可以写作
@license
http://www.baidu.com 百度
@final 指明是一个唯一的类、方法、属性,禁止派生、修改。
@abstrcut 说明当前类是一个抽象类或抽象函数
@static 说明当前方法、属性是静态的。
@todo 指明应该改进或没有实现的地方
@throws 指明此成员函数可能抛出的错误异常,以及其发生的情况
@param 指明一个函数的参数,包含类型、参数名称、参数描述等
@return 指明一个函数的返回值,包含类型

详细的注释风格可以参照下列案例,在实际项目中,并不是所有标记元都会全部用上,需要看团队具体的使用要求,下面代码仅供参考:

<?php
class TestApi {
    /**
     * 尼妹的
     * 这是一个给小黄牛用的函数,哈哈,不服你来打我啊
     *
     * @author 小黄牛
     * @version v1.0.0.1
     * @deprecated v1.0.9.* 版本后弃用 
     * @global 无
     * @todo 后期这个函数需要优化到神一样的执行速度
     * @param string $name 打死你
     * @return void
    */
    public function orderBack() {}
}

不过朋友们需要注意的是,每个标记元之间,都是使用一个空格进行分割,标记元例如@param中的参数间隔也是如此,
千万不要跟面向过程中的function(){}自定义封装函数的规范搞混了。
不过function(){}自定义封装函数注释中的标记元,可以参考上面的。

注意:ReflectionMethod 类是一个基于ReflectionClass 类的反射机制的扩展类,两个类混合使用可以实现许多功能,朋友们可以参照课件中的官方手册,尝试使用下ReflectionMethod 类。