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:
Nota:
Para detalhes sobre essas classes, procure nos próximos capítulos.
Se fossemos executar o código no exemplo abaixo:
Exemplo 19-31. 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 ] {
}
}
} |
|
A classe ReflectionFunction permite a
engenharia-reversa de funções.
Nota:
invokeArgs() foi acrescentado 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-32. 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().
A classe ReflectionParameter recupera
informação sobre os parâmetros de uma função ou de um método.
Nota:
getDefaultValue(),
isDefaultValueAvailable(),
isOptional() were added in 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-33. 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' ); } ?>
|
|
A classe ReflectionClass permite
fazer a engenharia-reversa de uma classe.
Nota:
hasMethod() was added in 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-34. 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').
A classe ReflectionMethod permite fazer
a engenharia-reversa de métodos de classes.
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-35. 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.
A classe ReflectionProperty permite
fazer engenharia-reversa das propriedades da classe.
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-36. 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.
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().
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-37. 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) ); ?>
|
|
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-38. 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