作者简介:

       姜海强:闷骚码农,互联网行业摸爬滚打数余载,先后担任中国体育直播TV主程、网信集团先锋支付架构师、奇虎360服务器端资深开发。热爱技术,喜欢分享,热衷领域:PHP/Golang语言、面向对象设计模式、Redis、Yaf、Yii2、微服务等。

视频课程

yaf+yar微服务-腾讯课堂
yaf+yar微服务-51CTO学院
CSDN学院

Github

个人主页
swoole-boot
roach
roach-orm

QQ群:

姜海强的QQ群

公众号:

360tryst公众号

反射

反射是一种动态获取类成员属性和成员方法,并且可以动态创建对象进行调用的一种技术。

Java语言有一个特别著名的框架叫Spring,几乎任何Java程序员对这个框架非常熟悉,这个框架早期最核心的思想就是DI(依赖注入),底层的技术实现核心就是依赖反射技术。

PHP本身其实不通过反射也能动态创建对象,通过new关键字就可以创建对象,如下例程:

  1. <?php
  2. namespace popo;
  3. class User
  4. {
  5. /**
  6. * @var string
  7. * @datetime 2020/7/1 9:20 PM
  8. * @author roach
  9. * @email jhq0113@163.com
  10. */
  11. public $userName;
  12. /**
  13. * @var string
  14. * @datetime 2020/7/1 9:20 PM
  15. * @author roach
  16. * @email jhq0113@163.com
  17. */
  18. public $password;
  19. }
  20. $class = 'popo\User';
  21. $user = new $class();
  22. $user->userName = 'reflect';
  23. var_dump($user);

以上例程输出:

  1. object(popo\User)#1 (2) {
  2. ["userName"]=>
  3. string(7) "reflect"
  4. ["password"]=>
  5. NULL
  6. }

1. 反射扩展

PHP提供强大的反射库,绝大多数的开源框架都使用了反射扩展实现了DI,下面来介绍一下上面例程如何用反射去实现。

  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Jiang Haiqiang
  5. * Date: 2020/7/1
  6. * Time: 9:19 PM
  7. */
  8. namespace popo;
  9. class User
  10. {
  11. /**
  12. * @var string
  13. * @datetime 2020/7/1 9:20 PM
  14. * @author roach
  15. * @email jhq0113@163.com
  16. */
  17. public $userName;
  18. /**
  19. * @var string
  20. * @datetime 2020/7/1 9:20 PM
  21. * @author roach
  22. * @email jhq0113@163.com
  23. */
  24. public $password;
  25. }
  26. $class = new \ReflectionClass('popo\User');
  27. $user = $class->newInstance();
  28. $user->userName = 'reflect';
  29. var_dump($user);

以上例程输出:

  1. object(popo\User)#2 (2) {
  2. ["userName"]=>
  3. string(7) "reflect"
  4. ["password"]=>
  5. NULL
  6. }

从以上例程可以看出,PHP的反射非常简单,但是功能却十分强大。

1.1 反射获取类所有成员方法和属性

  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Jiang Haiqiang
  5. * Date: 2020/7/1
  6. * Time: 9:19 PM
  7. */
  8. namespace popo;
  9. class User
  10. {
  11. /**
  12. * @var string
  13. * @datetime 2020/7/1 9:34 PM
  14. * @author roach
  15. * @email jhq0113@163.com
  16. */
  17. private $_salt = 'sdfas#$!@DDFSDF';
  18. /**
  19. * @var string
  20. * @datetime 2020/7/1 9:34 PM
  21. * @author roach
  22. * @email jhq0113@163.com
  23. */
  24. protected $_token;
  25. /**
  26. * @var string
  27. * @datetime 2020/7/1 9:20 PM
  28. * @author roach
  29. * @email jhq0113@163.com
  30. */
  31. public $userName;
  32. /**
  33. * @var string
  34. * @datetime 2020/7/1 9:20 PM
  35. * @author roach
  36. * @email jhq0113@163.com
  37. */
  38. public $password;
  39. /**
  40. * @param string $userId
  41. * @return string
  42. * @datetime 2020/7/1 9:35 PM
  43. * @author roach
  44. * @email jhq0113@163.com
  45. */
  46. private function _createToken($userId)
  47. {
  48. return hash_hmac('sha1', $this->_salt.$userId, $this->_salt);
  49. }
  50. /**
  51. * @param string $userId
  52. * @param string $token
  53. * @return bool
  54. * @datetime 2020/7/1 9:36 PM
  55. * @author roach
  56. * @email jhq0113@163.com
  57. */
  58. protected function _checkToken($userId, $token)
  59. {
  60. return $this->_createToken($userId) === $token;
  61. }
  62. /**
  63. * @return string
  64. * @datetime 2020/7/1 9:37 PM
  65. * @author roach
  66. * @email jhq0113@163.com
  67. */
  68. public function getToken()
  69. {
  70. return $this->_token;
  71. }
  72. }
  73. $className = 'popo\User';
  74. $class = new \ReflectionClass($className);
  75. $properties = $class->getProperties();
  76. foreach ($properties as $property) {
  77. /**
  78. * @var \ReflectionProperty $property
  79. */
  80. echo '类:'.$className.'拥有属性:'.$property->name
  81. .', private:'.$property->isPrivate()
  82. .', protected:'.$property->isProtected()
  83. .', public:'.$property->isPublic().PHP_EOL;
  84. }
  85. $methods = $class->getMethods();
  86. foreach ($methods as $method) {
  87. /**
  88. * @var \ReflectionMethod $method
  89. */
  90. echo '类:'.$className.'拥有方法:'.$method->name
  91. .', private:'.$method->isPrivate()
  92. .', protected:'.$method->isProtected()
  93. .', public:'.$method->isPublic().PHP_EOL;
  94. }

以上例程输出:

  1. 类:popo\User拥有方法:_createToken, private:1, protected:, public:
  2. 类:popo\User拥有方法:_checkToken, private:, protected:1, public:
  3. 类:popo\User拥有法:getToken, private:, protected:, public:1
  4. 类:popo\User拥有属性:_salt, private:1, protected:, public:
  5. 类:popo\User拥有属性:_token, private:, protected:1, public:
  6. 类:popo\User拥有属性:userName, private:, protected:, public:1
  7. 类:popo\User拥有属性:password, private:, protected:, public:1

通过以上例程我们可以看到,通过反射可以获取一个类的所有的成员属性和方法,同时能获取到属性和方法的访问权限。

我们通过调用\ReflectionClass实例的getProperties方法可以返回一个[]\ReflectionProperty,通过\ReflectionProperty我们可以获取属性的所有信息而无需真正的创建实例,包括注释。

我们通过调用\ReflectionClass实例的getMethods方法可以返回一个[]\ReflectionMethod,通过\ReflectionMethod我们可以获取属性的所有信息而无需真正的创建实例,包括注释。

1.2 通过反射调用构造函数实例化对象

  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Jiang Haiqiang
  5. * Date: 2020/7/1
  6. * Time: 9:19 PM
  7. */
  8. namespace popo;
  9. /**
  10. * Class Product
  11. * @package popo
  12. * @datetime 2020/7/1 10:11 PM
  13. * @author roach
  14. * @email jhq0113@163.com
  15. */
  16. class Product
  17. {
  18. /**
  19. * @var string
  20. * @datetime 2020/7/1 10:04 PM
  21. * @author roach
  22. * @email jhq0113@163.com
  23. */
  24. protected $_name;
  25. /**
  26. * @var float
  27. * @datetime 2020/7/1 10:04 PM
  28. * @author roach
  29. * @email jhq0113@163.com
  30. */
  31. protected $_price;
  32. /**
  33. * Product constructor.
  34. * @param string $name
  35. * @param float $price
  36. */
  37. public function __construct($name, $price)
  38. {
  39. $this->_name = $name;
  40. $this->_price = $price;
  41. }
  42. /**
  43. * @return string
  44. * @datetime 2020/7/1 10:05 PM
  45. * @author roach
  46. * @email jhq0113@163.com
  47. */
  48. public function getName()
  49. {
  50. return $this->_name;
  51. }
  52. /**
  53. * @return float
  54. * @datetime 2020/7/1 10:05 PM
  55. * @author roach
  56. * @email jhq0113@163.com
  57. */
  58. public function getPrice()
  59. {
  60. return $this->_price;
  61. }
  62. }
  63. $className = 'popo\Product';
  64. $class = new \ReflectionClass($className);
  65. $product = $class->newInstanceArgs(['PHP进阶教程', 99.99]);
  66. var_dump($product);

以上例程输出:

  1. object(popo\Product)#2 (2) {
  2. ["_name":protected]=>
  3. string(15) "PHP进阶教程"
  4. ["_price":protected]=>
  5. float(99.99)
  6. }

1.3 通用反射创建对象

通过以上案例我们可以通过反射调用构造函数创建并初始化对象了,但是有一点不足,不够通用。

实际开发中习惯定义一个基类,构造函数参数只有一个数组,通过数组参数装配对象,这样反射创建对象的时候通过数组配置就可以创建,如下:

  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Jiang Haiqiang
  5. * Date: 2020/7/1
  6. * Time: 9:19 PM
  7. */
  8. namespace popo;
  9. /**
  10. * Class BaseObject
  11. * @package popo
  12. * @datetime 2020/7/1 10:11 PM
  13. * @author roach
  14. * @email jhq0113@163.com
  15. */
  16. class BaseObject
  17. {
  18. /**
  19. * BaseObject constructor.
  20. * @param array $properties
  21. */
  22. public function __construct($properties = [])
  23. {
  24. foreach ($properties as $property => $value) {
  25. if(property_exists($this, $property)) {
  26. $this->$property = $value;
  27. }
  28. }
  29. }
  30. }
  31. /**
  32. * Class Product
  33. * @package popo
  34. * @datetime 2020/7/1 10:11 PM
  35. * @author roach
  36. * @email jhq0113@163.com
  37. */
  38. class Product extends BaseObject
  39. {
  40. /**
  41. * @var string
  42. * @datetime 2020/7/1 10:04 PM
  43. * @author roach
  44. * @email jhq0113@163.com
  45. */
  46. protected $_name;
  47. /**
  48. * @var float
  49. * @datetime 2020/7/1 10:04 PM
  50. * @author roach
  51. * @email jhq0113@163.com
  52. */
  53. protected $_price;
  54. /**
  55. * @return string
  56. * @datetime 2020/7/1 10:05 PM
  57. * @author roach
  58. * @email jhq0113@163.com
  59. */
  60. public function getName()
  61. {
  62. return $this->_name;
  63. }
  64. /**
  65. * @return float
  66. * @datetime 2020/7/1 10:05 PM
  67. * @author roach
  68. * @email jhq0113@163.com
  69. */
  70. public function getPrice()
  71. {
  72. return $this->_price;
  73. }
  74. }
  75. $className = 'popo\Product';
  76. $class = new \ReflectionClass($className);
  77. $product = $class->newInstanceArgs([
  78. [
  79. '_name' => 'PHP进阶教程',
  80. '_price' => 99.99
  81. ]
  82. ]);
  83. var_dump($product);

以上例程输出:

  1. object(popo\Product)#2 (2) {
  2. ["_name":protected]=>
  3. string(15) "PHP进阶教程"
  4. ["_price":protected]=>
  5. float(99.99)
  6. }

好了,关于反射我们就学习到这里了,反射还有好多强大的功能等着大家去探索,自己动手试试吧。

QQ群:

姜海强的QQ群

公众号:

360tryst公众号