简介

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

class Reflection { }
interface Reflector { }
class ReflectionException extends Exception { }
class ReflectionFunction implements Reflector { }
class ReflectionParameter implements Reflector { }
class ReflectionMethod extends ReflectionFunction { }
class ReflectionClass implements Reflector { }
class ReflectionObject extends ReflectionClass { }
class ReflectionProperty implements Reflector { }
class ReflectionExtension implements Reflector { }

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

ReflectionClass

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

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

  • 常量 Contants
  • 属性 Property Names
  • 方法 Method Names
  • 静态属性 Static Properties
  • 命名空间 Namespace
  • Person类是否为final或者abstract
<?php
namespace app; class Person{
/**
* For the sake of demonstration, we"re setting this private
*/
private $_allowDynamicAttributes = false; /** type=primary_autoincrement */
protected $id = 0; /** type=varchar length=255 null */
protected $name; /** type=text null */
protected $biography; public function getId(){
return $this->id;
} public function setId($v){
$this->id = $v;
} public function getName(){
return $this->name;
} public function setName($v){
$this->name = $v;
} public function getBiography(){
return $this->biography;
} public function setBiography($v){
$this->biography = $v;
}
} //传递类名或对象进来
$class = new \ReflectionClass('app\Person'); //获取属性,不管该属性是否public
$properties = $class->getProperties();
foreach($properties as $property) {
echo $property->getName()."\n";
}
// 输出:
// _allowDynamicAttributes
// id
// name
// biography //默认情况下,ReflectionClass会获取到所有的属性,private 和 protected的也可以。如果只想获取到private属性,就要额外传个参数:
/*
* ReflectionProperty::IS_STATIC
* ReflectionProperty::IS_PUBLIC
* ReflectionProperty::IS_PROTECTED
* ReflectionProperty::IS_PRIVATE
*/ //↓↓ 注意一个|组合: 获得IS_PRIVATE或者IS_PROTECTED的属性
$private_properties = $class->getProperties(\ReflectionProperty::IS_PRIVATE|\ReflectionProperty::IS_PROTECTED); foreach($private_properties as $property) {
//↓↓如果该属性是受保护的属性;
if($property->isProtected()) { // ↓↓ 获取注释
$docblock = $property->getDocComment();
preg_match('/ type\=([a-z_]*) /', $property->getDocComment(), $matches);
echo $matches[1]."\n";
}
}
// Output:
// primary_autoincrement
// varchar
// text $data = array("id" => 1, "name" => "Chris", "biography" => "I am am a PHP developer");
foreach($data as $key => $value) {
if(!$class->hasProperty($key)) {
throw new \Exception($key." is not a valid property");
} if(!$class->hasMethod("get".ucfirst($key))) {
throw new \Exception($key." is missing a getter");
} if(!$class->hasMethod("set".ucfirst($key))) {
throw new \Exception($key." is missing a setter");
} $object = new Person(); // http://php.net/manual/zh/class.reflectionmethod.php
// getMethod 获得一个该方法的reflectionmethod对象,然后使用里面的invoke方法;
$setter = $class->getMethod("set".ucfirst($key));
$ok = $setter->invoke($object, $value); // Get the setter method and invoke it
$getter = $class->getMethod("get".ucfirst($key));
$objValue = $getter->invoke($object); // Now compare
if($value == $objValue) {
echo "Getter or Setter has modified the data.\n";
} else {
echo "Getter and Setter does not modify the data.\n";
}
}

getMethod and invoke

ReflectionClass::getMethod — 获取一个类方法的 ReflectionMethod(可以理解为获得这个类方法的控制权,不管这个类方法是否是public)。

具体的参考:

<?php
class HelloWorld {
private function sayHelloTo($name,$arg1,$arg2) {
return 'Hello ' . $name.' '.$arg1.' '.$arg2;
}
} $obj = new HelloWorld();
// 第一个参数可以是对象,也可以是类
$reflectionMethod = new ReflectionMethod($obj , 'sayHelloTo');
if(!$reflectionMethod -> isPublic()){
$reflectionMethod -> setAccessible(true);
}
/*
* public mixed ReflectionMethod::invoke ( object $object [, mixed $parameter [, mixed $... ]] )
* 1. 获得某个类方法的ReflectionMethod
* 2. $object 该方法所在的类实例的对象,然后第二参数起对号入座到该方法的每个参数;
* 3. 通过invoke就可以执行这个方法了
*/
echo $reflectionMethod->invoke($obj, 'GangGe','How','are you'); //也可以把参数作为数组传进来
echo $reflectionMethod -> invokeArgs($obj,array('GangGe','How','are you'));

getProperty

获得一个 ReflectionProperty 类实例 (同上,获得该属性的控制权) http://cn2.php.net/manual/zh/class.reflectionproperty.php

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

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

<?php
class Foo {
public static $staticProperty = 'foobar';
public $property = 'barfoo';
protected $privateProperty = 'foofoo';
}
$reflectionClass = new ReflectionClass('Foo');
var_dump($reflectionClass->getProperty('staticProperty')->getValue()); //静态属性可以不加参数
var_dump($reflectionClass->getProperty('property')->getValue(new Foo)); //非静态属性必须加传一个类实例
$reflectionProperty = $reflectionClass->getProperty('privateProperty'); //受保护的属性就要通过setAccessible获得其权限
$reflectionProperty->setAccessible(true);
var_dump($reflectionProperty->getValue(new Foo));

Example

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

<?php

if (PHP_SAPI != 'cli') {
exit('Please run it in terminal!');
}
if ($argc < 3) {
exit('At least 2 arguments needed!');
} $controller = ucfirst($argv[1]) . 'Controller';
$action = 'action' . ucfirst($argv[2]); // 检查类是否存在
if (!class_exists($controller)) {
exit("Class $controller does not existed!");
} // 获取类的反射
$reflector = new ReflectionClass($controller);
// 检查方法是否存在
if (!$reflector->hasMethod($action)) {
exit("Method $action does not existed!");
} // 取类的构造函数,返回的是ReflectionMethod对象
$constructor = $reflector->getConstructor(); // 取构造函数的参数,这是一个对象数组
$parameters = $constructor->getParameters(); // 遍历参数
foreach ($parameters as $key => $parameter) {
// 获取参数声明的类
$injector = new ReflectionClass($parameter->getClass()->name);
// 实例化参数声明类并填入参数列表
$parameters[$key] = $injector->newInstance(); //实例化$parameter->getClass()->name类
} // 使用参数列表实例 controller 类
$instance = $reflector->newInstanceArgs($parameters);
// 执行
$instance->$action(); class HelloController
{
private $model; public function __construct(TestModel $model)
{
$this->model = $model;
} public function actionWorld()
{
echo $this->model->property, PHP_EOL;
}
} class TestModel
{
public $property = 'property';
}

TP框架中实现前后控制器

<?php
class BlogAction { public function detail() {
echo 'detail' . "\r\n";
} public function test($year = 2014, $month = 4, $day = 21) {
echo $year . '--' . $month . '--' . $day . "\r\n";
} public function _before_detail() {
echo __FUNCTION__ . "\r\n";
} public function _after_detail() {
echo __FUNCTION__ . "\r\n";
}
} // 执行detail方法
$method = new ReflectionMethod('BlogAction', 'detail');
$instance = new BlogAction(); // 进行权限判断
if ($method->isPublic()) { $class = new ReflectionClass('BlogAction'); // 执行前置方法
if ($class->hasMethod('_before_detail')) {
$beforeMethod = $class->getMethod('_before_detail');
if ($beforeMethod->isPublic()) {
$beforeMethod->invoke($instance);
}
} $method->invoke(new BlogAction); // 执行后置方法
if ($class->hasMethod('_after_detail')) {
$beforeMethod = $class->getMethod('_after_detail');
if ($beforeMethod->isPublic()) {
$beforeMethod->invoke($instance);
}
}
} // 执行带参数的方法
$method = new ReflectionMethod('BlogAction', 'test');
$params = $method->getParameters();
foreach ($params as $param) {
$paramName = $param->getName();
if (isset($_REQUEST[$paramName])) {
$args[] = $_REQUEST[$paramName];
} elseif ($param->isDefaultValueAvailable()) {
$args[] = $param->getDefaultValue();
}
} if (count($args) == $method->getNumberOfParameters()) {
$method->invokeArgs($instance, $args);
} else {
echo 'parameters is wrong!';
}

其他参考

/**
* 执行App控制器
*/
public function execApp() { // 创建action控制器实例
$className = MODULE_NAME . 'Controller';
$namespaceClassName = '\\apps\\' . APP_NAME . '\\controller\\' . $className;
load_class($namespaceClassName, false); if (!class_exists($namespaceClassName)) {
throw new \Exception('Oops! Module not found : ' . $namespaceClassName);
} $controller = new $namespaceClassName(); // 获取当前操作名
$action = ACTION_NAME; // 执行当前操作
//call_user_func(array(&$controller, $action)); // 其实吧,用这个函数足够啦!!!
try {
$methodInfo = new \ReflectionMethod($namespaceClassName, $action);
if ($methodInfo->isPublic() && !$methodInfo->isStatic()) {
$methodInfo->invoke($controller);
} else { // 操作方法不是public类型,抛出异常
throw new \ReflectionException();
}
} catch (\ReflectionException $e) {
// 方法调用发生异常后,引导到__call方法处理
$methodInfo = new \ReflectionMethod($namespaceClassName, '__call');
$methodInfo->invokeArgs($controller, array($action, ''));
}
return;
}

PHP 反射机制Reflection的更多相关文章

  1. Java - 反射机制(Reflection)

    Java - 反射机制(Reflection)     > Reflection 是被视为 动态语言的关键,反射机制允许程序在执行期借助于 Reflection API 取得任何类的       ...

  2. java反射机制--reflection

    反射,reflection,听其名就像照镜子一样,可以看见自己也可以看见别人的每一部分.在java语言中这是一个很重要的特性.下面是来自sun公司官网关于反射的介绍:    Reflection is ...

  3. JAVA动态性之一一反射机制reflection

    package com.bjsxt.reflection.test.bean; public class User { private int id; private int age; private ...

  4. Java反射机制Reflection

    Java反射机制 1 .class文件 2 Class类 3 Class类与反射机制 4 Java反射机制的类库支持及简介 5 反射机制的定义与应用 6 反射机制Demo Java反射机制demo(一 ...

  5. Java中的反射机制Reflection

    目录 什么是反射? 获取.class字节码文件对象 获取该.class字节码文件对象的详细信息 通过反射机制执行函数 反射链 反射机制是java的一个非常重要的机制,一些著名的应用框架都使用了此机制, ...

  6. as与c++的反射机制对比

    所谓反射机制(Reflection),简单来说,就是可以根据class的名称获取这个class以及其对应的实例.具体来说, 指的是我们可以于运行时加载.探知.使用编译期间完全未知的classes.换句 ...

  7. Java动态性之--反射机制

    1. 动态语言 程序运行时,可以改变结构或变量类型.典型的语言: Python.ruby.javascript等 如下javascript代码 function test(){ var s = &qu ...

  8. Java 反射机制(一)

    阅读<Core Java Volume I --- Fundamentals>反射部分,总觉得许多概念艰涩难懂.模棱两可.我想造成这个结果的主要原因可能是Cay S. Horstmann和 ...

  9. Java进阶之reflection(反射机制)——反射概念与基础

    反射机制是Java动态性之一,而说到动态性首先得了解动态语言.那么何为动态语言? 一.动态语言 动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化.比如常见 ...

随机推荐

  1. 68.vivado与modelsim的关联以及器件库编译

    vivado软件中也自带仿真工具,但用了几天之后感觉仿真速度有点慢,至少比modelsim慢挺多的.而modelsim是我比较熟悉的一款仿真软件,固然选它作为设计功能的验证.为了将vivado和mod ...

  2. Qt 延时

    第一部分: 关于sleep函数,我们先来看一下他的作用:sleep函数是使调用sleep函数的线程休眠,线程主动放弃时间片.当经过指定的时间间隔后,再启动线程,继续执行代码.sleep函数并不能起到定 ...

  3. 11.5Daily Scrum

    人员 任务分配完成情况 明天任务分配 王皓南 实现网页上视频浏览的功能.研究相关的代码和功能.811 数据库测试 申开亮 实现网页上视频浏览的功能.研究相关的代码和功能.812 实现视频浏览的功能 王 ...

  4. background-clip

    background-clip 用来将背景图片做适当的裁剪以适应实际需要. 语法: background-clip : border-box | padding-box | content-box | ...

  5. codeforces399D

    题目大意:ainta刷一面n*n的二维墙.墙一开始可能有一些块被刷了.他终止刷墙的前提是墙的每一行每一列都至少有一块被刷.他每一次从n*n的墙随机选择一块,如果这一块没有被刷就刷,刷了就无视,刷墙不计 ...

  6. HDU 5800 To My Girlfriend 背包

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5800 To My Girlfriend Time Limit: 2000/2000 MS (Java ...

  7. HDU 3555 Bomb 数位dp

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3555 Bomb Time Limit: 2000/1000 MS (Java/Others) Mem ...

  8. WPF使用RoutedCommand自定义命令

    主要代码如下所示: /// <summary> /// 声明并定义命令. /// </summary> RoutedCommand ClearCommand = new Rou ...

  9. html之colspan && rowspan讲解

    1.colspan && rowspan均在td标签中使用 2.每个单元格大小一致的前提 <table border="1" bordercolor=&quo ...

  10. Mac OS 上设置 JAVA_HOME

    Mac OS 上设置 JAVA_HOME 原文链接:http://han.guokai.blog.163.com/blog/static/136718271201301183938165/ 由于需要, ...