Manual do PHP
Anterior | Capítulo 19. Classes e Objetos (PHP 5) | Próxima

# 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 implements Reflector { } class ReflectionParameter implements Reflector { } class ReflectionMethod extends ReflectionFunction { } 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:

Exemplo 19-33. 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 [ <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

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 implements Reflector { final private __clone() public object __construct(string name) public string __toString() public static string export(string name, bool return) 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 mixed invoke(mixed args) public mixed invokeArgs(array args) public bool returnsReference() public ReflectionParameter[] getParameters() public int getNumberOfParameters() public int getNumberOfRequiredParameters() } ?>`

> 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.

Exemplo 19-34. 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 object __construct(string name) public string __toString() public static string export(mixed class, bool return) public string getName() public bool isPassedByReference() public ReflectionClass getClass() public bool isArray() public bool allowsNull() 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.

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.

Exemplo 19-35. 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 uma classe.

`<?php class ReflectionClass implements Reflector { final private __clone() public object __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 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.

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.

Exemplo 19-36. 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 object __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 ReflectionFunction { public __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 ReflectionFunction 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.

Exemplo 19-37. 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 __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.

Exemplo 19-38. 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 __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() } ?>`

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.

Exemplo 19-39. 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.

Exemplo 19-40. Herdando as classes built-in
`<?php /** * Minha classe ReflectionMethod */ class My_Reflection_Method extends ReflectionMethod { public $visibility = ''; 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`

Anterior | Principal | Próxima
Comparando objetos | Acima | Indução de Tipo