Manuel PHP

Réflexion

Introduction

PHP 5 introduit API de réflexion complète qui permet de faire du reverse-engineering 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 :

Example#1 API de réflexion

  1. <?php
  2. class Reflection { }  
  3. interface Reflector { }  
  4. class ReflectionException extends Exception { }  
  5. class ReflectionFunction extends ReflectionFunctionAbstract implements Reflector { }  
  6. class ReflectionParameter implements Reflector { }  
  7. class ReflectionMethod extends ReflectionFunctionAbstract implements Reflector { }  
  8. class ReflectionClass implements Reflector { }  
  9. class ReflectionObject extends ReflectionClass { }  
  10. class ReflectionProperty implements Reflector { }  
  11. class ReflectionExtension implements Reflector { }  
  12. ?> 

Note: Pour plus de détails sur ces classes, lisez les chapitres suivants.

Si nous exécutons le code de l'exemple ci-dessous :

Example#2 Utilisation basique de l'API de réflexion

  1. <?php
  2. Reflection::export(new ReflectionClass('Exception'));  
  3. ?> 

L'exemple ci-dessus va afficher :

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 étend le standard Exception et est lancé par l'API Reflection. Aucune méthode spécifique ni de propriété ne sont introduites.

ReflectionFunction

La classe ReflectionFunction vous permet de faire du reverse-engineering sur les fonctions.

Example#3 La classe ReflectionFunction

  1. <?php
  2. class ReflectionFunction extends ReflectionFunctionAbstract implements Reflector  
  3. { 
  4.    final private __clone() 
  5.    public object __construct(string name) 
  6.    public string __toString() 
  7.    public static string export(string name, bool return) 
  8.    public string getName() 
  9.    public bool isInternal() 
  10.    public bool isUserDefined() 
  11.    public string getFileName() 
  12.    public int getStartLine() 
  13.    public int getEndLine() 
  14.    public string getDocComment() 
  15.    public array getStaticVariables() 
  16.    public mixed invoke(mixed args) 
  17.    public mixed invokeArgs(array args) 
  18.    public bool returnsReference() 
  19.    public ReflectionParameter[] getParameters() 
  20.    public int getNumberOfParameters() 
  21.    public int getNumberOfRequiredParameters()  
  22. }  
  23. ?> 

Note: getNumberOfParameters() et getNumberOfRequiredParameters() ont été ajoutés en PHP 5.0.3, tandis que invokeArgs() a été ajouté en PHP 5.1.0.

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.

Example#4 Utilisation de la classe ReflectionFunction

  1. <?php
  2. /**
  3. * Un simple compteur
  4. *
  5. * @return   int
  6. */  
  7. function counter()  
  8. { 
  9.    static $c = 0; 
  10.  
  11.    return $c++;  
  12. }  
  13.  
  14. // Création d'une instance de la classe Reflection_Function
  15. $func = new ReflectionFunction('counter');  
  16.  
  17. // Affichage d'informations basiques
  18. printf( 
  19.    "===> The %s function '%s'\n". 
  20.    "    declared in %s\n". 
  21.    "    lines %d to %d\n", 
  22.    $func->isInternal() ? 'internal' : 'user-defined', 
  23.    $func->getName(), 
  24.    $func->getFileName(), 
  25.    $func->getStartLine(), 
  26.    $func->getEndline()  
  27. );  
  28.  
  29. // Affichage du commentaire de la documentation
  30. printf("---> Documentation:\n %s\n", var_export($func->getDocComment(), 1));  
  31.  
  32. // Affichage des variables statiques si elles existent
  33. if ($statics = $func->getStaticVariables())  
  34. { 
  35.    printf("---> Variables statiques : %s\n", var_export($statics, 1));  
  36. }  
  37.  
  38. // Appel de la fonction
  39. printf("---> Invocation des résultats dans : ");  
  40. var_dump($func->invoke());  
  41.  
  42.  
  43. // vous pouvez préférer utiliser la méthode export()
  44. echo "\nRésultat de ReflectionFunction::export() :\n";  
  45. echo ReflectionFunction::export('counter');  
  46. ?> 

Note: La méthode invoke() accepte un nombre variable d'arguments, tout comme la fonction call_user_func().

ReflectionParameter

La classe ReflectionParameter récupère les informations concernant les paramètres des fonctions ou des méthodes.

Example#5 La classe ReflectionParameter

  1. <?php
  2. class ReflectionParameter implements Reflector  
  3. { 
  4.    final private __clone() 
  5.    public object __construct(string function, string parameter) 
  6.    public string __toString() 
  7.    public static string export(mixed function, mixed parameter, bool return) 
  8.    public string getName() 
  9.    public bool isPassedByReference() 
  10.    public ReflectionClass getDeclaringClass() 
  11.    public ReflectionClass getClass() 
  12.    public bool isArray() 
  13.    public bool allowsNull() 
  14.    public bool isPassedByReference() 
  15.    public bool isOptional() 
  16.    public bool isDefaultValueAvailable() 
  17.    public mixed getDefaultValue()  
  18. }  
  19. ?> 

Note: getDefaultValue(), isDefaultValueAvailable() et isOptional() ont été ajoutés en PHP 5.0.3, tandis que isArray() a été ajoutée 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.

Example#6 Utilisation de la classe ReflectionParameter

  1. <?php
  2. function foo($a, $b, $c) { }  
  3. function bar(Exception $a, &$b, $c) { }  
  4. function baz(ReflectionFunction $a, $b = 1, $c = null) { }  
  5. function abc() { }  
  6.  
  7. // Création d'une instance de la classe Reflection_Function avec le
  8. // paramètre fourni en ligne de commande.
  9. $reflect = new ReflectionFunction($argv[1]);  
  10.  
  11. echo $reflect;  
  12.  
  13. foreach ($reflect->getParameters() as $i => $param) { 
  14.    printf( 
  15.       "-- Paramètre #%d : %s {\n". 
  16.       " Classe : %s\n". 
  17.       " Autorise NULL : %s\n". 
  18.       " Passé par référence : %s\n". 
  19.       " Est optionnel ?: %s\n". 
  20.       "}\n", 
  21.       $i, 
  22.       $param->getName(), 
  23.       var_export($param->getClass(), 1), 
  24.       var_export($param->allowsNull(), 1), 
  25.       var_export($param->isPassedByReference(), 1), 
  26.       $param->isOptional() ? 'oui' : 'non' 
  27.    );  
  28. }  
  29. ?> 

ReflectionClass

La classe ReflectionClass vous permet de faire du reverse-engineering sur des classes.

Example#7 La classe ReflectionClass

  1. <?php
  2. class ReflectionClass implements Reflector  
  3. { 
  4.    final private __clone() 
  5.    public object __construct(string name) 
  6.    public string __toString() 
  7.    public static string export(mixed class, bool return) 
  8.    public string getName() 
  9.    public bool isInternal() 
  10.    public bool isUserDefined() 
  11.    public bool isInstantiable() 
  12.    public bool hasConstant(string name) 
  13.    public bool hasMethod(string name) 
  14.    public bool hasProperty(string name) 
  15.    public string getFileName() 
  16.    public int getStartLine() 
  17.    public int getEndLine() 
  18.    public string getDocComment() 
  19.    public ReflectionMethod getConstructor() 
  20.    public ReflectionMethod getMethod(string name) 
  21.    public ReflectionMethod[] getMethods() 
  22.    public ReflectionProperty getProperty(string name) 
  23.    public ReflectionProperty[] getProperties() 
  24.    public array getConstants() 
  25.    public mixed getConstant(string name) 
  26.    public ReflectionClass[] getInterfaces() 
  27.    public bool isInterface() 
  28.    public bool isAbstract() 
  29.    public bool isFinal() 
  30.    public int getModifiers() 
  31.    public bool isInstance(stdclass object) 
  32.    public stdclass newInstance(mixed args) 
  33.    public stdclass newInstanceArgs(array args) 
  34.    public ReflectionClass getParentClass() 
  35.    public bool isSubclassOf(ReflectionClass class) 
  36.    public array getStaticProperties() 
  37.    public mixed getStaticPropertyValue(string name [, mixed default]) 
  38.    public void setStaticPropertyValue(string name, mixed value) 
  39.    public array getDefaultProperties() 
  40.    public bool isIterateable() 
  41.    public bool implementsInterface(string name) 
  42.    public ReflectionExtension getExtension() 
  43.    public string getExtensionName()  
  44. }  
  45. ?> 

Note: hasConstant(), hasMethod(), hasProperty(), getStaticPropertyValue() et setStaticPropertyValue() ont été ajoutées en PHP 5.1.0, tandis que newInstanceArgs() a été ajoutée dans PHP 5.1.3.

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 quelle méthode sur cette instance.

Example#8 Utilisation de la classe ReflectionClass

  1. <?php
  2. interface Linearisable  
  3. { 
  4.    // ...
  5. }  
  6.  
  7. class Object  
  8. { 
  9.    // ...
  10. }  
  11.  
  12. /**
  13. * Une classe compteur
  14. *
  15. */  
  16. class Compteur extends Object implements Linearisable  
  17. { 
  18.    const START = 0; 
  19.    private static $c = Compteur::START; 
  20.  
  21.    /**
  22.     * Invocation du compteur
  23.     *
  24.     * @access public
  25.     * @return int
  26.     */ 
  27.    public function count() 
  28.    { 
  29.       return self::$c++; 
  30.    }  
  31. }  
  32.  
  33. // Création d'une instance de la classe ReflectionClass
  34. $class = new ReflectionClass('Compteur');  
  35.  
  36. // Affichage d'informations basiques
  37. printf( 
  38.    "===> La %s%s%s %s '%s' [extension de %s]\n". 
  39.    "    déclarée dans %s\n". 
  40.    "    lignes %d à %d\n". 
  41.    "    a le modificateur %d [%s]\n", 
  42.    $class->isInternal() ? 'internal' : 'user-defined', 
  43.    $class->isAbstract() ? ' abstract' : '', 
  44.    $class->isFinal() ? ' final' : '', 
  45.    $class->isInterface() ? 'interface' : 'class', 
  46.    $class->getName(), 
  47.    var_export($class->getParentClass(), 1), 
  48.    $class->getFileName(), 
  49.    $class->getStartLine(), 
  50.    $class->getEndline(), 
  51.    $class->getModifiers(), 
  52.    implode(' ', Reflection::getModifierNames($class->getModifiers()))  
  53. );  
  54.  
  55. // Affichage du commentaire de la documentation
  56. printf("---> Documentation:\n %s\n", var_export($class->getDocComment(), 1));  
  57.  
  58. // Affichage de l'interface qui implémente cette classe
  59. printf("---> Implémenté :\n %s\n", var_export($class->getInterfaces(), 1));  
  60.  
  61. // Affichage des constantes de la classe
  62. printf("---> Constantes : %s\n", var_export($class->getConstants(), 1));  
  63.  
  64. // Affichage des propriétés de la classe
  65. printf("---> Properties: %s\n", var_export($class->getProperties(), 1));  
  66.  
  67. // Affichage des méthodes de la classe
  68. printf("---> Méthodes : %s\n", var_export($class->getMethods(), 1));  
  69.  
  70. // Si cette classe est instanciable, création d'une instance
  71. if ($class->isInstantiable()) { 
  72.    $counter = $class->newInstance(); 
  73.  
  74.    echo '---> $counter est uneinstance ? '; 
  75.    echo $class->isInstance($counter) ? 'oui' : 'non'; 
  76.  
  77.    echo "\n---> Le nouvel objet Object() est une instance ? "; 
  78.    echo $class->isInstance(new Object()) ? 'oui' : 'non';  
  79. }  
  80. ?> 

Note: La méthode newinstance() accepte un nombre variable d'arguments, 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').

ReflectionObject

La classe ReflectionObject vous permet de retrouver les objets.

  1. <?php
  2. class ReflectionObject extends ReflectionClass  
  3. { 
  4.    final private __clone() 
  5.    public object __construct(mixed object) 
  6.    public string __toString() 
  7.    public static string export(mixed object, bool return)  
  8. }  
  9. ?> 

ReflectionMethod

La classe ReflectionMethod vous permet de faire du reverse-engineering sur les méthodes des classes.

Example#9 La classe ReflectionMethod

  1. <?php
  2. class ReflectionMethod extends ReflectionFunctionAbstract implements Reflector  
  3. { 
  4.    public __construct(mixed class, string name) 
  5.    public string __toString() 
  6.    public static string export(mixed class, string name, bool return) 
  7.    public mixed invoke(stdclass object, mixed args) 
  8.    public mixed invokeArgs(stdclass object, array args) 
  9.    public bool isFinal() 
  10.    public bool isAbstract() 
  11.    public bool isPublic() 
  12.    public bool isPrivate() 
  13.    public bool isProtected() 
  14.    public bool isStatic() 
  15.    public bool isConstructor() 
  16.    public bool isDestructor() 
  17.    public int getModifiers() 
  18.    public ReflectionClass getDeclaringClass() 
  19.  
  20.    // Inherited from ReflectionFunction
  21.    final private __clone() 
  22.    public string getName() 
  23.    public bool isInternal() 
  24.    public bool isUserDefined() 
  25.    public string getFileName() 
  26.    public int getStartLine() 
  27.    public int getEndLine() 
  28.    public string getDocComment() 
  29.    public array getStaticVariables() 
  30.    public bool returnsReference() 
  31.    public ReflectionParameter[] getParameters() 
  32.    public int getNumberOfParameters() 
  33.    public int getNumberOfRequiredParameters()  
  34. }  
  35. ?> 

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 quelle méthode de cette instance.

Example#10 Utilisation de la classe ReflectionMethod

  1. <?php
  2. class Compteur  
  3. { 
  4.    private static $c = 0; 
  5.  
  6.    /**
  7.     * Incrémentation d'un compteur
  8.     *
  9.     * @final
  10.     * @static
  11.     * @access public
  12.     * @return int
  13.     */ 
  14.    final public static function increment() 
  15.    { 
  16.       return self::$c; 
  17.    }  
  18. }  
  19.  
  20. // Création d'une instance de la classe Reflection_Method
  21. $method = new ReflectionMethod('Compteur', 'increment');  
  22.  
  23. // Affichage d'informations basiques
  24. printf( 
  25.    "===> La méthode %s%s%s%s%s%s%s '%s' (qui est %s)\n". 
  26.    "    déclaré dans %s\n". 
  27.    "    lignes %d à %d\n". 
  28.    "    a les modificateurs %d[%s]\n", 
  29.    $method->isInternal() ? 'internal' : 'user-defined', 
  30.    $method->isAbstract() ? ' abstract' : '', 
  31.    $method->isFinal() ? ' final' : '', 
  32.    $method->isPublic() ? ' public' : '', 
  33.    $method->isPrivate() ? ' private' : '', 
  34.    $method->isProtected() ? ' protected' : '', 
  35.    $method->isStatic() ? ' static' : '', 
  36.    $method->getName(), 
  37.    $method->isConstructor() ? 'the constructor' : 'a regular method', 
  38.    $method->getFileName(), 
  39.    $method->getStartLine(), 
  40.    $method->getEndline(), 
  41.    $method->getModifiers(), 
  42.    implode(' ', Reflection::getModifierNames($method->getModifiers()))  
  43. );  
  44.  
  45. // Affichage du commentaire de la documentation
  46. printf("---> Documentation:\n %s\n", var_export($method->getDocComment(), 1));  
  47.  
  48. // Affichage des variables statiques si elles existent
  49. if ($statics= $method->getStaticVariables()) { 
  50.    printf("---> Variales statiques : %s\n", var_export($statics, 1));  
  51. }  
  52.  
  53. // Invocation de la méthode
  54. printf("---> Résultat de l'invocation dans : ");  
  55. var_dump($method->invoke(NULL));  
  56. ?> 

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.

ReflectionProperty

La classe ReflectionProperty vous permet de faire du reverse-engineering sur les propriétés des classes.

Example#11 La classe ReflectionProperty

  1. <?php
  2. class ReflectionProperty implements Reflector  
  3. { 
  4.    final private __clone() 
  5.    public __construct(mixed class, string name) 
  6.    public string __toString() 
  7.    public static string export(mixed class, string name, bool return) 
  8.    public string getName() 
  9.    public bool isPublic()