PHP 5 viene con un API completa de reflexión que agrega la
habilidad de hacer ingeniería inversa de clases, interfaces,
funciones y métodos así como extensiones. Adicionalmente,
el API de reflexión también ofrece formas de obtener los
comentarios de los documentos para funciones, clases y métodos.
El API de reflexión es una extensión orientada a objetos
para el compilador Zend, consistente de las siguientes clases:
Nota:
Para detalles de estas clases, de una mirada a los siguientes
capítulos.
Si fuéramos a ejecutar el código en el siguiente ejemplo:
Ejemplo 19-31. Uso básico del API reflexión
<?php Reflection::export(new ReflectionClass('Exception')); ?>
|
El resultado del ejemplo seria: 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 ] {
}
}
} |
|
La clase ReflectionFunction te permite funciones
de ingenierí inversa.
Nota:
invokeArgs() fue agregado en PHP 5.1.0.
Para entender directamente una función, primero tiene que crear
una isntancia de la clase ReflectionFunction.
Hasta entonces puede llamar cualquier de los métodos anteriores en
esta instancia.
Ejemplo 19-32. Usando la clase ReflectionFunction
<?php /** * A simple counter * * @return int */ function counter() { static $c = 0; return $c++; }
// Create an instance of the Reflection_Function class $func = new ReflectionFunction('counter');
// Print out basic information 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() );
// Print documentation comment printf("---> Documentation:\n %s\n", var_export($func->getDocComment(), 1));
// Print static variables if existant if ($statics = $func->getStaticVariables()) { printf("---> Static variables: %s\n", var_export($statics, 1)); }
// Invoke the function printf("---> Invokation results in: "); var_dump($func->invoke());
// you may prefer to use the export() method echo "\nReflectionFunction::export() results:\n"; echo ReflectionFunction::export('counter'); ?>
|
|
Nota:
El método invoke() acepta un número de
variable de argumentos los cuales son pasados a la función tal
y como se hace en call_user_func().
La case ReflectionParameter obtiene
información acerca de los parámetros de una función
o un método.
Nota:
getDefaultValue(),
isDefaultValueAvailable(),
isOptional() fueron agregados en PHP 5.1.0.
Para entender los parámetros de la función, tendrá
primero que crear una instancia de la clase
ReflectionFunction o de la clase
ReflectionMethod y entonces usar sus método
getParameters() para obtener una matriz de
parámetros.
Ejemplo 19-33. Usando la clase ReflectionParameter
<?php function foo($a, $b, $c) { } function bar(Exception $a, &$b, $c) { } function baz(ReflectionFunction $a, $b = 1, $c = null) { } function abc() { }
// Create an instance of Reflection_Function with the // parameter given from the command line. $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' ); } ?>
|
|
La clase ReflectionClass te permite hacer
ingeniería inversa de clases.
Nota:
hasConstant(), hasMethod(),
hasProperty() fueron agregados en PHP 5.1.0.
Para entender una clase, primero tendrá que crear una instancia
de la clase ReflectionClass. Entonces puede
llamar cualquier de los métodos anteriores en esta instancia.
Ejemplo 19-34. Usando la clase ReflectionClass
<?php interface Serializable { // ... }
class Object { // ... }
/** * A counter class */ class Counter extends Object implements Serializable { const START = 0; private static $c = Counter::START;
/** * Invoke counter * * @access public * @return int */ public function count() { return self::$c++; } }
// Create an instance of the ReflectionClass class $class = new ReflectionClass('Counter');
// Print out basic information 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())) );
// Print documentation comment printf("---> Documentation:\n %s\n", var_export($class->getDocComment(), 1));
// Print which interfaces are implemented by this class printf("---> Implements:\n %s\n", var_export($class->getInterfaces(), 1));
// Print class constants printf("---> Constants: %s\n", var_export($class->getConstants(), 1));
// Print class properties printf("---> Properties: %s\n", var_export($class->getProperties(), 1));
// Print class methods printf("---> Methods: %s\n", var_export($class->getMethods(), 1));
// If this class is instantiable, create an instance 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'; } ?>
|
|
Nota:
El método newInstance() acepta un número
variable de argumentos los cuales son pasados a la función tal y
como si se usara call_user_func().
Nota:
$class = new ReflectionClass('Foo'); $class->isInstance($arg)
es equivalente a $arg instanceof Foo o
is_a($arg, 'Foo').
La clase ReflectionMethod te permite hacer
ingenieria inversa de los métodos de la clase.
Para entender los métodos, primero tendrá que crear una
instancia de la clase ReflectionMethod. Puede
entonces llamar cualquiera de los métodos anteriores en esta
instancia.
Ejemplo 19-35. Usando la clase ReflectionMethod
<?php class Counter { private static $c = 0;
/** * Increment counter * * @final * @static * @access public * @return int */ final public static function increment() { return ++self::$c; } }
// Create an instance of the Reflection_Method class $method = new ReflectionMethod('Counter', 'increment');
// Print out basic information 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())) );
// Print documentation comment printf("---> Documentation:\n %s\n", var_export($method->getDocComment(), 1));
// Print static variables if existant if ($statics= $method->getStaticVariables()) { printf("---> Static variables: %s\n", var_export($statics, 1)); }
// Invoke the method printf("---> Invokation results in: "); var_dump($method->invoke(NULL)); ?>
|
|
Nota:
Tratar de invocar métodos private, protected o abstract
resultará en una excepción siendo arrojada del
método invoke().
Nota:
Para métodos static como se vió anteriormente, se debe
a invoke() se debe pasar NULL como primer argumento.
Para métodos no estáticos, se pasa una instancia de la clase.
La clase ReflectionProperty te permite
hacer ingeniería inversa a las propiedades de la clase.
Para entender las propiedades, se debe primero crear una instancia
de la clase ReflectionProperty. Y entonces
puede llamar cualquiera de los métodos anteriores sobre esta
instancia.
Ejemplo 19-36. Usando la clase ReflectionProperty
<?php class String { public $length = 5; }
// Create an instance of the ReflectionProperty class $prop = new ReflectionProperty('String', 'length');
// Print out basic information 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) );
// Create an instance of String $obj= new String();
// Get current value printf("---> Value is: "); var_dump($prop->getValue($obj));
// Change value $prop->setValue($obj, 10); printf("---> Setting value to 10, new value is: "); var_dump($prop->getValue($obj));
// Dump object var_dump($obj); ?>
|
|
Nota:
Trying to get or set private or protected class property's values
will result in an exception being thrown.
La clase ReflectionExtension te permite hacer
ingeniería inversa a extensiones. Puede obtener todas las
extensiones cargadas en tiempo de ejecución usando
get_loaded_extensions().
Para entender una extensión, primero se tiene que crear una
instancia de la clase ReflectionExtension. Y
entonces puede llamarse a cualquiera de los étodos mencionados
arriba sobre esa instancia.
Ejemplo 19-37. Usando la clase ReflectionExtension
<?php // Create an instance of the ReflectionProperty class $ext = new ReflectionExtension('standard');
// Print out basic information 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) ); ?>
|
|
EN caso de que se quiera crear una versión especializada de las
clases integradas (es decir, para crear HTML con colores cuando se
exporta, tener fácil acceso a las variables de los miembros en
lugar de los métodos o tener métodos de utilería),
se puede simplemente extenderlos.
Ejemplo 19-38. Extendiendo las clase integradas
<?php /** * My Reflection_Method class */ class My_Reflection_Method extends ReflectionMethod { public $visibility = '';
public function __construct($o, $m) { parent::__construct($o, $m); $this->visibility= Reflection::getModifierNames($this->getModifiers()); } }
/** * Demo class #1 * */ class T { protected function x() {} }
/** * Demo class #2 * */ class U extends T { function x() {} }
// Print out information var_dump(new My_Reflection_Method('U', 'x')); ?>
|
|
Nota:
Precaución: Si se desea sobreescribir el constructor, recuerde
llamar el constructor padre _antes_ que cualquier otro código
que se inserte. El no hacerlo así resultará en:
Fatal error: Internal error: Failed to retrieve the reflection object