[关闭]
@a5635268 2016-01-15T21:25:34.000000Z 字数 7367 阅读 1699

PHP 反射机制Reflection

PHP


简介

PHP Reflection API是PHP5才有的新功能,它是用来导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。

  1. class Reflection { }
  2. interface Reflector { }
  3. class ReflectionException extends Exception { }
  4. class ReflectionFunction implements Reflector { }
  5. class ReflectionParameter implements Reflector { }
  6. class ReflectionMethod extends ReflectionFunction { }
  7. class ReflectionClass implements Reflector { }
  8. class ReflectionObject extends ReflectionClass { }
  9. class ReflectionProperty implements Reflector { }
  10. class ReflectionExtension implements Reflector { }

用得比较多的就只有两个ReflectionClassReflectionObject,两个的用法都一样,只是前者针对类,后者针对对象,后者是继承前者的类;然后其中又有一些属性或方法能返回对应的Reflection对象(ReflectionProperty以及ReflectionMethod)

ReflectionClass

具体参考手册:http://php.net/manual/zh/class.reflectionclass.php

通过ReflectionClass,我们可以得到Person类的以下信息:

  1. <?php
  2. namespace app;
  3. class Person{
  4. /**
  5. * For the sake of demonstration, we"re setting this private
  6. */
  7. private $_allowDynamicAttributes = false;
  8. /** type=primary_autoincrement */
  9. protected $id = 0;
  10. /** type=varchar length=255 null */
  11. protected $name;
  12. /** type=text null */
  13. protected $biography;
  14. public function getId(){
  15. return $this->id;
  16. }
  17. public function setId($v){
  18. $this->id = $v;
  19. }
  20. public function getName(){
  21. return $this->name;
  22. }
  23. public function setName($v){
  24. $this->name = $v;
  25. }
  26. public function getBiography(){
  27. return $this->biography;
  28. }
  29. public function setBiography($v){
  30. $this->biography = $v;
  31. }
  32. }
  33. //传递类名进来
  34. $class = new \ReflectionClass('app\Person');
  35. //获取属性,不管该属性是否public
  36. $properties = $class->getProperties();
  37. foreach($properties as $property) {
  38. echo $property->getName()."\n";
  39. }
  40. // 输出:
  41. // _allowDynamicAttributes
  42. // id
  43. // name
  44. // biography
  45. //默认情况下,ReflectionClass会获取到所有的属性,private 和 protected的也可以。如果只想获取到private属性,就要额外传个参数:
  46. /*
  47. * ReflectionProperty::IS_STATIC
  48. * ReflectionProperty::IS_PUBLIC
  49. * ReflectionProperty::IS_PROTECTED
  50. * ReflectionProperty::IS_PRIVATE
  51. */
  52. //↓↓ 注意一个|组合: 获得IS_PRIVATE或者IS_PROTECTED的属性
  53. $private_properties = $class->getProperties(\ReflectionProperty::IS_PRIVATE|\ReflectionProperty::IS_PROTECTED);
  54. foreach($private_properties as $property) {
  55. //↓↓如果该属性是受保护的属性;
  56. if($property->isProtected()) {
  57. // ↓↓ 获取注释
  58. $docblock = $property->getDocComment();
  59. preg_match('/ type\=([a-z_]*) /', $property->getDocComment(), $matches);
  60. echo $matches[1]."\n";
  61. }
  62. }
  63. // Output:
  64. // primary_autoincrement
  65. // varchar
  66. // text
  67. $data = array("id" => 1, "name" => "Chris", "biography" => "I am am a PHP developer");
  68. foreach($data as $key => $value) {
  69. if(!$class->hasProperty($key)) {
  70. throw new \Exception($key." is not a valid property");
  71. }
  72. if(!$class->hasMethod("get".ucfirst($key))) {
  73. throw new \Exception($key." is missing a getter");
  74. }
  75. if(!$class->hasMethod("set".ucfirst($key))) {
  76. throw new \Exception($key." is missing a setter");
  77. }
  78. $object = new Person();
  79. // http://php.net/manual/zh/class.reflectionmethod.php
  80. // getMethod 获得一个该方法的reflectionmethod对象,然后使用里面的invoke方法;
  81. $setter = $class->getMethod("set".ucfirst($key));
  82. $ok = $setter->invoke($object, $value);
  83. // Get the setter method and invoke it
  84. $getter = $class->getMethod("get".ucfirst($key));
  85. $objValue = $getter->invoke($object);
  86. // Now compare
  87. if($value == $objValue) {
  88. echo "Getter or Setter has modified the data.\n";
  89. } else {
  90. echo "Getter and Setter does not modify the data.\n";
  91. }
  92. }

getMethod and invoke

ReflectionClass::getMethod — 获取一个类方法的 ReflectionMethod(这是个类报告了一个方法的有关信息)。

具体的参考:

  1. <?php
  2. class HelloWorld {
  3. public function sayHelloTo($name) {
  4. return 'Hello ' . $name;
  5. }
  6. }
  7. $obj = new HelloWorld();
  8. // 第一个参数可以是对象,也可以是类
  9. $reflectionMethod = new ReflectionMethod($obj , 'sayHelloTo');
  10. /*
  11. * public mixed ReflectionMethod::invoke ( object $object [, mixed $parameter [, mixed $... ]] )
  12. * 1. 获得某个类方法的ReflectionMethod
  13. * 2. $object 该方法所在的类实例的对象,然后第二参数起对号入座到该方法的每个参数;
  14. * 3. 通过invoke就可以执行这个方法了
  15. */
  16. echo $reflectionMethod->invoke($obj, 'GangGe');

getProperty

获得一个 ReflectionProperty 类实例 http://cn2.php.net/manual/zh/class.reflectionproperty.php

getValue获取属性值
  1. public mixed ReflectionProperty::getValue ([ object $object ] )

如果该获得该实例的类属性不是一个static的属性,就必须传该类的实例

  1. <?php
  2. class Foo {
  3. public static $staticProperty = 'foobar';
  4. public $property = 'barfoo';
  5. protected $privateProperty = 'foofoo';
  6. }
  7. $reflectionClass = new ReflectionClass('Foo');
  8. var_dump($reflectionClass->getProperty('staticProperty')->getValue()); //静态属性可以不加参数
  9. var_dump($reflectionClass->getProperty('property')->getValue(new Foo)); //非静态属性必须加传一个类实例
  10. $reflectionProperty = $reflectionClass->getProperty('privateProperty'); //受保护的属性就要通过setAccessible设置其属性
  11. $reflectionProperty->setAccessible(true);
  12. var_dump($reflectionProperty->getValue(new Foo));
  13. ?>

Example

模拟YII框架中控制器调用方法的实现

  1. <?php
  2. if (PHP_SAPI != 'cli') {
  3. exit('Please run it in terminal!');
  4. }
  5. if ($argc < 3) {
  6. exit('At least 2 arguments needed!');
  7. }
  8. $controller = ucfirst($argv[1]) . 'Controller';
  9. $action = 'action' . ucfirst($argv[2]);
  10. // 检查类是否存在
  11. if (!class_exists($controller)) {
  12. exit("Class $controller does not existed!");
  13. }
  14. // 获取类的反射
  15. $reflector = new ReflectionClass($controller);
  16. // 检查方法是否存在
  17. if (!$reflector->hasMethod($action)) {
  18. exit("Method $action does not existed!");
  19. }
  20. // 取类的构造函数,返回的是ReflectionMethod对象
  21. $constructor = $reflector->getConstructor();
  22. // 取构造函数的参数
  23. $parameters = $constructor->getParameters();
  24. // 遍历参数
  25. foreach ($parameters as $key => $parameter) {
  26. // 获取参数声明的类
  27. $injector = new ReflectionClass($parameter->getClass()->name);
  28. // 实例化参数声明类并填入参数列表
  29. $parameters[$key] = $injector->newInstance();
  30. }
  31. // 使用参数列表实例 controller 类
  32. $instance = $reflector->newInstanceArgs($parameters);
  33. // 执行
  34. $instance->$action();
  35. class HelloController
  36. {
  37. private $model;
  38. public function __construct(TestModel $model)
  39. {
  40. $this->model = $model;
  41. }
  42. public function actionWorld()
  43. {
  44. echo $this->model->property, PHP_EOL;
  45. }
  46. }
  47. class TestModel
  48. {
  49. public $property = 'property';
  50. }

TP框架中实现前后控制器

  1. <?php
  2. class BlogAction {
  3. public function detail() {
  4. echo 'detail' . "\r\n";
  5. }
  6. public function test($year = 2014, $month = 4, $day = 21) {
  7. echo $year . '--' . $month . '--' . $day . "\r\n";
  8. }
  9. public function _before_detail() {
  10. echo __FUNCTION__ . "\r\n";
  11. }
  12. public function _after_detail() {
  13. echo __FUNCTION__ . "\r\n";
  14. }
  15. }
  16. // 执行detail方法
  17. $method = new ReflectionMethod('BlogAction', 'detail');
  18. $instance = new BlogAction();
  19. // 进行权限判断
  20. if ($method->isPublic()) {
  21. $class = new ReflectionClass('BlogAction');
  22. // 执行前置方法
  23. if ($class->hasMethod('_before_detail')) {
  24. $beforeMethod = $class->getMethod('_before_detail');
  25. if ($beforeMethod->isPublic()) {
  26. $beforeMethod->invoke($instance);
  27. }
  28. }
  29. $method->invoke(new BlogAction);
  30. // 执行后置方法
  31. if ($class->hasMethod('_after_detail')) {
  32. $beforeMethod = $class->getMethod('_after_detail');
  33. if ($beforeMethod->isPublic()) {
  34. $beforeMethod->invoke($instance);
  35. }
  36. }
  37. }
  38. // 执行带参数的方法
  39. $method = new ReflectionMethod('BlogAction', 'test');
  40. $params = $method->getParameters();
  41. foreach ($params as $param) {
  42. $paramName = $param->getName();
  43. if (isset($_REQUEST[$paramName])) {
  44. $args[] = $_REQUEST[$paramName];
  45. } elseif ($param->isDefaultValueAvailable()) {
  46. $args[] = $param->getDefaultValue();
  47. }
  48. }
  49. if (count($args) == $method->getNumberOfParameters()) {
  50. $method->invokeArgs($instance, $args);
  51. } else {
  52. echo 'parameters is wrong!';
  53. }

其他参考

  1. /**
  2. * 执行App控制器
  3. */
  4. public function execApp() {
  5. // 创建action控制器实例
  6. $className = MODULE_NAME . 'Controller';
  7. $namespaceClassName = '\\apps\\' . APP_NAME . '\\controller\\' . $className;
  8. load_class($namespaceClassName, false);
  9. if (!class_exists($namespaceClassName)) {
  10. throw new \Exception('Oops! Module not found : ' . $namespaceClassName);
  11. }
  12. $controller = new $namespaceClassName();
  13. // 获取当前操作名
  14. $action = ACTION_NAME;
  15. // 执行当前操作
  16. //call_user_func(array(&$controller, $action)); // 其实吧,用这个函数足够啦!!!
  17. try {
  18. $methodInfo = new \ReflectionMethod($namespaceClassName, $action);
  19. if ($methodInfo->isPublic() && !$methodInfo->isStatic()) {
  20. $methodInfo->invoke($controller);
  21. } else { // 操作方法不是public类型,抛出异常
  22. throw new \ReflectionException();
  23. }
  24. } catch (\ReflectionException $e) {
  25. // 方法调用发生异常后,引导到__call方法处理
  26. $methodInfo = new \ReflectionMethod($namespaceClassName, '__call');
  27. $methodInfo->invokeArgs($controller, array($action, ''));
  28. }
  29. return;
  30. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注