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 :

<?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 { }
?>

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 #1 Utilisation basique de l'API de r�flexion

<?php
Reflection
::export(new ReflectionClass('Exception'));
?>

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 [ <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 est une interface impl�ment�e par toutes les classes exportables de r�flection.

<?php
interface Reflector
{
    public 
string __toString()
    public static 
string export()
}
?>

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.

<?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 
mixed getClosure() /* Depuis PHP 5.3.0 */
    
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()
}
?>

La classe parent ReflectionFunctionAbstract a les m�mes m�thodes, except�e invoke(), invokeArgs(), export() et isDisabled().

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.

Exemple #2 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 ReflectionFunction
$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($statics1));
}

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

<?php
class ReflectionParameter implements Reflector
{
    final private 
__clone()
    public 
void __construct(string function, string parameter)
    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()
    public 
int getPosition()
}
?>

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. getDeclaringFunction() et getPosition() ont �t� ajout�s en PHP 5.2.3.

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 #3 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 ReflectionFunction 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->getPosition() peut �tre utilis� depuis PHP 5.2.3
        
$param->getName(),
        
var_export($param->getClass(), 1),
        
var_export($param->allowsNull(), 1),
        
var_export($param->isPassedByReference(), 1),
        
$param->isOptional() ? 'oui' 'non'
    
);
}
?>

ReflectionClass

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

<?php
class ReflectionClass implements Reflector
{
    final private 
__clone()
    public 
void __construct(string name)
    public 
string __toString()
    public static 
string export(mixed class, bool return)
    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()
}
?>

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.

Exemple #4 Utilisation de la classe ReflectionClass

<?php
interface Linearisable
{
    
// ...
}

class 
Object
{
    
// ...
}

/**
 * Une classe compteur
 */
class Compteur extends Object implements Linearisable
{
    const 
START 0;
    private static 
$c Compteur::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('Compteur');

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

<?php
class ReflectionObject extends ReflectionClass
{
    final private 
__clone()
    public 
void __construct(mixed object)
    public 
string __toString()
    public static 
string export(mixed objectbool return)
}
?>

ReflectionMethod

La classe ReflectionMethod vous permet de faire du reverse-engineering sur les m�thodes des 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 
mixed 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 
mixed getClosure() /* Depuis PHP 5.3.0 */
    
public ReflectionClass getDeclaringClass()

    
// Inherited from 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()
}
?>

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.

Exemple #5 Utilisation de la classe ReflectionMethod

<?php
class Compteur
{
    private static 
$c 0;

    
/**
     * Incr�mentation d'un compteur
     *
     * @final
     * @static
     * @access  public
     * @return  int
     */
    
final public static function increment()
    {
        return 
self::$c;
    }
}

// Cr�ation d'une instance de la classe ReflectionMethod
$method = new ReflectionMethod('Compteur''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($statics1));
}

// Invocation de la m�thode
printf("---> R�sultat de l'invocation dans : ");
var_dump($method->invoke(NULL));
?>

ReflectionProperty

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

<?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 
void setAccessible() /* Depuis PHP 5.3.0 */
    
public int getModifiers()
    public 
mixed getValue(stdclass object)
    public 
void setValue(stdclass objectmixed value)
    public 
ReflectionClass getDeclaringClass()
    public 
string getDocComment()
}
?>

Note: getDocComment() a �t� ajout� en PHP 5.1.0. setAccessible() a �t� ajout� en PHP 5.3.0.

Pour conna�tre le fonctionnement d'une propri�t�, vous devez d'abord cr�er une instance de la classe ReflectionProperty. Vous pourrez ainsi appeler n'importe quelle m�thode de cette instance.

Exemple #6 Utilisation de la classe ReflectionProperty

<?php
class Chaine
{
    public 
$length  5;
}

// Cr�ation d'une instance de la classe ReflectionProperty
$prop = new ReflectionProperty('Chaine''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 Chaine
$obj= new Chaine();

// R�cup�ration de la valeur courante
printf("---> La veleur est : ");
var_dump($prop->getValue($obj));

// Modification de la valeur
$prop->setValue($obj10);
printf("---> D�finition de la valeur � 10, la nouvelle valeur est : ");
var_dump($prop->getValue($obj));

// Affichage de l'objet
var_dump($obj);
?>

Exemple #7 Obtenir la valeur d'une propri�t� priv�e et prot�g�e en utilisant la classe ReflectionProperty

<?php

class Foo {
    public 
$x 1;
    protected 
$y 2;
    private 
$z 3;
}

$obj = new Foo;

$prop = new ReflectionProperty('Foo''y');
$prop->setAccessible(true); /* Depuis PHP 5.3.0 */
var_dump($prop->getValue($obj)); // int(2)

$prop = new ReflectionProperty('Foo''z');
$prop->setAccessible(true); /* Depuis PHP 5.3.0 */
var_dump($prop->getValue($obj)); // int(2)

?>

Note: Essayer de r�cup�rer ou de d�finir les valeurs des propri�t�s d'une classe priv�e ou prot�g�e produira une exception.

ReflectionExtension

La classe ReflectionExtension vous permet de faire du reverse-engineering sur les extensions. Vous pouvez conna�tre toutes les extensions charg�es � l'ex�cution en utilisation la fonction 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()
}
?>

Pour conna�tre le fonctionnement d'une extension, vous devez d'abord cr�er une instance de la classe ReflectionExtension. Vous pourrez ainsi appeler n'importe quelle m�thode sur cette instance.

Exemple #8 Utilisation de la classe ReflectionExtension

<?php
// Cr�ation d'une instance de la classe ReflectionExtension
$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" .
    
"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)
);
?>

Extension des classes de r�flexion

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 #9 Extension des classes embarqu�es

<?php
/**
 * Ma classe Reflection_Method
 */
class My_Reflection_Method extends ReflectionMethod
{
  public 
$visibility = array();

  public function 
__construct($o$m)
  {
    
parent::__construct($o$m);
    
$this->visibility Reflection::getModifierNames($this->getModifiers());
  }
}

/**
 * D�mo classe #1
 *
 */
class {
  protected function 
x() {}
}

/**
 * D�mo classe #2
 *
 */
class extends {
  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