PHP 5 introduit API de réflexion complète qui permet de faire du
reverse-engineer sur les classes, les interfaces,
les fonctions et les méthodes tout comme les extensions. L'API de
réflexion permet également d'obtenir les commentaires de la documentation
pour les fonctions, les classes et les méthodes.
L'API de réflexion est une extension orientée objet du Moteur Zend,
constituée des classes suivantes :
Note :
Pour plus de détails sur ces classes, lisez les chapitres suivants.
Si nous exécutons le code de l'exemple ci-dessous :
Exemple 18-20. Utilisation basique de l'API de réflexion
<?php Reflection::export(new ReflectionClass('Exception')); ?>
|
|
Nous verrons :
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> method __construct ] {
}
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 classe ReflectionFunction vous permet de faire
du reverse-engineer sur les fonctions.
Pour connaître le fonctionnement d'une fonction, vous devez tout d'abord
créer une instance de la classe ReflectionFunction.
Ainsi, vous pouvez appeler n'importe quelle méthode de cette instance.
Exemple 18-21. Utilisation de la classe ReflectionFunction
<?php /** * Un simple compteur * * @return int */ function counter() { static $c = 0;
return $c++; }
// Création d'une instance de la classe Reflection_Function $func = new ReflectionFunction('counter');
// Affichage d'informations basiques 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() );
// Affichage du commentaire de la documentation printf("---> Documentation:\n %s\n", var_export($func->getDocComment(), 1));
// Affichage des variables statiques si elles existent if ($statics = $func->getStaticVariables()) { printf("---> Variables statiques : %s\n", var_export($statics, 1)); }
// Appel de la fonction printf("---> Invocation des résultats dans : "); var_dump($func->invoke());
// vous pouvez préférer utiliser la méthode export() echo "\nRésultat de ReflectionFunction::export() :\n"; echo ReflectionFunction::export('counter'); ?>
|
|
Note :
La méthode invoke() accepte un nombre variable
d'arguments passés à la fonction, tout comme la fonction
call_user_func().
La classe ReflectionParameter récupère les
informations concernant les paramètres des fonctions ou des méthodes.
Note :
isOptional() a été ajouté en PHP 5.1.0.
Pour connaître le fonctionnement des paramètres d'une fonction, vous
devez tout d'abord créer une instance de la classe
ReflectionFunction ou
ReflectionMethod et ainsi, utiliser leurs méthodes
getparameters() pour récupérer un tableau de paramètres.
Exemple 18-22. Utilisation de la classe ReflectionParameter
<?php function foo($a, $b, $c) { } function bar(Exception $a, &$b, $c) { } function baz(ReflectionFunction $a, $b = 1, $c = null) { } function abc() { }
// Création d'une instance de la classe Reflection_Function avec le // paramètre fourni en ligne de commande. $reflect = new ReflectionFunction($argv[1]);
echo $reflect;
foreach ($reflect->getParameters() as $i => $param) { printf( "-- Paramètre #%d : %s {\n". " Classe : %s\n". " Autorise NULL : %s\n". " Passé par référence : %s\n". " Est optionnel ?: %s\n". "}\n", $i, $param->getName(), var_export($param->getClass(), 1), var_export($param->allowsNull(), 1), var_export($param->isPassedByReference(), 1), $param->isOptional() ? 'oui' : 'non' ); } ?>
|
|
La classe ReflectionClass vous permet
de faire du reverse-engineer sur des classes.
Pour connaître le fonctionnement d'une classe, vous devez d'abord créer
une instance de la classe ReflectionClass. Vous
pourrez donc appeler n'importe quelles méthodes de cette instance.
Exemple 18-23. Utilisation de la classe ReflectionClass
<?php interface Serializable { // ... }
class Object { // ... }
/** * Une classe compteur * */ class Counter extends Object implements Serializable { const START = 0; private static $c = Counter::START;
/** * Invocation du compteur * * @access public * @return int */ public function count() { return self::$c++; } }
// Création d'une instance de la classe ReflectionClass $class= new ReflectionClass('Counter');
// Affichage d'informations basiques printf( "===> La %s%s%s %s '%s' [extension de %s]\n". " déclarée dans %s\n". " lignes %d à %d\n". " a le modificateur %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())) );
// Affichage du commentaire de la documentation printf("---> Documentation:\n %s\n", var_export($class->getDocComment(), 1));
// Affichage de l'interface qui implémente cette classe printf("---> Implémenté :\n %s\n", var_export($class->getInterfaces(), 1));
// Affichage des constantes de la classe printf("---> Constantes : %s\n", var_export($class->getConstants(), 1));
// Affichage des propriétés de la classe printf("---> Properties: %s\n", var_export($class->getProperties(), 1));
// Affichage des méthodes de la classe printf("---> Méthodes : %s\n", var_export($class->getMethods(), 1));
// Si cette classe est instanciable, création d'une instance if ($class->isInstantiable()) { $counter= $class->newInstance();
echo '---> $counter est uneinstance ? '; echo $class->isInstance($counter) ? 'oui' : 'non';
echo "\n---> Le nouvel objet Object() est une instance ? "; echo $class->isInstance(new Object()) ? 'oui' : 'non'; } ?>
|
|
Note :
La méthode newinstance() accepte un nombre variable
d'arguments passés à la fonction, tout comme la fonction
call_user_func().
Note :
$class = new ReflectionClass('Foo'); $class->isInstance($arg)
est équivalent à $arg instanceof Foo ou
is_a($arg, 'Foo').
La classe ReflectionMethod vous permet de faire du
reverse-engineer sur les méthodes des classes.
Pour connaître le fonctionnement d'une méthode, vous devez d'abord créer
une instance de la classe ReflectionMethod. Vous
pourrez ainsi appeler n'importe quelles méthode de cette instance.
Exemple 18-24. Utilisation de la classe ReflectionMethod
<?php class Counter { private static $c = 0;
/** * Incrémentation d'un compteur * * @final * @static * @access public * @return int */ final public static function increment() { self::$c++; return self::$c; } }
// Création d'une instance de la classe Reflection_Method $method= new ReflectionMethod('Counter', 'increment');
// Affichage d'informations basiques printf( "===> La méthode %s%s%s%s%s%s%s '%s' (qui est %s)\n". " déclaré dans %s\n". " lignes %d à %d\n". " a les modificateurs %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())) );
// Affichage du commentaire de la documentation printf("---> Documentation:\n %s\n", var_export($method->getDocComment(), 1));
// Affichage des variables statiques si elles existent if ($statics= $method->getStaticVariables()) { printf("---> Variales statiques : %s\n", var_export($statics, 1)); }
// Invocation de la méthode printf("---> Résultat de l'invocation dans : "); var_dump($method->invoke(NULL)); ?>
|
|
Note :
Invoquer des méthodes privées, protégées ou abstraites provoquera
une exception jetée par la méthode invoke().
Note :
Pour les méthodes statiques comme vu précédemment, vous devez passer
NULL comme premier argument à la fonction invoke().
Pour les méthodes non-statiques, passez une instance de la classe.
La classe ReflectionProperty vous permet
de faire du reverse-engineer sur les propriétés des
classes.
Pour connaître le fonctionnement d'une méthode, vous devez d'abord créer une instance
de la classe ReflectionProperty. Vous pourrez ainsi
appeler n'importe quelles méthodes de cette instance.
Exemple 18-25. Utilisation de la classe ReflectionProperty
<?php class String { public $length = 5; }
// Création d'une instance de la classe ReflectionProperty $prop = new ReflectionProperty('String', 'length');
// Affichage d'informations basiques printf( "===> Les propriétés %s%s%s%s '%s' (qui a %s)\n". " a les modificateurs %s\n", $prop->isPublic() ? ' public' : '', $prop->isPrivate() ? ' private' : '', $prop->isProtected() ? ' protected' : '', $prop->isStatic() ? ' static' : '', $prop->getName(), $prop->isDefault() ? 'déclaré au moment de la compilation' : 'créé au moment de l\'exécution', var_export(Reflection::getModifierNames($prop->getModifiers()), 1) );
// Création d'une instance de String $obj= new String();
// Récupération de la valeur courante printf("---> La veleur est : "); var_dump($prop->getValue($obj));
// Modification de la valeur $prop->setValue($obj, 10); printf("---> Définition de la valeur à 10, la nouvelle valeur est : "); var_dump($prop->getValue($obj));
// Affichage de l'objet var_dump($obj); ?>
|
|
Note :
Essayer de récupérer ou définir les valeurs des propriétés d'une classe
privée ou protégée produira une exception.
La classe ReflectionExtension vous permet de faire du
reverse-engineer sur les extensions. Vous pouvez retrouver toutes
les extensions chargées à l'exécution en utilisation la fonction
get_loaded_extensions().
Pour connaître le fonctionnement d'une méthode, vous devez d'abord créer une instance
de la classe ReflectionProperty. Vous pourrez ainsi
appeler n'importe quelles méthodes de cette instance.
Exemple 18-26. Utilisation de la classe ReflectionExtension
<?php // Création d'une instance de la classe ReflectionProperty $ext = new ReflectionExtension('standard');
// Affichage d'informations basiques printf( "Nom : %s\n". "Version : %s\n". "Fonctions : [%d] %s\n". "Constantes : [%d] %s\n". "Entrées INI : [%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) ); ?>
|
|
Dans le cas où vous voudriez créer des versions spéciales des classes
embarquées (par exemple pour créer du HTML colorisé lorsqu'il est exporté,
pour avoir un accès facile aux variables des membres au lieu des méthodes ou
pour avoir des méthodes utiles), vous devez étendre la classe.
Exemple 18-27. Extension des classes embarquées
<?php /** * Ma classe Reflection_Method * */ class My_Reflection_Method extends ReflectionMethod { public $visibility= '';
public function __construct($o, $m) { parent::__construct($o, $m); $this->visibility= Reflection::getModifierNames($this->getModifiers()); } }
/** * Démo classe #1 * */ class T { protected function x() {} }
/** * Démo classe #2 * */ class U extends T { function x() {} }
// Affichage des informations var_dump(new My_Reflection_Method('U', 'x')); ?>
|
|
Note :
Attention : si vous écrasez le constructeur, n'oubliez
pas d'appeler le constructeur parent avant d'insérer le
moindre code. Sinon, votre code produira l'erreur suivante :
Fatal error: Internal error: Failed to retrieve the reflection object