PHP 5には完全なリフレクション APIが付属しており、
クラス、インターフェイス、関数、メソッド、そしてエクステンションについて
リバースエンジニアリングを行うことができます。
さらに、このリフレクション APIは関数、クラス、メソッドに
関するドキュメントコメントも取得することができます。
リフレクション APIは、Zend Engineのオブジェクト指向エクステンション
で、以下のクラスから構成されています。
注意:
これらのクラスに関する詳細については、次章を参照してください。
以下の例のコードを実行してみましょう。
例 19-33. リフレクション APIの基本的な使用法
<?php Reflection::export(new ReflectionClass('Exception')); ?>
|
上の例の出力は以下となります。 Class [ <internal> class Exception ] {
- Constants [0] {
}
- Static properties [0] {
}
- Static methods [0] {
}
- Properties [6] {
Property [ <default> protected $message ]
Property [ <default> private $string ]
Property [ <default> protected $code ]
Property [ <default> protected $file ]
Property [ <default> protected $line ]
Property [ <default> private $trace ]
}
- Methods [9] {
Method [ <internal> final private method __clone ] {
}
Method [ <internal> <ctor> public method __construct ] {
- Parameters [2] {
Parameter #0 [ <required> $message ]
Parameter #1 [ <required> $code ]
}
}
Method [ <internal> final public method getMessage ] {
}
Method [ <internal> final public method getCode ] {
}
Method [ <internal> final public method getFile ] {
}
Method [ <internal> final public method getLine ] {
}
Method [ <internal> final public method getTrace ] {
}
Method [ <internal> final public method getTraceAsString ] {
}
Method [ <internal> public method __toString ] {
}
}
} |
|
ReflectionException は標準の Exception を継承しており、
リフレクション API によって投げられます。
固有のメソッドやプロパティは導入されていません。
ReflectionFunctionクラスにより、
関数のリバースエンジニアリングが可能となります。
注意:
getNumberOfParameters() と
getNumberOfRequiredParameters() は PHP 5.0.3
で追加され、
invokeArgs() は PHP 5.1.0 で追加されました。
関数の内部を調べるために、まず、
ReflectionFunctionクラスのインスタンスを
生成する必要があります。
次にこのインスタンスの上のメソッドのどれかをコールすることができます。
例 19-34. ReflectionFunctionクラスの使用法
<?php /** * 簡単なカウンタ * * @return int */ function counter() { static $c = 0; return $c++; }
// Reflection_Function クラスのインスタンスを生成する $func = new ReflectionFunction('counter');
// 基本情報を表示する printf( "===> The %s function '%s'\n". " declared in %s\n". " lines %d to %d\n", $func->isInternal() ? 'internal' : 'user-defined', $func->getName(), $func->getFileName(), $func->getStartLine(), $func->getEndline() );
// ドキュメントコメントを表示する printf("---> Documentation:\n %s\n", var_export($func->getDocComment(), 1));
// static 変数があれば表示する if ($statics = $func->getStaticVariables()) { printf("---> Static variables: %s\n", var_export($statics, 1)); }
// 関数を呼び出す printf("---> Invokation results in: "); var_dump($func->invoke());
// export() メソッドを使用する方が良い知れない echo "\nReflectionFunction::export() results:\n"; echo ReflectionFunction::export('counter'); ?>
|
|
ReflectionParameterクラスは、
関数またはメソッドのパラメータに関する情報を取得します。
注意:
getDefaultValue(),
isDefaultValueAvailable(),
isOptional() は PHP 5.0.3 で追加され、
isArray() は PHP 5.1.0で追加されました。
関数パラメータの内部を調べる際には、まず、
ReflectionFunction クラスまたは
ReflectionMethod クラスのインスタンスを
作成する必要があります。
次に、配列のパラメータを取得するために、そのインスタンスの
getParameters()メソッドを使用してください。
例 19-35. ReflectionParameterクラスの使用
<?php function foo($a, $b, $c) { } function bar(Exception $a, &$b, $c) { } function baz(ReflectionFunction $a, $b = 1, $c = null) { } function abc() { }
// コマンドラインから与えられたパラメータを使って // Reflection_Function のインスタンスを生成する $reflect = new ReflectionFunction($argv[1]);
echo $reflect;
foreach ($reflect->getParameters() as $i => $param) { printf( "-- Parameter #%d: %s {\n". " Class: %s\n". " Allows NULL: %s\n". " Passed to by reference: %s\n". " Is optional?: %s\n". "}\n", $i, $param->getName(), var_export($param->getClass(), 1), var_export($param->allowsNull(), 1), var_export($param->isPassedByReference(), 1), $param->isOptional() ? 'yes' : 'no' ); } ?>
|
|
ReflectionClassクラスにより、
クラスのリバースエンジニアリングが可能となります。
注意:
hasConstant(), hasMethod(),
hasProperty(), getStaticPropertyValue()
および setStaticPropertyValue()
は、PHP 5.1.0 で追加されました。
また、newInstanceArgs() は PHP 5.1.3 で追加されました。
クラスのイントロスペクションを行うには、まず
ReflectionClass
クラスのインスタンスを生成する必要があります。それから、
このインスタンスのメソッドをコールしてください。
例 19-36. ReflectionClassクラスの使用法
<?php interface Serializable { // ... }
class Object { // ... }
/** * カウンタクラス */ class Counter extends Object implements Serializable { const START = 0; private static $c = Counter::START;
/** * カウンタを呼び出す * * @access public * @return int */ public function count() { return self::$c++; } }
// ReflectionClass クラスのインスタンスを生成する $class = new ReflectionClass('Counter');
// 基本情報を表示する printf( "===> The %s%s%s %s '%s' [extends %s]\n" . " declared in %s\n" . " lines %d to %d\n" . " having the modifiers %d [%s]\n", $class->isInternal() ? 'internal' : 'user-defined', $class->isAbstract() ? ' abstract' : '', $class->isFinal() ? ' final' : '', $class->isInterface() ? 'interface' : 'class', $class->getName(), var_export($class->getParentClass(), 1), $class->getFileName(), $class->getStartLine(), $class->getEndline(), $class->getModifiers(), implode(' ', Reflection::getModifierNames($class->getModifiers())) );
// ドキュメントコメントを表示する printf("---> Documentation:\n %s\n", var_export($class->getDocComment(), 1));
// このクラスが実装しているインターフェースを表示する printf("---> Implements:\n %s\n", var_export($class->getInterfaces(), 1));
// クラス定数を表示する printf("---> Constants: %s\n", var_export($class->getConstants(), 1));
// クラスプロパティを表示する printf("---> Properties: %s\n", var_export($class->getProperties(), 1));
// クラスメソッドを表示する printf("---> Methods: %s\n", var_export($class->getMethods(), 1));
// このクラスがインスタンス化可能な場合、インスタンスを生成する if ($class->isInstantiable()) { $counter = $class->newInstance();
echo '---> $counter is instance? '; echo $class->isInstance($counter) ? 'yes' : 'no';
echo "\n---> new Object() is instance? "; echo $class->isInstance(new Object()) ? 'yes' : 'no'; } ?>
|
|
注意:
$class = new ReflectionClass('Foo'); $class->isInstance($arg)
は、$arg instanceof Foo または
is_a($arg, 'Foo')と等価です。
ReflectionObject クラスにより、
オブジェクトのリバースエンジニアリングが可能となります。
ReflectionMethodクラスにより、
クラスメソッドのリバースエンジニアリングが可能となります。
メソッドの内部を調べるために、まず、
ReflectionMethodクラスのインスタンスを
生成する必要があります。
次にこのインスタンスの上のメソッドのどれかをコールすることができます。
例 19-37. ReflectionMethodクラスの使用
<?php class Counter { private static $c = 0;
/** * カウンタをインクリメントする * * @final * @static * @access public * @return int */ final public static function increment() { return ++self::$c; } }
// Reflection_Method クラスのインスタンスを生成する $method = new ReflectionMethod('Counter', 'increment');
// 基本情報を表示する printf( "===> The %s%s%s%s%s%s%s method '%s' (which is %s)\n" . " declared in %s\n" . " lines %d to %d\n" . " having the modifiers %d[%s]\n", $method->isInternal() ? 'internal' : 'user-defined', $method->isAbstract() ? ' abstract' : '', $method->isFinal() ? ' final' : '', $method->isPublic() ? ' public' : '', $method->isPrivate() ? ' private' : '', $method->isProtected() ? ' protected' : '', $method->isStatic() ? ' static' : '', $method->getName(), $method->isConstructor() ? 'the constructor' : 'a regular method', $method->getFileName(), $method->getStartLine(), $method->getEndline(), $method->getModifiers(), implode(' ', Reflection::getModifierNames($method->getModifiers())) );
// ドキュメントコメントを表示する printf("---> Documentation:\n %s\n", var_export($method->getDocComment(), 1));
// static 変数があれば表示する if ($statics= $method->getStaticVariables()) { printf("---> Static variables: %s\n", var_export($statics, 1)); }
// メソッドを呼び出す printf("---> Invokation results in: "); var_dump($method->invoke(NULL)); ?>
|
|
注意:
private, protectedまたはabstractメソッドのinvokeを行うと、
invoke()から例外がスローされます。
注意:
上記のstaticメソッドの場合、
invoke()の最初の引数にNULLを渡す必要があります。
staticでないメソッドの場合、ここにクラスのインスタンスを指定してください。
ReflectionPropertyクラスにより、
クラスプロパティに関する
リバースエンジニアリングが可能となります。
プロパティの内部を調べるために、まず、
ReflectionPropertyクラスのインスタンスを
生成する必要があります。
次にこのインスタンスの上のメソッドのどれかをコールすることができます。
例 19-38. ReflectionPropertyクラスの使用
<?php class String { public $length = 5; }
// ReflectionProperty クラスのインスタンスを生成する $prop = new ReflectionProperty('String', 'length');
// 基本情報を表示する printf( "===> The%s%s%s%s property '%s' (which was %s)\n" . " having the modifiers %s\n", $prop->isPublic() ? ' public' : '', $prop->isPrivate() ? ' private' : '', $prop->isProtected() ? ' protected' : '', $prop->isStatic() ? ' static' : '', $prop->getName(), $prop->isDefault() ? 'declared at compile-time' : 'created at run-time', var_export(Reflection::getModifierNames($prop->getModifiers()), 1) );
// String のインスタンスを生成する $obj= new String();
// 現在の値を取得する printf("---> Value is: "); var_dump($prop->getValue($obj));
// 値を変更する $prop->setValue($obj, 10); printf("---> Setting value to 10, new value is: "); var_dump($prop->getValue($obj));
// オブジェクトをダンプする var_dump($obj); ?>
|
|
注意:
privateまたはprotectedクラスプロパティの値の取得または設定を
行うと、例外がスローされます。
The ReflectionExtensionクラスにより、
エクステンションのリバースエンジニアリングが可能となります。
実行時にロードされている全てのエクステンションを
get_loaded_extensions()により取得することができます。
メソッドの内部を調べるために、まず、
ReflectionExtensionクラスのインスタンスを
生成する必要があります。
次にこのインスタンスの上のメソッドのどれかをコールすることができます。
例 19-39. ReflectionExtensionクラスの使用
<?php // ReflectionProperty クラスのインスタンスを生成する $ext = new ReflectionExtension('standard');
// 基本情報を表示する printf( "Name : %s\n" . "Version : %s\n" . "Functions : [%d] %s\n" . "Constants : [%d] %s\n" . "INI entries : [%d] %s\n" . "Classes : [%d] %s\n", $ext->getName(), $ext->getVersion() ? $ext->getVersion() : 'NO_VERSION', sizeof($ext->getFunctions()), var_export($ext->getFunctions(), 1),
sizeof($ext->getConstants()), var_export($ext->getConstants(), 1),
sizeof($ext->getINIEntries()), var_export($ext->getINIEntries(), 1),
sizeof($ext->getClassNames()), var_export($ext->getClassNames(), 1) ); ?>
|
|
組み込みクラスの特別なバージョンを作成したい場合
(例えば、、エクスポートする際に、色づけしたHTMLを作成したり、
メソッドの代わりに簡単にアクセスできるメンバー変数を作成したり、
補助的なメソッドを作成したり、)、
Reflectionクラスを拡張することができます。
例 19-40. 組み込みクラスを拡張する
<?php /** * 独自の Reflection_Method クラス */ class My_Reflection_Method extends ReflectionMethod { public $visibility = '';
public function __construct($o, $m) { parent::__construct($o, $m); $this->visibility= Reflection::getModifierNames($this->getModifiers()); } }
/** * デモクラス #1 * */ class T { protected function x() {} }
/** * デモクラス #2 * */ class U extends T { function x() {} }
// 基本情報を表示する var_dump(new My_Reflection_Method('U', 'x')); ?>
|
|
注意:
注意: Iコンストラクタを上書きした場合、
挿入するコードの前に
親クラスのコンストラクタをコールしわすれないようにしてください。
これを怠ると、以下のようなエラーを発生します。
Fatal error: Internal error: Failed to retrieve the reflection object