*
反射是操纵面向对象范型中元模型的API,其功能十分强大,可帮助我们构建复杂,可扩展的应用。其用途如:自动加载插件,自动生成文档,甚至可用来扩充PHP语言。php反射api由若干类组成,可帮助我们用来访问程序的元数据或者同相关的注释交互。借助反射我们可以获取诸如类实现了那些方法,创建一个类的实例(不同于用new创建),调用一个方法(也不同于常规调用),传递参数,动态调用类的静态方法。
*
**
反射api是php内建的oop技术扩展,包括一些类,异常和接口,综合使用他们可用来帮助我们分析其它类,接口,方法,属性,方法和扩展。这些oop扩展被称为反射,位于php源码/ext/reflection目录下。
可以使用反射api自省反射api本身(这可能就是反射最初的意思,自己“看”自己):

<?php
Reflection::export(new ReflectionExtension('reflection'));
?>

几乎所有的反射api都实现了reflector接口,所有实现该接口的类都有一个export方法,该方法打印出参数对象的相关信息。
使用get_declared_classes()获取所有php内置类,get_declared_interfaces();
get_defined_functions();get_defined_vars(); get_defined_constants();可获取php接口,方法,变量,常量信息。

反射初探:

<?php
//定义一个自定义类
class MyTestClass{ public function testFunc($para0='defaultValue0'){ }
}
//接下来反射它
foreach(get_declared_classes() as $class){
//实例化一个反射类
$reflectionClass = new ReflectionClass($class);
//如果该类是自定义类
if($reflectionClass->isUserDefined()){
//导出该类信息
Reflection::export($reflectionClass);
}
}
?>

以上片段实例如何查看自定义类的基本信息。
描述数据的数据被称为元数据,用反射获取的信息就是元数据信息,这些信息用来描述类,接口方法等等。(元---》就是原始之意,比如元模型就是描述模型的模型,比如UML元模型就是描述UML结构的模型),元数据进一步可分为硬元数据(hard matadata)和软元数据(soft metadata),前者由编译代码导出,如类名字,方法,参数等。
后者是人为加入的数据,如phpDoc块,php中的属性等。

现在商业软件很多都是基于插件架构的,比如eclipse,和visual studio,netbeans等一些著名IDE都是基于插件的GUI应用。第三方或本方开发插件时,必须导入定义好的相关接口,然后实现这些接口,最后把实现的包放在指定目录下,宿主应用程序在启动时自动检测所有的插件实现,并加载它们。如果我们自己想实现这样的架构也是可能的。

<?php
//先定义UI接口
interface IPlugin {
//获取插件的名字
public static function getName();
//要显示的菜单项
function getMenuItems();
//要显示的文章
function getArticles();
//要显示的导航栏
function getSideBars();
}
//一下是对插件接口的实现
class SomePlugin implements IPlugin {
public function getMenuItems() {
//返回菜单项
return null;
}
public function getArticles() {
//返回我们的文章
return null;
}
public function getSideBars() {
//我们有一个导航栏
return array('SideBarItem');
}
//返回插件名
public static function getName(){
return "SomePlugin";
}
}
?>

php中也有使用插件的解决方案,不像eclipse。
使用我们的插件:1.先使用get_declared_classes()获取所有已加载类。2.遍历所有类,判断其是否实现了我们自定义的插件接口IPlugin。3.获取所有的插件实现。4.在宿主应用中与插件交互

下面这个方法帮助我们找到实现了插件接口的所有类:

function findPlugins() {
$plugins = array();
foreach(get_declared_classes() as $class) {
$reflectionClass = new ReflectionClass($class);
//判断一个类是否实现了IPlugin接口
if($reflectionClass->implementsInterface('IPlugin')) {
$plugins[] = $reflectionClass;
}
}
return $plugins;
}

注意到所有的插件实现是作为反射类实例返回的,而不是类名本身,或是类的实例。因为如果使用反射来调用方法还需要一些条件判断。
判断一个类是否实现了某个方法使用反射类的hasMethod()方法。
接下来我们把所有的插件菜单项放在一个菜单上。

function integratePlugInMenus() {
$menu = array();
//遍历所有的插件实现
foreach(findPlugins() as $plugin) {
//判断插件是否实现了getMenuItems方法
if($plugin->hasMethod('getMenuItems')) {
/*实例化一个方法实例(注意当你将类和方法看成概念时,它们就可以有实例,就像“人”这个概念一样),该方法返回的是ReflectionMethod的实例*/
$reflectionMethod = $plugin->getMethod('getMenuItems');
//如果方法是静态的
if($reflectionMethod->isStatic()) {
//调用静态方法,注意参数是null而不是一个反射类实例
$items = $reflectionMethod->invoke(null);
} else {
//如果方法不是静态的,则先实例化一个反射类实例所代表的类的实例。
$pluginInstance = $plugin->newInstance();
//使用反射api来调用一个方法,参数是通过反射实例化的对象引用
$items = $reflectionMethod->invoke($pluginInstance);
}
//合并所有的插件菜单项为一个菜单。
$menu = array_merge($menu, $items);
}
}
return $menu;
}

这里主要用到的反射方法实例的方法调用:
public mixed invoke(stdclass object, mixed args=null);
请一定搞清楚我们常规方法的调用是这种形式:$objRef->someMethod($argList...);
因为使用了反射,这时你在想调用一个方法时形式变为:
$reflectionMethodRef->invoke($reflectionClassRef,$argList...);
如果使用反射调用方法,我们必须实例化一个反射方法的实例,如果是实例方法还要有一个实例的引用,可能还需传递必要的参数。当调用一个静态方法时,显式传入null作为第一参数。
对插件类实现的其他方法有类似的处理逻辑,这里不再敷述。
以下是我的一个简单测试:

<?php
/**
* 定义一个插件接口
* */
interface IPlugIn
{
/**
* getSidebars()
*
* @return 返回侧导航栏
*/
public function getSidebars();
/**
* GetName()
*
* @return 返回类名
*/
public static function GetName();
}
/*下面是对插件的实现,其实应该放在不同的文件中,甚至是不同的包中*/
class MyPlugIn implements IPlugIn
{
public function getSidebars()
{
//构造自己的导航栏
$sideBars = '<div><ul >
<li><a href="">m1</a>
</li>
<li><a href="">m2</a>
</li>
</ul>
</div>';
return $sideBars;
}
public static function GetName()
{
return 'MyPlugIn';
}
}
//第二个插件实现;
class MyPlugIn2 implements IPlugIn
{
public function getSidebars()
{
//构造自己的导航栏
$sideBars = '<div><ul >
<li><a href="">mm1</a>
</li>
<li><a href="">mm2</a>
</li>
</ul>
</div>';
return $sideBars;
}
public static function GetName()
{
return 'MyPlugIn2';
}
}
//在宿主程序中使用插件
class HostApp
{
public function initAll()
{
// 初始化各个部分
echo "yiqing95.";
$this->renderAll();
}
//渲染GUI格部分
function renderAll(){
$rsltSidebars="<table>";
foreach($this->integrateSidebarsOfPlugin() as $sidebarItem){
$rsltSidebars.="<tr><td>$sidebarItem</td></tr>";
}
$rsltSidebars.="</table>"; echo $rsltSidebars;
}
/*加载所有的插件实现:*/
protected function findPlugins()
{
$plugins = array();
foreach (get_declared_classes() as $class) {
$reflectionClass = new ReflectionClass($class);
if ($reflectionClass->implementsInterface('IPlugin')) {
$plugins[] = $reflectionClass;
}
}
return $plugins;
}
/**加载组装所有插件实现***/
protected function integrateSidebarsOfPlugin()
{
$sidebars = array();
foreach ($this->findPlugins() as $plugin) {
if ($plugin->hasMethod('getSidebars')) {
$reflectionMethod = $plugin->getMethod('getSidebars');
if ($reflectionMethod->isStatic()) {
$items = $reflectionMethod->invoke(null);
} else {
$pluginInstance = $plugin->newInstance();
$items = $reflectionMethod->invoke($pluginInstance) ;
}
}
//$sidebars = array_merge($sidebars, $items);
$sidebars[]=$items;
}
return $sidebars;
} }
//运行程序:
$entryClass =new HostApp();
$entryClass->initAll();
?>

****
××××
$reflectionClass = new ReflectionClass("IPlugIn");
echo $reflectionClass-> getDocComment();
这段代码可以帮助我们获取类的文档注释,一旦我们获取了类的注释内容我们就可以扩展我们的类功能,比如先获取注释,然后分析注释使用docblock tokenizer 『pecl扩展』,或使用自带的Tokenizer类又或者使用正则表达式,字符串函数来解析注释文档,你可以在注释中加入任何东西,包括指令,在使用反射调用前可判断这些通过注释传递的指令或数据:
<?php
//"分析相关的注释数据"
analyse($reflectionClass-> getDocComment());//analyse是自己定义的!!!
//根据分析的结果来执行方法,或者传递参数等
if(xxxx){
$reflectionMethod->invoke($pluginInstance) ;
}
?>
因为注释毕竟是字符串,可以使用任何字符串解析技术,提取有用的信息,再根据这些信息来调用方法,就是说程序的逻辑不光可由方法实现决定,还可能由注释决定(前提是你使用了反射,注释格式严格有要求)。

反射api和其他类一样可被继承扩展,所以我们可以为这些api添加自己的功能。结合自定义注释标记。就是以@开头的东东,标注(Java中称为annotation),.net中称为属性attribute(或称为特性)。然后扩展Reflection类,就可以实现强大的扩展功能了。
值得一提的是工厂方法设计模式(GOF之一),也常使用反射来实例化对象,下面是示例性质的伪码:

Class XXXFactory{
function getInstance($className){
$reflectionClass =new ReflectionClass($className);
return $reflectionClass->newInstance();
}
//使用接口的那个类实现,可能来自配置文件
function getInstance(){
$pathOfConfig = "xxx/xx/XXXImplement.php";
$className= Config->getItem($pathOfClass,'SomeClassName');
return $this->getInstance($className);
}
}
转载:http://blog.csdn.net/siren0203/article/details/5994571

PHP API反射实例的更多相关文章

  1. Google Map JavaScript API V3 实例大全

    Google Map JavaScript API V3 实例大全 基础知识 简单的例子 地理位置 语言 位置 坐标 简单的投影 事件 简单事件 关闭事件 多次添加事件 事件属性 控制 php禁用ui ...

  2. [Python][flask][flask-login]关于flask-login中各种API使用实例

    本篇博文跟上一篇[Python][flask][flask-wtf]关于flask-wtf中API使用实例教程有莫大的关系. 简介:Flask-Login 为 Flask 提供了用户会话管理.它处理了 ...

  3. [Python][flask][flask-wtf]关于flask-wtf中API使用实例教程

    简介:简单的集成flask,WTForms,包括跨站请求伪造(CSRF),文件上传和验证码. 一.安装(Install) 此文仍然是Windows操作系统下的教程,但是和linux操作系统下的运行环境 ...

  4. The MySQL C API 编程实例

    在网上找了一些MYSQL C API编程的文章,看了后认为还是写的不够充分,依据自己经验写了这篇<The MySQL C API 编程实例>,希望对须要调用到MYSQL的C的API的朋友有 ...

  5. 腾讯QQAndroid API调用实例(QQ分享无需登录)

    腾讯QQAndroid API调用实例(QQ分享无需登录)   主要分为两个步骤: 配置Androidmanifest.xml 修改activity里边代码 具体修改如下:   1.Activity代 ...

  6. 微信WeixinJSBridge API使用实例

    http://www.jb51.net/article/66642.htm 这篇文章主要介绍了微信WeixinJSBridge API使用实例,本文直接给出HTML代码,代码中包含了很多实用功能,如图 ...

  7. javamail模拟邮箱功能--邮件回复-中级实战篇【邮件回复方法】(javamail API电子邮件实例)

    引言: JavaMai下载地址l jar包:http://java.sun.com/products/javamail/downloads/index.html 此篇是紧随上篇文章而封装出来的,阅读本 ...

  8. javamail模拟邮箱功能获取邮件内容-中级实战篇【内容|附件下载方法】(javamail API电子邮件实例)

    引言: JavaMail jar包下载地址:http://java.sun.com/products/javamail/downloads/index.html 此篇是紧随上篇文章而封装出来的,阅读本 ...

  9. 关于操作 ASP.NET Web API的实例

    WCF的野心造成了它的庞大复杂,HTTP的单纯造就了它的简单优美.为了实现分布式Web应用,我们不得不将两者凑合在一起 —— WCF服务以HTTP绑定宿主于IIS. 于是有了让人晕头转向的配置.让人郁 ...

随机推荐

  1. hdfs的实现机制和文件系统概念

    1.HDFS的诞生背景: 数据量太大,在一个结点(机器)存不下.所以需要分布式存储,HDFS就是hadoop的分布式文件系统,来存储分布式数据. 2.共享文件系统也是一种分布式存储但有缺点:1.并发差 ...

  2. JS window对象的top、parent、opener含义

    .top 该变更永远指分割窗口最高层次的浏览器窗口.如果计划从分割窗口的最高层次开始执行命令,就可以用top变量. .opener opener用于在window.open的页面引用执行该window ...

  3. (2010-8-31) awk内存泄漏以及缓慢的正则表达式计算速度

    AWK内存泄露: 这几天本来就很郁闷,遇到搭建在hadoop平台上的hive平台有很多问题.写个好好的sql语句,总是说这个错误那个错误.然后,今天遇到一个更加郁闷的问题,居然分析淘宝数据的awk都运 ...

  4. 在UC浏览器上很炫的一个效果

    效果简述: 这个效果将会强行去除UC浏览器的网址输入框和底部的菜单栏,这样网页将会占据整个手机界面.感觉看起来很像是一个APP. 浏览器界面右下角将会出现一个向上的标志,用来唤出简单的菜单栏. (界面 ...

  5. yii CMenu的配置(导航栏)

    给主键的li 和a标签添加元素$this->myMenu = array( 'id'=>'myMenu', 'items'=>array( array( 'label'=>'H ...

  6. PowerShell3.0中,所有的命令

    Get-Command * >> cmd.txt CommandType Name ModuleName ----------- ---- ---------- Alias % -> ...

  7. [置顶] Android学习系列-把文件保存到SD卡上面(6)

    Android学习系列-把文件保存到SD卡上面(5) 一般多媒体文件,大文件需要保存到SD卡中.关键点如下: 1,SD卡保存目录:mnt/sdcard,一般采用Environment.getExter ...

  8. poj 3190 Stall Reservations 贪心 + 优先队列

    题意:给定N头奶牛,每头牛有固定的时间[a,b]让农夫去挤牛奶,农夫也只能在对应区间对指定奶牛进行挤奶, 求最少要多少个奶牛棚,使得在每个棚内的奶牛的挤奶时间不冲突. 思路:1.第一个想法就是贪心,对 ...

  9. boost.asio系列——Timer

    同步Timer asio中提供的timer名为deadline_timer,它提供了超时计时的功能.首先以一个最简单的同步Timer为例来演示如何使用它. #include<iostream&g ...

  10. iOS多线程及其感悟

    感觉每天都是匆匆忙忙的,每天似乎都是时间不够用一样,可是等真的想要动手敲代码的时候才发现,原来还有好多好多的知识点不是太熟练,所以,人不可以一直感觉自我良好, 有时间就是那种自我感觉良好的心态毁了自己 ...