php 反射类简介

反射是操纵面向对象范型中元模型的API,其功能十分强大,可帮助我们构建复杂,可扩展的应用。其用途如:PHP
强烈推介IDEA2020.2破解激活,IntelliJ IDEA 注册码,2020.2 IDEA 激活码

反射是操纵面向对象范型中元模型的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);

}

}

*****

本文来源21aspnet,由javajgs_com转载发布,观点不代表Java架构师必看的立场,转载请标明来源出处:https://javajgs.com/archives/2537

发表评论