Reflexão
Table of Contents
Introdução
PHP 5 vem com uma API completa de reflexão que acrescenta a habilidade de fazer engenharia-reversa em classes, interfaces, funções e métodos assim como extensões. Além disso, a API de reflexão também oferece maneiras de recuperar comentários de documentação para funções, classes e métodos.
A API de reflexão é uma extensão orientada a objetos ao Engine Zend, consistindo das seguintes classes:
<?php
class Reflection { }
interface Reflector { }
class ReflectionException extends Exception { }
class ReflectionFunction extends ReflectionFunctionAbstract implements Reflector { }
class ReflectionParameter implements Reflector { }
class ReflectionMethod extends ReflectionFunctionAbstract implements Reflector { }
class ReflectionClass implements Reflector { }
class ReflectionObject extends ReflectionClass { }
class ReflectionProperty implements Reflector { }
class ReflectionExtension implements Reflector { }
?>
Nota: Para detalhes sobre essas classes, procure nos próximos capÃtulos.
Se fossemos executar o código no exemplo abaixo:
Example#1 Uso básico da API de reflexão
<?php
Reflection::export(new ReflectionClass('Exception'));
?>
O exemplo acima irá imprimir:
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 [ <optional> $message ] Parameter #1 [ <optional> $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 ] { } } }
Reflector
Reflector é uma interface implementada por todas classes Reflection exportáveis.
<?php
interface Reflector
{
public string __toString()
public static string export()
}
?>
ReflectionException
ReflectionException herda da classe de exceção padrão Exception e é disparada pela API de Reflecção. Nenhum método ou propriedade especÃficos são introduzidos.
ReflectionFunction
A classe ReflectionFunction permite a engenharia-reversa de funções.
<?php
class ReflectionFunction extends ReflectionFunctionAbstract implements Reflector
{
final private __clone()
public void __construct(string name)
public string __toString()
public static string export(string name, bool return)
public string getName()
public bool isInternal()
public bool isDisabled()
public bool isUserDefined()
public string getFileName()
public int getStartLine()
public int getEndLine()
public string getDocComment()
public array getStaticVariables()
public mixed invoke([mixed args [, ...]])
public mixed invokeArgs(array args)
public bool returnsReference()
public ReflectionParameter[] getParameters()
public int getNumberOfParameters()
public int getNumberOfRequiredParameters()
}
?>
Parent class ReflectionFunctionAbstract has the same methods except invoke(), invokeArgs(), export() and isDisabled().
Nota: getNumberOfParameters() e getNumberOfRequiredParameters() foram adicionados no PHP 5.0.3, e invokeArgs() foi adicionado no PHP 5.1.0.
Para introspectar uma função, você primeiro terá que criar uma instância da classe ReflectionFunction. Você pode, então, chamar qualquer um dos métodos acima nessa instância.
Example#2 Usando a classe ReflectionFunction
<?php
/**
* Um contador simples
*
* @return int
*/
function counter()
{
static $c = 0;
return $c++;
}
// Cria uma instância da classe ReflectionFunction
$func = new ReflectionFunction('counter');
// Imprime informações básicas
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()
);
// Imprime comentários de documentação
printf("---> Documentation:\n %s\n", var_export($func->getDocComment(), 1));
// Imprime variáveis estáticas se existirem
if ($statics = $func->getStaticVariables())
{
printf("---> Static variables: %s\n", var_export($statics, 1));
}
// Invoca a função
printf("---> Invokation results in: ");
var_dump($func->invoke());
// Você pode preferir usar o método export()
echo "\nReflectionFunction::export() results:\n";
echo ReflectionFunction::export('counter');
?>
Nota: O método invoke() aceita um número variável de argumentos que serão passados para a função assim como em call_user_func().
ReflectionParameter
A classe ReflectionParameter recupera informação sobre os parâmetros de uma função ou de um método.
<?php
class ReflectionParameter implements Reflector
{
final private __clone()
public void __construct(string name)
public string __toString()
public static string export(mixed function, mixed parameter, bool return)
public string getName()
public bool isPassedByReference()
public ReflectionClass getDeclaringClass()
public ReflectionClass getClass()
public bool isArray()
public bool allowsNull()
public bool isPassedByReference()
public bool isOptional()
public bool isDefaultValueAvailable()
public mixed getDefaultValue()
}
?>
Nota: getDefaultValue(), isDefaultValueAvailable(), isOptional() foram adicionados no PHP 5.0.3, e isArray() foi adicionado no PHP 5.1.0. getDeclaringFunction() e getPosition() foram adicionados no PHP 5.2.3.
Para introspectar parâmetros, você terá que primeiro criar uma instância das classes ReflectionFunction ou ReflectionMethod e então usar o seu método getParameters() para recuperar um array de parâmetros.
Example#3 Usando a classe ReflectionParameter
<?php
function foo($a, $b, $c) { }
function bar(Exception $a, &$b, $c) { }
function baz(ReflectionFunction $a, $b = 1, $c = null) { }
function abc() { }
// Crie uma instância de ReflectionFunction com o
// parâmetro dado da linha de comando.
$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
A classe ReflectionClass permite fazer a engenharia-reversa de classes e interfaces.
<?php
class ReflectionClass implements Reflector
{
final private __clone()
public void __construct(string name)
public string __toString()
public static string export()
public string getName()
public bool isInternal()
public bool isUserDefined()
public bool isInstantiable()
public bool hasConstant(string name)
public bool hasMethod(string name)
public bool hasProperty(string name)
public string getFileName()
public int getStartLine()
public int getEndLine()
public string getDocComment()
public ReflectionMethod getConstructor()
public ReflectionMethod getMethod(string name)
public ReflectionMethod[] getMethods()
public ReflectionProperty getProperty(string name)
public ReflectionProperty[] getProperties()
public array getConstants()
public mixed getConstant(string name)
public ReflectionClass[] getInterfaces()
public bool isInterface()
public bool isAbstract()
public bool isFinal()
public int getModifiers()
public bool isInstance(stdclass object)
public stdclass newInstance(mixed args)
public stdclass newInstanceArgs(array args)
public ReflectionClass getParentClass()
public bool isSubclassOf(ReflectionClass class)
public array getStaticProperties()
public mixed getStaticPropertyValue(string name [, mixed default])
public void setStaticPropertyValue(string name, mixed value)
public array getDefaultProperties()
public bool isIterateable()
public bool implementsInterface(string name)
public ReflectionExtension getExtension()
public string getExtensionName()
}
?>
Nota: hasConstant(), hasMethod(), hasProperty(), getStaticPropertyValue() e setStaticPropertyValue() foram adicionados no PHP 5.1.0, enquanto newInstanceArgs() foi adicionado no PHP 5.1.3.
To introspect a class, you will first have to create an instance of the ReflectionClass class. You can then call any of the above methods on this instance.
Example#4 Using the ReflectionClass class
<?php
interface Serializable
{
// ...
}
class Object
{
// ...
}
/**
* Uma classe contadora
*/
class Counter extends Object implements Serializable
{
const START = 0;
private static $c = Counter::START;
/**
* Contador de invocação
*
* @access public
* @return int
*/
public function count() {
return self::$c++;
}
}
// Crie uma instância da classe ReflectionClass
$class = new ReflectionClass('Counter');
// Imprime informação básica
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()))
);
// Imprime comentários de documentação
printf("---> Documentation:\n %s\n", var_export($class->getDocComment(), 1));
// Imprime quais interfaces são implementadas por essa classe
printf("---> Implements:\n %s\n", var_export($class->getInterfaces(), 1));
// Imprime as constantes da classe
printf("---> Constants: %s\n", var_export($class->getConstants(), 1));
// Imprime as propriedades da classe
printf("---> Properties: %s\n", var_export($class->getProperties(), 1));
// Imprime os métodos da classe
printf("---> Methods: %s\n", var_export($class->getMethods(), 1));
// Se essa classe for instanciável, cria uma instância
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: O método newInstance() aceita um número variável de argumentos que são passados para a função assim como em call_user_func().
Nota: $class = new ReflectionClass('Foo'); $class->isInstance($arg) é equivalente a$arg instanceof Foo ou is_a($arg, 'Foo').
ReflectionObject
A classe ReflectionObject permite que você faça engenharia reversa de objetos.
<?php
class ReflectionObject extends ReflectionClass
{
final private __clone()
public void __construct(mixed object)
public string __toString()
public static string export(mixed object, bool return)
}
?>
ReflectionMethod
A classe ReflectionMethod permite fazer a engenharia-reversa de métodos de classes.
<?php
class ReflectionMethod extends ReflectionFunctionAbstract implements Reflector
{
public void __construct(mixed class, string name)
public string __toString()
public static string export(mixed class, string name, bool return)
public mixed invoke(stdclass object [, mixed args [, ...]])
public moxed invokeArgs(stdclass object, array args)
public bool isFinal()
public bool isAbstract()
public bool isPublic()
public bool isPrivate()
public bool isProtected()
public bool isStatic()
public bool isConstructor()
public bool isDestructor()
public int getModifiers()
public ReflectionClass getDeclaringClass()
// Herdado de ReflectionFunctionAbstract
final private __clone()
public string getName()
public bool isInternal()
public bool isUserDefined()
public string getFileName()
public int getStartLine()
public int getEndLine()
public string getDocComment()
public array getStaticVariables()
public bool returnsReference()
public ReflectionParameter[] getParameters()
public int getNumberOfParameters()
public int getNumberOfRequiredParameters()
}
?>
Para introspectar um método, você tem que primeiro criar uma instância da classe ReflectionMethod. Você pode, então, chamar qualquer um dos métodos acima nessa instância.
Example#5 Usando a classe ReflectionMethod
<?php
class Counter
{
private static $c = 0;
/**
* Incrementa o contador
*
* @final
* @static
* @access public
* @return int
*/
final public static function increment()
{
return ++self::$c;
}
}
// Cria uma instância da classe ReflectionMethod
$method = new ReflectionMethod('Counter', 'increment');
// Imprime informação básica
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()))
);
// Imprime comentários de documentação
printf("---> Documentation:\n %s\n", var_export($method->getDocComment(), 1));
// Imprime variáveis estáticas se existirem
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: Tentar invocar métodos privados, protegidos ou abstratos resultarão numa exceção sendo disparada do método invoke()
Nota: Para métodos estáticos como visto acima, você deve passar NULL como o primeiro argumento para invoke(). Para métodos não-estáticos, passe uma instância da classe.
ReflectionProperty
A classe ReflectionProperty permite fazer engenharia-reversa das propriedades da classe.
<?php
class ReflectionProperty implements Reflector
{
final private __clone()
public void __construct(mixed class, string name)
public string __toString()
public static string export(mixed class, string name, bool return)
public string getName()
public bool isPublic()
public bool isPrivate()
public bool isProtected()
public bool isStatic()
public bool isDefault()
public int getModifiers()
public mixed getValue(stdclass object)
public void setValue(stdclass object, mixed value)
public ReflectionClass getDeclaringClass()
public string getDocComment()
}
?>
Nota: getDocComment() foi adicionado no PHP 5.1.0.
Para introspectar um método, você tem que primeiro criar uma instância da classe ReflectionProperty. Você pode, então, chamar qualquer um dos métodos acima nessa instância.
Example#6 Usando a classe ReflectionProperty
<?php
class String
{
public $length = 5;
}
// Cria uma instância da classe ReflectionProperty
$prop = new ReflectionProperty('String', 'length');
// Imprime informação básica
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)
);
// Cria uma instância de String
$obj= new String();
// Pega o valor atual
printf("---> Value is: ");
var_dump($prop->getValue($obj));
// Muda o valor
$prop->setValue($obj, 10);
printf("---> Setting value to 10, new value is: ");
var_dump($prop->getValue($obj));
// Destrói o objeto
var_dump($obj);
?>
Nota: Tentar pegar ou editar o valor de propriedades privadas ou protegidaas de uma classe resultará no disparo de uma exceção.
ReflectionExtension
A classe ReflectionExtension permite fazer engenharia-reversa de extensões. Você pode recuperar todas as extensões em tempo de execução usando a função get_loaded_extensions().
<?php
class ReflectionExtension implements Reflector {
final private __clone()
public void __construct(string name)
public string __toString()
public static string export(string name, bool return)
public string getName()
public string getVersion()
public ReflectionFunction[] getFunctions()
public array getConstants()
public array getINIEntries()
public ReflectionClass[] getClasses()
public array getClassNames()
public string info()
}
?>
Para introspectar um método, você tem que primeiro criar uma instância da classe ReflectionProperty. Você pode, então, chamar qualquer um dos métodos acima nessa instância.
Example#7 Usando a classe ReflectionExtension
<?php
// Cria uma instância da classe ReflectionProperty
$ext = new ReflectionExtension('standard');
// Imprime informação básica
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)
);
?>
Herdando as classes de reflexão
Caso você queira criar versões especializdas das classes built-in (digamos, para criar HTML colorido quando sendo exportado, tendo variáveis membros de fácil acesso ao invés de métodos ou tendo métodos utilitários), você pode herdá-las.
Example#8 Herdando as classes built-in
<?php
/**
* Minha classe ReflectionMethod
*/
class My_Reflection_Method extends ReflectionMethod
{
public $visibility = array();
public function __construct($o, $m)
{
parent::__construct($o, $m);
$this->visibility= Reflection::getModifierNames($this->getModifiers());
}
}
/**
* Classe demo #1
*
*/
class T {
protected function x() {}
}
/**
* Classe demo #2
*
*/
class U extends T {
function x() {}
}
// Imprime informação
var_dump(new My_Reflection_Method('U', 'x'));
?>
Nota: Cuidado: Se você estiver sobrescrevendo um construtor, lembre-se de chamar o construtor do pai _antes_ de qualquer código que você acrescentar. Não fazer isso resultará no seguinte: Fatal error: Internal error: Failed to retrieve the reflection object