反射
反射是一种动态获取类成员属性和成员方法,并且可以动态创建对象进行调用的一种技术。
Java
语言有一个特别著名的框架叫Spring
,几乎任何Java
程序员对这个框架非常熟悉,这个框架早期最核心的思想就是DI(依赖注入),底层的技术实现核心就是依赖反射
技术。
PHP
本身其实不通过反射也能动态创建对象,通过new
关键字就可以创建对象,如下例程:
<?php
namespace popo;
class User
{
/**
* @var string
* @datetime 2020/7/1 9:20 PM
* @author roach
* @email jhq0113@163.com
*/
public $userName;
/**
* @var string
* @datetime 2020/7/1 9:20 PM
* @author roach
* @email jhq0113@163.com
*/
public $password;
}
$class = 'popo\User';
$user = new $class();
$user->userName = 'reflect';
var_dump($user);
以上例程输出:
object(popo\User)#1 (2) {
["userName"]=>
string(7) "reflect"
["password"]=>
NULL
}
1. 反射扩展
PHP提供强大的反射库,绝大多数的开源框架都使用了反射扩展实现了DI,下面来介绍一下上面例程如何用反射去实现。
<?php
/**
* Created by PhpStorm.
* User: Jiang Haiqiang
* Date: 2020/7/1
* Time: 9:19 PM
*/
namespace popo;
class User
{
/**
* @var string
* @datetime 2020/7/1 9:20 PM
* @author roach
* @email jhq0113@163.com
*/
public $userName;
/**
* @var string
* @datetime 2020/7/1 9:20 PM
* @author roach
* @email jhq0113@163.com
*/
public $password;
}
$class = new \ReflectionClass('popo\User');
$user = $class->newInstance();
$user->userName = 'reflect';
var_dump($user);
以上例程输出:
object(popo\User)#2 (2) {
["userName"]=>
string(7) "reflect"
["password"]=>
NULL
}
从以上例程可以看出,PHP的反射非常简单,但是功能却十分强大。
1.1 反射获取类所有成员方法和属性
<?php
/**
* Created by PhpStorm.
* User: Jiang Haiqiang
* Date: 2020/7/1
* Time: 9:19 PM
*/
namespace popo;
class User
{
/**
* @var string
* @datetime 2020/7/1 9:34 PM
* @author roach
* @email jhq0113@163.com
*/
private $_salt = 'sdfas#$!@DDFSDF';
/**
* @var string
* @datetime 2020/7/1 9:34 PM
* @author roach
* @email jhq0113@163.com
*/
protected $_token;
/**
* @var string
* @datetime 2020/7/1 9:20 PM
* @author roach
* @email jhq0113@163.com
*/
public $userName;
/**
* @var string
* @datetime 2020/7/1 9:20 PM
* @author roach
* @email jhq0113@163.com
*/
public $password;
/**
* @param string $userId
* @return string
* @datetime 2020/7/1 9:35 PM
* @author roach
* @email jhq0113@163.com
*/
private function _createToken($userId)
{
return hash_hmac('sha1', $this->_salt.$userId, $this->_salt);
}
/**
* @param string $userId
* @param string $token
* @return bool
* @datetime 2020/7/1 9:36 PM
* @author roach
* @email jhq0113@163.com
*/
protected function _checkToken($userId, $token)
{
return $this->_createToken($userId) === $token;
}
/**
* @return string
* @datetime 2020/7/1 9:37 PM
* @author roach
* @email jhq0113@163.com
*/
public function getToken()
{
return $this->_token;
}
}
$className = 'popo\User';
$class = new \ReflectionClass($className);
$properties = $class->getProperties();
foreach ($properties as $property) {
/**
* @var \ReflectionProperty $property
*/
echo '类:'.$className.'拥有属性:'.$property->name
.', private:'.$property->isPrivate()
.', protected:'.$property->isProtected()
.', public:'.$property->isPublic().PHP_EOL;
}
$methods = $class->getMethods();
foreach ($methods as $method) {
/**
* @var \ReflectionMethod $method
*/
echo '类:'.$className.'拥有方法:'.$method->name
.', private:'.$method->isPrivate()
.', protected:'.$method->isProtected()
.', public:'.$method->isPublic().PHP_EOL;
}
以上例程输出:
类:popo\User拥有方法:_createToken, private:1, protected:, public:
类:popo\User拥有方法:_checkToken, private:, protected:1, public:
类:popo\User拥有法:getToken, private:, protected:, public:1
类:popo\User拥有属性:_salt, private:1, protected:, public:
类:popo\User拥有属性:_token, private:, protected:1, public:
类:popo\User拥有属性:userName, private:, protected:, public:1
类:popo\User拥有属性:password, private:, protected:, public:1
通过以上例程我们可以看到,通过反射可以获取一个类的所有的成员属性和方法,同时能获取到属性和方法的访问权限。
我们通过调用
\ReflectionClass
实例的getProperties
方法可以返回一个[]\ReflectionProperty
,通过\ReflectionProperty
我们可以获取属性的所有信息而无需真正的创建实例,包括注释。
我们通过调用
\ReflectionClass
实例的getMethods
方法可以返回一个[]\ReflectionMethod
,通过\ReflectionMethod
我们可以获取属性的所有信息而无需真正的创建实例,包括注释。
1.2 通过反射调用构造函数实例化对象
<?php
/**
* Created by PhpStorm.
* User: Jiang Haiqiang
* Date: 2020/7/1
* Time: 9:19 PM
*/
namespace popo;
/**
* Class Product
* @package popo
* @datetime 2020/7/1 10:11 PM
* @author roach
* @email jhq0113@163.com
*/
class Product
{
/**
* @var string
* @datetime 2020/7/1 10:04 PM
* @author roach
* @email jhq0113@163.com
*/
protected $_name;
/**
* @var float
* @datetime 2020/7/1 10:04 PM
* @author roach
* @email jhq0113@163.com
*/
protected $_price;
/**
* Product constructor.
* @param string $name
* @param float $price
*/
public function __construct($name, $price)
{
$this->_name = $name;
$this->_price = $price;
}
/**
* @return string
* @datetime 2020/7/1 10:05 PM
* @author roach
* @email jhq0113@163.com
*/
public function getName()
{
return $this->_name;
}
/**
* @return float
* @datetime 2020/7/1 10:05 PM
* @author roach
* @email jhq0113@163.com
*/
public function getPrice()
{
return $this->_price;
}
}
$className = 'popo\Product';
$class = new \ReflectionClass($className);
$product = $class->newInstanceArgs(['PHP进阶教程', 99.99]);
var_dump($product);
以上例程输出:
object(popo\Product)#2 (2) {
["_name":protected]=>
string(15) "PHP进阶教程"
["_price":protected]=>
float(99.99)
}
1.3 通用反射创建对象
通过以上案例我们可以通过反射调用构造函数创建并初始化对象了,但是有一点不足,不够通用。
实际开发中习惯定义一个基类,构造函数参数只有一个数组,通过数组参数装配对象,这样反射创建对象的时候通过数组配置就可以创建,如下:
<?php
/**
* Created by PhpStorm.
* User: Jiang Haiqiang
* Date: 2020/7/1
* Time: 9:19 PM
*/
namespace popo;
/**
* Class BaseObject
* @package popo
* @datetime 2020/7/1 10:11 PM
* @author roach
* @email jhq0113@163.com
*/
class BaseObject
{
/**
* BaseObject constructor.
* @param array $properties
*/
public function __construct($properties = [])
{
foreach ($properties as $property => $value) {
if(property_exists($this, $property)) {
$this->$property = $value;
}
}
}
}
/**
* Class Product
* @package popo
* @datetime 2020/7/1 10:11 PM
* @author roach
* @email jhq0113@163.com
*/
class Product extends BaseObject
{
/**
* @var string
* @datetime 2020/7/1 10:04 PM
* @author roach
* @email jhq0113@163.com
*/
protected $_name;
/**
* @var float
* @datetime 2020/7/1 10:04 PM
* @author roach
* @email jhq0113@163.com
*/
protected $_price;
/**
* @return string
* @datetime 2020/7/1 10:05 PM
* @author roach
* @email jhq0113@163.com
*/
public function getName()
{
return $this->_name;
}
/**
* @return float
* @datetime 2020/7/1 10:05 PM
* @author roach
* @email jhq0113@163.com
*/
public function getPrice()
{
return $this->_price;
}
}
$className = 'popo\Product';
$class = new \ReflectionClass($className);
$product = $class->newInstanceArgs([
[
'_name' => 'PHP进阶教程',
'_price' => 99.99
]
]);
var_dump($product);
以上例程输出:
object(popo\Product)#2 (2) {
["_name":protected]=>
string(15) "PHP进阶教程"
["_price":protected]=>
float(99.99)
}
好了,关于反射我们就学习到这里了,反射还有好多强大的功能等着大家去探索,自己动手试试吧。