Forum und email

Reflexão

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 namebool 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($statics1));
}

// 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 parameterbool 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 namemixed 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 objectbool 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 namebool 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($statics1));
}

// 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 namebool 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 objectmixed 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($obj10);
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 namebool 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->visibilityReflection::getModifierNames($this->getModifiers());
    }
}

/**
 * Classe demo #1
 *
 */
class {
    protected function 
x() {}
}

/**
 * Classe demo #2
 *
 */
class extends {
    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