*
反射是操纵面向对象范型中元模型的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. SQLServer中数据库文件的存放方式,文件和文件组

    我们公司近一年来做了一个CRM系统. 遇到一个问题就是:在插入交流记录的时候速度特别慢.(交流记录数据量大) 后来我们经理采用文件组的方法,将客户交流记录这张表提出来就快很多了 这里有一篇关于文件组的 ...

  2. js 对url字符转译全解

    1.js 对url进行字符解码设计到3个方法 escape , encodeURI , encodeURIComponent eg: var url='http://baidu.com';encode ...

  3. MVC入门之.Net语法学习

    本节中主要学习.Net框架性语法.开发者可以使用新语法提高编程的效率以及代码的运行效率:其本质都是“语法糖”,由编译器在编译时转成原始语法. u  自动属性 Auto-Implemented Prop ...

  4. BZOJ 1977 次小生成树(最近公共祖先)

    题意:求一棵树的严格次小生成树,即权值严格大于最小生成树且权值最小的生成树. 先求最小生成树,对于每个不在树中的边,取两点间路径的信息,如果这条边的权值等于路径中的权值最大值,那就删掉路径中的次大值, ...

  5. MultipartEntityBuilder.addTextBody 中文乱码

    // 使用addPart+ StringBody代替addTextBody,解决中文乱码 // builder.addTextBody(entry.getKey(), entry.getValue() ...

  6. Pro Android 4 第六章 构建用户界面以及使用控件(一)

         目前为止,我们已经介绍了android的基础内容,但是还没开始接触用户界面(UI).本章我们将开始探讨用户界面和控件.我们先讨论一下android中UI设计的一般原理,然后我们在介绍一下an ...

  7. 【转】Linux中history历史命令使用方法详解

    原文网址:http://os.51cto.com/art/201205/335040.htm 当你在玩Linux的时候,如果你经常使用命令行来控制你的Linux系统,那么有效地使用命令历史机制将会使效 ...

  8. mybatis和hibernate对比

    Hibernate是一个数据库表和java对象之间完全映射的框架,java开发人员直接对java对象操作,而不对数据库表进行操作: Mybatis是对SQL语句和java对象进行映射,仍需要开发人员编 ...

  9. 认识什么是SEO

    何为SEO? SEO是由英 文Search Engine Optimization缩写而来, 中文意译为“搜索引擎优化”,是指在了解搜索引擎自然排名机制的基础上,对网站进行内部及外部的调整优化,改进网 ...

  10. hihoCoder 1041 国庆出游 (DFS)

    题意: 小Hi和小Ho准备国庆期间去A国旅游.A国的城际交通比较有特色:它共有n座城市(编号1-n):城市之间恰好有n-1条公路相连,形成一个树形公路网.小Hi计划从A国首都(1号城市)出发,自驾遍历 ...