详解PHP反射API

具备完整的反射 API,添加了对类、接口、函数、方法和扩展进行_反向工程_的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。php

请注意部份内部 API 丢失了反射扩展工做所需的代码。 例如,一个内置的 PHP 类可能丢失了反射属性的数据。这些少数的状况被认为是错误,不过, 正由于如此,它们应该被发现和修复。数组

知道人体构造和体内真气分布,你能够引导真气到手指,练成一阳指、六脉神剑、弹指神通、九阴白骨爪等;也可让真气汇聚,冲破任督二脉,开辟洞天;还能够逆转全身经脉,练成蛤蟆功…内省的好处可见一斑。安全

反射让代码感知自身结构,有什么好处呢?反射API提供了三种在运行时对代码操做的能力:框架

  1. 设置访问控制权: setAccessible 。可获取私有的方法/属性。 注意: setAccessible 只是让方法/成员变量能够 invoke/getValue/setValue ,并不表明类定义的访问存取权限改变;
  2. 调用函数/方法: invoke/invokeArgs 。配合获取函数参数的API,能够安全的传参和调用函数, call_user_func(_array) 的加强版;
  3. 不依赖构造函数生成实例: newInstanceWithoutConstructor

以单例来讲一下反射API的功能,单例类代码以下:函数

# foo.php
class 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);
 
$class = new ReflectionClass("Foo");
$constructor = $class->getConstructor();
if ((ReflectionProperty::IS_PUBLIC & $constructor->getModifiers()) === 0) {
    $constructor->setAccessible(true);
}
$instance2 = $class->newInstanceWithoutConstructor();
$constructor->invoke($instance2);
var_dump($instance2);
 
# 脚本执行结果
construct, instance id: 1
object(Foo)#1 (0) {
}
construct, instance id: 2
object(Foo)#4 (0) {
}

咱们成功的生成了两个实例,并调用构造函数完成对象初始化。若是没有反射API,这几乎是不可能完成的工做。

反射API的部分类

描         述
Reflection 为类的摘要信息提供静态函数export()
ReflectionClass 类信息和工具
ReflectionMethod 类方法信息和工具
ReflectionParameter 方法参数信息
ReflectionProperty 类属性信息
ReflectionFunction 函数信息和工具
ReflectionExtension PHP扩展信息
ReflectionException 错误类

使用反射API这些类,咱们能够得到在运行时访问对象、函数和脚本中的扩展的信息。经过这些信息咱们能够用来分析类或者构建框架。工具

获取类的信息

咱们在工做中使用过一些用于检查类属性的函数,例如:get_class_methods、getProduct等。这些方法对获取详细类信息有很大的局限性。测试

咱们能够经过反射API类:Reflection 和 ReflectionClass 提供的静态方法 export 来获取类的相关信息, export 能够提供类的几乎全部的信息,包括属性和方法的访问控制状态、每一个方法须要的参数以及每一个方法在脚本文档中的位置。这两个工具类, export 静态方法输出结果是一致的,只是使用方式不一样。ui

首先,构建一个简单的类this

<?php

class Student {
    public    $name;
    protected $age;
    private   $sex;

    public function __construct($name, $age, $sex)
    {
        $this->setName($name);
        $this->setAge($age);
        $this->setSex($sex);
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    protected function setAge($age)
    {
        $this->age = $age;
    }

    private function setSex($sex)
    {
        $this->sex = $sex;
    }
}

使用 ReflectionClass::export() 获取类信息

ReflectionClass::export('Student');

打印结果:编码

Class [ class Student ] {
    @@ D:\wamp\www\test2.php 3-29
    - Constants [0] { }
    - Static properties [0] { }
    - Static methods [0] { }
    - Properties [3] {
        Property [ public $name ]
        Property [ protected $age ]
        Property [ private $sex ]
    }
    - Methods [4] {
        Method [ public method __construct ] {
            @@ D:\wamp\www\test2.php 8 - 13
            - Parameters [3] {
                Parameter #0 [ $name ]
                Parameter #1 [ $age ]
                Parameter #2 [ $sex ]
            }
        }
        Method [ public method setName ] {
            @@ D:\wamp\www\test2.php 15 - 18
            - Parameters [1] {
                Parameter #0 [ $name ]
            }
        }
        Method [ protected method setAge ] {
            @@ D:\wamp\www\test2.php 20 - 23
            - Parameters [1] {
                Parameter #0 [ $age ]
            }
        }
        Method [ private method setSex ] {
            @@ D:\wamp\www\test2.php 25 - 28
            - Parameters [1] {
                Parameter #0 [ $sex ]
            }
        }
    }
}

ReflectionClass类提供了很是多的工具方法,官方手册给的列表以下:

ReflectionClass::__construct — 初始化 ReflectionClass 类
ReflectionClass::export — 导出一个类
ReflectionClass::getConstant — 获取定义过的一个常量
ReflectionClass::getConstants — 获取一组常量
ReflectionClass::getConstructor — 获取类的构造函数
ReflectionClass::getDefaultProperties — 获取默认属性
ReflectionClass::getDocComment — 获取文档注释
ReflectionClass::getEndLine — 获取最后一行的行数
ReflectionClass::getExtension — 根据已定义的类获取所在扩展的 ReflectionExtension 对象
ReflectionClass::getExtensionName — 获取定义的类所在的扩展的名称
ReflectionClass::getFileName — 获取定义类的文件名
ReflectionClass::getInterfaceNames — 获取接口(interface)名称
ReflectionClass::getInterfaces — 获取接口
ReflectionClass::getMethod — 获取一个类方法的 ReflectionMethod。
ReflectionClass::getMethods — 获取方法的数组
ReflectionClass::getModifiers — 获取类的修饰符
ReflectionClass::getName — 获取类名
ReflectionClass::getNamespaceName — 获取命名空间的名称
ReflectionClass::getParentClass — 获取父类
ReflectionClass::getProperties — 获取一组属性
ReflectionClass::getProperty — 获取类的一个属性的 ReflectionProperty
ReflectionClass::getReflectionConstant — Gets a ReflectionClassConstant for a class's constant
ReflectionClass::getReflectionConstants — Gets class constants
ReflectionClass::getShortName — 获取短名
ReflectionClass::getStartLine — 获取起始行号
ReflectionClass::getStaticProperties — 获取静态(static)属性
ReflectionClass::getStaticPropertyValue — 获取静态(static)属性的值
ReflectionClass::getTraitAliases — 返回 trait 别名的一个数组
ReflectionClass::getTraitNames — 返回这个类所使用 traits 的名称的数组
ReflectionClass::getTraits — 返回这个类所使用的 traits 数组
ReflectionClass::hasConstant — 检查常量是否已经定义
ReflectionClass::hasMethod — 检查方法是否已定义
ReflectionClass::hasProperty — 检查属性是否已定义
ReflectionClass::implementsInterface — 接口的实现
ReflectionClass::inNamespace — 检查是否位于命名空间中
ReflectionClass::isAbstract — 检查类是不是抽象类(abstract)
ReflectionClass::isAnonymous — 检查类是不是匿名类
ReflectionClass::isCloneable — 返回了一个类是否可复制
ReflectionClass::isFinal — 检查类是否声明为 final
ReflectionClass::isInstance — 检查类的实例
ReflectionClass::isInstantiable — 检查类是否可实例化
ReflectionClass::isInterface — 检查类是不是一个接口(interface)
ReflectionClass::isInternal — 检查类是否由扩展或核心在内部定义
ReflectionClass::isIterateable — 检查是否可迭代(iterateable)
ReflectionClass::isSubclassOf — 检查是否为一个子类
ReflectionClass::isTrait — 返回了是否为一个 trait
ReflectionClass::isUserDefined — 检查是否由用户定义的
ReflectionClass::newInstance — 从指定的参数建立一个新的类实例
ReflectionClass::newInstanceArgs — 从给出的参数建立一个新的类实例。
ReflectionClass::newInstanceWithoutConstructor — 建立一个新的类实例而不调用它的构造函数
ReflectionClass::setStaticPropertyValue — 设置静态属性的值
ReflectionClass::__toString — 返回 ReflectionClass 对象字符串的表示形式。

使用 Reflection::export() 获取类信息

$prodClass = new ReflectionClass('Student');
Reflection::export($prodClass);

打印结果

Class [ class Student ] {
    @@ D:\wamp\www\test2.php 3-29
    - Constants [0] { }
    - Static properties [0] { }
    - Static methods [0] { }
    - Properties [3] {
        Property [ public $name ]
        Property [ protected $age ]
        Property [ private $sex ]
    }
    - Methods [4] {
        Method [ public method __construct ] {
            @@ D:\wamp\www\test2.php 8 - 13
            - Parameters [3] {
                Parameter #0 [ $name ]
                Parameter #1 [ $age ]
                Parameter #2 [ $sex ]
            }
        }
        Method [ public method setName ] {
            @@ D:\wamp\www\test2.php 15 - 18
            - Parameters [1] {
                Parameter #0 [ $name ]
            }
        }
        Method [ protected method setAge ] {
            @@ D:\wamp\www\test2.php 20 - 23
            - Parameters [1] {
                Parameter #0 [ $age ]
            }
        }
        Method [ private method setSex ] {
            @@ D:\wamp\www\test2.php 25 - 28
            - Parameters [1] {
                Parameter #0 [ $sex ]
            }
        }
    }
}

建立 ReflectionClass对象后,就可使用 Reflection 工具类输出 Student 类的相关信息。Reflection::export() 能够格式化和输出任何实现 Reflector 接口的类的实例。

检查类

前面咱们了解的 ReflectionClass 工具类,知道此类提供了不少的工具方法用于获取类的信息。例如,咱们能够获取到 Student 类的类型,是否能够实例化

工具函数

function classData(ReflectionClass $class) {
    $details = '';
    $name = $class->getName();          // 返回要检查的类名
    if ($class->isUserDefined()) {      // 检查类是否由用户定义
        $details .= "$name is user defined" . PHP_EOL;
    }
    if ($class->isInternal()) {         // 检查类是否由扩展或核心在内部定义
        $details .= "$name is built-in" . PHP_EOL;
    }
    if ($class->isInterface()) {        // 检查类是不是一个接口
        $details .= "$name is interface" . PHP_EOL;
    }
    if ($class->isAbstract()) {         // 检查类是不是抽象类
        $details .= "$name is an abstract class" . PHP_EOL;
    }
    if ($class->isFinal()) {            // 检查类是否声明为 final
        $details .= "$name is a final class" . PHP_EOL;
    }
    if ($class->isInstantiable()) {     // 检查类是否可实例化
        $details .= "$name can be instantiated" . PHP_EOL;
    } else {
        $details .= "$name can not be instantiated" . PHP_EOL;
    }
    return $details;
}

$prodClass = new ReflectionClass('Student');
print classData($prodClass);

打印结果

Student is user defined
Student can be instantiated

除了获取类的相关信息,还能够获取 ReflectionClass 对象提供自定义类所在的文件名及文件中类的起始和终止行等相关源代码信息。

function getClassSource(ReflectionClass $class) {
    $path  = $class->getFileName();  // 获取类文件的绝对路径
    $lines = @file($path);           // 得到由文件中全部行组成的数组
    $from  = $class->getStartLine(); // 提供类的起始行
    $to    = $class->getEndLine();   // 提供类的终止行
    $len   = $to - $from + 1;
    return implode(array_slice($lines, $from - 1, $len));
}

$prodClass = new ReflectionClass('Student');
var_dump(getClassSource($prodClass));

打印结果

string 'class Student {
    public    $name;
    protected $age;
    private   $sex;

    public function __construct($name, $age, $sex)
    {
        $this->setName($name);
        $this->setAge($age);
        $this->setSex($sex);
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    protected function setAge($age)
    {
        $this->age = $age;
    }

    private function setSex($sex)
    {
        $this->sex = $sex;
    }
}
' (length=486)

咱们看到 getClassSource 接受一个 ReflectionClass 对象做为它的参数,并返回相应类的源代码。该函数忽略了错误处理,在实际中应该要检查参数和结果代码!

检查方法

相似于检查类,ReflectionMethod 对象能够用于检查类中的方法。

得到 ReflectionMethod 对象的方法有两种:

第一种是经过 ReflectionClass::getMethods() 得到 ReflectionMethod 对象的数组,这种方式的好处是不用提早知道方法名,会返回类中全部方法的 ReflectionMethod  对象。

第二种是直接使用 ReflectionMethod  类实例化对象,这种方式只能获取一个类方法对象,须要提早知道方法名。

ReflectionMethod 对象的工具方法:

ReflectionMethod::__construct — ReflectionMethod 的构造函数
ReflectionMethod::export — 输出一个回调方法
ReflectionMethod::getClosure — 返回一个动态创建的方法调用接口,译者注:可使用这个返回值直接调用非公开方法。
ReflectionMethod::getDeclaringClass — 获取反射函数调用参数的类表达
ReflectionMethod::getModifiers — 获取方法的修饰符
ReflectionMethod::getPrototype — 返回方法原型 (若是存在)
ReflectionMethod::invoke — Invoke
ReflectionMethod::invokeArgs — 带参数执行
ReflectionMethod::isAbstract — 判断方法是不是抽象方法
ReflectionMethod::isConstructor — 判断方法是不是构造方法
ReflectionMethod::isDestructor — 判断方法是不是析构方法
ReflectionMethod::isFinal — 判断方法是否认义 final
ReflectionMethod::isPrivate — 判断方法是不是私有方法
ReflectionMethod::isProtected — 判断方法是不是保护方法 (protected)
ReflectionMethod::isPublic — 判断方法是不是公开方法
ReflectionMethod::isStatic — 判断方法是不是静态方法
ReflectionMethod::setAccessible — 设置方法是否访问
ReflectionMethod::__toString — 返回反射方法对象的字符串表达

ReflectionClass::getMethods()

咱们能够经过 ReflectionClass::getMethods() 得到 ReflectionMethod 对象的数组。

$prodClass = new ReflectionClass('Student');
$methods = $prodClass->getMethods();
var_dump($methods);

打印结果

array (size=4)
  0 => &
    object(ReflectionMethod)[2]
      public 'name' => string '__construct' (length=11)
      public 'class' => string 'Student' (length=7)
  1 => &
    object(ReflectionMethod)[3]
      public 'name' => string 'setName' (length=7)
      public 'class' => string 'Student' (length=7)
  2 => &
    object(ReflectionMethod)[4]

能够看到咱们获取到了 Student 的 ReflectionMethod 对象数组,每一个元素是一个对象,其中有两个公共的属性,name 为方法名,class 为所属类。咱们能够调用对象方法来获取方法的信息。

ReflectionMethod

直接使用 ReflectionMethod 类获取类方法有关信息

$method = new ReflectionMethod('Student', 'setName');
var_dump($method);

打印结果

object(ReflectionMethod)[1]
  public 'name' => string 'setName' (length=7)
  public 'class' => string 'Student' (length=7)

注意

在PHP5中,若是被检查的方法只返回对象(即便对象是经过引用赋值或传递的),那么 ReflectionMethod::retursReference() 不会返回 true。只有当被检测的方法已经被明确声明返回引用(在方法名前面有&符号)时,ReflectionMethod::returnsReference() 才返回 true。

检查方法参数

在PHP5中,声明类方法时能够限制参数中对象的类型,所以检查方法的参数变得很是必要。

相似于检查方法,ReflectionParameter 对象能够用于检查类中的方法,该对象能够告诉你参数的名称,变量是否能够按引用传递,还能够告诉你参数类型提示和方法是否接受空值做为参数。

得到 ReflectionParameter 对象的方法有一样两种,这和获取 ReflectionMethod 对象很是相似:

第一种是经过 ReflectionMethod::getParameters() 方法返回 ReflectionParameter 对象数组,这种方法能够获取到一个方法的所有参数对象。

第二种是直接使用 ReflectionParameter  类实例化获取对象,这种方法只能获取到单一参数的对象。

ReflectionParameter 对象的工具方法:

rameter::allowsNull — Checks if null is allowed
ReflectionParameter::canBePassedByValue — Returns whether this parameter can be passed by value
ReflectionParameter::__clone — Clone
ReflectionParameter::__construct — Construct
ReflectionParameter::export — Exports
ReflectionParameter::getClass — Get the type hinted class
ReflectionParameter::getDeclaringClass — Gets declaring class
ReflectionParameter::getDeclaringFunction — Gets declaring function
ReflectionParameter::getDefaultValue — Gets default parameter value
ReflectionParameter::getDefaultValueConstantName — Returns the default value's constant name if default value is constant or null
ReflectionParameter::getName — Gets parameter name
ReflectionParameter::getPosition — Gets parameter position
ReflectionParameter::getType — Gets a parameter's type
ReflectionParameter::hasType — Checks if parameter has a type
ReflectionParameter::isArray — Checks if parameter expects an array
ReflectionParameter::isCallable — Returns whether parameter MUST be callable
ReflectionParameter::isDefaultValueAvailable — Checks if a default value is available
ReflectionParameter::isDefaultValueConstant — Returns whether the default value of this parameter is constant
ReflectionParameter::isOptional — Checks if optional
ReflectionParameter::isPassedByReference — Checks if passed by reference
ReflectionParameter::isVariadic — Checks if the parameter is variadic
ReflectionParameter::__toString — To string

ReflectionMethod::getParameters()

同获取方法,此方法会返回一个数组,包含方法每一个参数的 ReflectionParameter 对象

$method = new ReflectionMethod('Student', 'setName');
$params = $method->getParameters();
var_dump($params);

打印结果

array (size=1)
  0 => &
    object(ReflectionParameter)[2]
      public 'name' => string 'name' (length=4)

ReflectionParameter

咱们来了解一下这种方式,为了更好的理解,我修改一下 Student 类的 setName方法,增长两个参数 a, b

...
    public function setName($name, $a, $b)
    {
        $this->name = $name;
    }
...

首先咱们看一下 ReflectionParameter 类的构造方法

public ReflectionParameter::__construct ( string $function , string $parameter )

能够看到该类实例化时接收两个参数:

$function:当须要获取函数为公共函数时只需传函数名称便可。当该函数是某个类方法时,须要传递一个数组,格式为:array('class', 'function')。

$parameter:这个参数能够传递两种,第一种为参数名(无$符号),第二种为参数索引。注意:不管是参数名仍是索引,该参数都必须存在,不然会报错。

下面举例:

$params = new ReflectionParameter(array('Student', 'setName'), 1);
var_dump($params);

打印结果

object(ReflectionParameter)[1]
  public 'name' => string 'a' (length=1)

咱们再定义一个函数测试一下

function foo($a, $b, $c) { }
$reflect = new ReflectionParameter('foo', 'c');
var_dump($reflect);

打印结果

object(ReflectionParameter)[2]
  public 'name' => string 'c' (length=1)

结语

php的反射API功能很是的强大,它能够将一个类的详细信息获取出来。咱们能够经过反射API编写个类来动态调用Module对象,该类能够自由加载第三方插件并集成进已有的系统。而不须要把第三方的代码硬编码进原有的代码中。虽然实际开发中使用反射状况比较少,但了解反射API对工做中对代码结构的了解和开发业务模式帮助仍是很是大的。

相关文章
相关标签/搜索