深入PHP面向对象、模式与实践——对象工具(3)
反射API反射API由一系列可以分析属性、方法和类的内置类组成。入门反射API不仅仅被用于检查类。例如,ReflectionFunction类提供了关于给定函数的信息,ReflectionExtension类可以查看编译到PHP语言中的扩展。利用反射API的这些类,可以在运行时访问对象、函数和脚本中的扩展的信息。反射的另一用途是根据命名规则创建一个调用模板类中方法的框架。开始
反射API
反射API由一系列可以分析属性、方法和类的内置类组成。
- 入门
反射API不仅仅被用于检查类。例如,ReflectionFunction类提供了关于给定函数的信息,ReflectionExtension类可以查看编译到PHP语言中的扩展。利用反射API的这些类,可以在运行时访问对象、函数和脚本中的扩展的信息。反射的另一用途是根据命名规则创建一个调用模板类中方法的框架。
- 开始行动
ReflectionClass提供揭示给定类所有信息的方法,无论这个类是用户定义的还是PHP自带的内置类。
$prod_class = new ReflectionClass('CdProduct');
Reflection::export($prod_class);
该函数与调试函数var_dump()相比较,var_dump()函数是汇总数据的通用工具,但使用前必须先实例化一个对象,而且无法提供想Reflection::export()提供的那么多细节。
$cd = new CdProduct("cd1", "bob", "bobbleson", 4, 50);
var_dump($cd);
var_dump()和它的姊妹函数print_r()是检测PHP代码中数据的利器,但对于类和函数,反射API提供了更高层次的功能。
- 检查类
接着,让我们使用ReflectionClass对象来研究脚本中的CDProduct。这个类属于哪一种类型?可以创建实例吗?下面这个函数回答了这些问题:
function classData(ReflectionClass $class)
{
$details = "";
$name = $class->getName();
if ($class->isUserDefined()) {
$details .= "$name is user defined\n";
}
if ($class->isInternal()) {
$details .= "$name is built-in\n";
}
if ($class->isAbstract()) {
$details .= "$name is an abstract class\n";
}
if ($class->isInterface()) {
$details .= "$name is interface\n";
}
if ($class->isFinal()) {
$details .= "$name is a final class\n";
}
if ($class->isInstantiable()) {
$details .= "$name can be instantiated\n";
} else {
$details .= "$name can not be instantiated\n";
}
return $details;
}
$prod_class = new ReflectionClass(('CdProduct'));
print classData($prod_class);
你甚至可以检查用户自定义的相关源代码。ReflectionClass对象提供自定义类所在的文件名及文件中类的起始和终止行。
class ReflectionUtil
{
static function getClassSource(ReflectionClass $class)
{
$path = $class->getFileName();
$lines = @file($path);
$from = $class->getStartLine();
$to = $class->getEndLine();
$len = $to - $from + 1;
return implode(array_slice($lines, $from - 1, $len));
}
}
print ReflectionUtil::getClassSource(new ReflectionClass('CdProduct'));
在实际项目中,应该检查参数和代码结果。
- 检查方法
$prod_class = new ReflectionClass('CdProduct');
$methods = $prod_class->getMethods();
foreach ($methods as $method) {
print methodData($method);
print "\n----\n";
}
function methodData(ReflectionMethod $method)
{
$details = "";
$name = $method->getName();
if ($method->isUserDefined()) {
$details .= "$name is user defined\n";
}
if ($method->isInternal()) {
$details .= "$name is built-in\n";
}
if ($method->isAbstract()) {
$details .= "$name is an abstract class\n";
}
if ($method->isPublic()) {
$details .= "$name is public\n";
}
if ($method->isProtected()) {
$details .= "$name is protected\n";
}
if ($method->isPrivate()) {
$details .= "$name is private\n";
}
if ($method->isStatic()) {
$details .= "$name is static\n";
}
if ($method->isFinal()) {
$details .= "$name is final\n";
}
if ($method->isConstructor()) {
$details .= "$name is the constructor\n";
}
if ($method->returnsReference()) {
$details .= "$name returns a reference (as opposed to a value)\n";
}
return $details;
}
可以像之前那样使用ReflectionClass那样获得类方法的源代码。
class ReflectionUtil
{
static function getMethodSource(ReflectionMethod $method)
{
$path = $method->getFileName();
$lines = @file($path);
$from = $method->getStartLine();
$to = $method->getEndLine();
$len = $to - $from + 1;
return implode(array_slice($lines, $from - 1, $len));
}
}
$class = new ReflectionClass('CdProduct');
$method = $class->getMethod('getSummaryLine');
print ReflectionUtil::getMethodSource($method);
- 检查方法参数
在PHP5中,声明类方法时可以限制参数中对象的类型,因此检查方法的参数变得非常必要。为此,反射API提供了ReflectionParameter类。
$class = new ReflectionClass('CdProduct');
$method = $class->getMethod('__construct');
$params = $method->getParameters();
foreach ($params as $param) {
print argData($param) . "\n";
}
function argData(ReflectionParameter $arg)
{
$details = "";
$declaringclass = $arg->getDeclaringClass();
$name = $arg->getName();
$class = $arg->getClass();
$position = $arg->getPosition();
$details .= "\$$name has position $position\n";
if (!empty($class)) {
$classname = $class->getName();
$details .= "\$$name must be a $classname object\n";
}
if ($arg->isPassedByReference()) {
$details .= "\$$name is passed by reference\n";
}
if ($arg->isDefaultValueAvailable()) {
$def = $arg->getDefaultValue();
$details .= "\$$name has default:$def\n";
}
return $details;
}
- 使用反射API
假设我们要创建一个类来动态调用Module对象,即该类可以自由加载第三方插件并集成进已有的系统,而不需把第三方代码硬编码进原有的代码。要达到这个目的,可以在module接口或抽象类中定义一个execute()方法,强制要求所有的子类必须实现该方法。可以允许用户在外部XML配置文件中列出所有Module类。系统可以使用XML提供的信息来加载一定数目的Module对象,然后对每个Module对象调用execute()。
class Person
{
public $name;
function __construct($name)
{
$this->name = $name;
}
}
interface Module
{
function execute();
}
class FtpModule implements Module
{
function setHost($host)
{
print "FtpModule::setHost():$host\n";
}
function setUser($user)
{
print "FtpModule::setUser():$user\n";
}
function execute()
{
}
}
class PersonModule implements Module
{
function setPerson(Person $person)
{
print "PersonModule::setPerson():{$person->name}\n";
}
function execute()
{
}
}
class ModuleRunner
{
private $configData = array(
"PersonModule" => array('person' => 'bob'),
"FtpModule" => array('host' => 'example.com',
'user' => 'anon')
);
private $modules = array();
function init()
{
$interface = new ReflectionClass('Module');
foreach ($this->configData as $modulename => $params) {
$module_class = new ReflectionClass($modulename);
if (!$module_class->isSubclassOf($interface)) {
throw new Exception("unknown module type:$modulename");
}
$module = $module_class->newInstance();
foreach ($module_class->getMethods() as $method) {
$this->handleMethod($module, $method, $params);
//
}
array_push($this->modules, $module);
}
}
function handleMethod(Module $module, ReflectionMethod $method, $params)
{
$name = $method->getName();
$args = $method->getParameters();
if (count($args) != 1 || substr($name, 0, 3) != "set") {
return false;
}
$property = strtolower(substr($name, 3));
if (!isset($params[$property])) {
return false;
}
$arg_class = $args[0]->getClass();
if (empty($arg_class)) {
$method->invoke($module, $params[$property]);
} else {
$method->invoke($module, $arg_class->newInstance($params[$property]));
}
}
}
更多推荐
所有评论(0)