1. 什么是反射
反射在百度百科里面的解释是“反射是一种计算机处理方式。有程序可以访问、检测和修改它本身状态或行为的这种能力。能提供封装程序集、类型的对象”。
PHP提供了对类、函数、方法以及拓展进行反射的能力。通过反射我们可以在动态运行程序时候,获取类的名字,参数,方法、注释等信息以及动态调用对象方法,通过这些可以实现自动生成文档、自动注入依赖,插件管理。
反射的优点:
- 反射提高了程序的灵活性和扩展性。
- 降低耦合性,提高自适应能力。
- 它允许程序创建和控制任何类的对象,无需提前硬编码目标类。
2. PHP反射API
PHP虽然提供了诸如class_exists(),method_exists(),get_class(),get_calss_methods()等Introspection Functions
,来获取类/对象信息,但获取的信息有限,而反射API提供了丰富的功能来获取对象信息,以及操作对象
PHP反射API常用部分:
类 |
说明 |
ReflectionClass |
反射类信息 |
ReflectionMethod |
反射类方法信息 |
ReflectionFunction |
反射函数信息 |
ReflectionExtension |
PHP拓展信息 |
ReflectionException |
反射异常信息 |
2.1. 反射类/对象
反射类或者对象的信息使用ReflectionClass这个类,常用的操作如下:
2.1.1. 实例化对象
1 2 3 4 5 6 7 8 9 10 11 12
| $class = new ReflectionClass('mysqli'); $args = ['localhost', 'root', '', 'forge']; $mysqli = $class->newInstanceArgs($args); if ($mysqli->connect_error) { die('Connect error:' . $mysqli->connect_errno . ' ' . $mysqli->connect_error); }
$result = $mysqli->query('show tables', MYSQLI_USE_RESULT); while ($row = $result->fetch_row()) { printf("%s\n", $row[0]); }
|
2.1.2. 获取常量
1 2 3 4
| $class = new ReflectionClass('mysqli'); $constants = $class->getConstants(); $constant = $class->getConstant('CONSTANT_NAME'); $isDefined = $class->hasConstant('CONSTANT_NAME');
|
2.1.3. 获取属性
1 2 3 4 5 6 7 8
| $props = $class->getProperties(ReflectionProperty::IS_STATIC | ReflectionProperty::IS_PUBLIC); foreach ($props as $prop) { echo $prop->getName() . '=' . $prop->getValue($mysqli) . "\n"; }
$prop = $class->getProperty('host_info'); echo $prop->getName() . '=' . $prop->getValue($mysqli);
|
getProperties方法部分参数如下:
- ReflectionProperty::IS_STATIC
- ReflectionProperty::IS_PUBLIC
- ReflectionProperty::IS_PROTECTED
- ReflectionProperty::IS_PRIVATE
2.1.4. 获取方法
1 2 3 4 5 6 7 8
| $methods = $class->getMethods(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_STATIC); foreach ($methods as $method) { echo $method->getName() . "\n"; }
$method = $class->getMethod('stat'); print_r($method->invoke($mysqli));
|
getMethods部分可用参数如下:
- ReflectionMethod::IS_STATIC
- ReflectionMethod::IS_PUBLIC
- ReflectionMethod::IS_PROTECTED
- ReflectionMethod::IS_FINAL
2.1.5. 获取类文件名称、行数等信息
1 2 3 4 5 6 7 8 9 10
| class Cat { private $name = 'tom'; } $class = new ReflectionClass('Cat'); echo $class->getFileName(); $file = file('./cat.php'); $offset = $class->getStartLine() - 1; $length = $class->getEndLine() - $class->getStartLine() + 1); echo implode('', array_slice($file, $offset, $length);
|
- getFileName 获取类被定义的文件的文件名,内置或拓展类返回false
- getStartLine 获取类定义文件起始行号,内置或拓展类返回false
- getEndLine 获取类定义的结束行号,内置或拓展类返回false
2.2. 反射类方法
2.2.1. 判断方法可见性
- isPublic
- isPrivate
- isProtected
1 2
| $method = new ReflectionMethod('mysqli', 'stat'); $isPublic = $method->isPublic();
|
2.2.2. 获取方法参数
1 2 3 4 5 6 7 8 9 10 11 12 13
| $method = new ReflectionMethod('mysqli', 'query'); echo $method->getNumberOfParameters(); echo $method->getNumberOfRequiredParameters();
$params = $method->getParameters(); foreach ($params as $param) { $name = $param->getName(); if ($param->isDefaultValueAvailable()) { echo "$name=" . $param->getDefaultValue(); } else { echo "$name don't have default value"; } }
|
3. 反射应用示例
3.1. 控制器层的方法动态调用
这里只是简单演示,所以路由只是简单处理的,控制器文件直接写在一起了,而不是独立文件并自动载入,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
define('CONTROLLER', ucfirst(isset($_REQUEST['m']) ? $_REQUEST['m'] : 'Home') . 'Controller'); define('ACTION', isset($_REQUEST['a']) ? $_REQUEST['a'] : 'index');
class HomeController { public function __construct() { echo __METHOD__ . "\n"; }
public function index() { echo __METHOD__ . "\n"; }
public function __beforeHello() { echo __METHOD__ . "\n"; }
public function __afterHello() { echo __METHOD__ . "\n"; }
public function hello() { echo __METHOD__ . "\n"; echo "==>hello world!\n"; } }
$method = new ReflectionMethod(CONTROLLER, ACTION); if ($method->isPublic()) { $class = new ReflectionClass(CONTROLLER); $classObj = $class->newInstance(); if ($class->hasMethod('__before' . ucfirst(ACTION))) { $beforeMethod = new ReflectionMethod(CONTROLLER, '__before' . ucfirst(ACTION)); if ($beforeMethod->isPublic()) { $beforeMethod->invoke($classObj); } } $method->invoke($classObj); if ($class->hasMethod('__after' . ucfirst(ACTION))) { $afterMethod = new ReflectionMethod(CONTROLLER, '__after' . ucfirst(ACTION)); if ($afterMethod->isPublic()) { $afterMethod->invoke($classObj); } } } else { throw new ReflectionException("Action is not public"); }
|
3.2. 类属性根据配置自动设置
根据配置文件动态set类属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class Upload { ... public function initialize(array $config = array(), $reset = TRUE) { $reflection = new ReflectionClass($this); ... foreach ($config as $key => &$value) { if ($key[0] !== '_' && $reflection->hasProperty($key)) { if ($reflection->hasMethod('set_'.$key)) { $this->{'set_'.$key}($value); } else { $this->$key = $value; } } } } ... }
$uploader = new Upload(); $config = include('./upload.config.php'); $uploader->initialize($config); ....
|
3.3. 插件管理