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 :
Exemple 19.33. API de réflexion
<?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 19.34. 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 [ <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
étend le standard Exception
et est lancé par l'API Reflection. Aucune méthode spécifique ni de propriété ne sont introduites.
La classe ReflectionFunction
vous permet de faire du reverse-engineering
sur les fonctions.
Exemple 19.35. La classe ReflectionFunction
<?php
class
ReflectionFunction
extends
ReflectionFunctionAbstract
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
()
}
?>
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 19.36. 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, 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.
Exemple 19.37. La classe ReflectionParameter
<?php
class
ReflectionParameter
implements
Reflector
{
final private
__clone
()
public
object __construct
(
string
function,
string parameter
)
public
string __toString
()
public static
string export
(
mixed
function,
mixed parameter
,
bool
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
()
}
?>
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.
Exemple 19.38. 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-engineering
sur des classes.
Exemple 19.39. La classe ReflectionClass
<?php
class
ReflectionClass
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 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 name
,
mixed 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 19.40. 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') .
La classe ReflectionObject
vous permet de retrouver les objets.
<?php
class
ReflectionObject
extends
ReflectionClass
{
final private
__clone
()
public
object __construct
(
mixed object
)
public
string __toString
()
public static
string export
(
mixed object
,
bool
return)
}
?>
La classe ReflectionMethod
vous permet de faire du reverse-engineering
sur les méthodes des classes.
Exemple 19.41. La classe ReflectionMethod
<?php
class
ReflectionMethod
extends
ReflectionFunctionAbstract
implements
Reflector
{
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
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
ReflectionClass getDeclaringClass
()
// Inherited from 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
()
}
?>
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 19.42. 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 Reflection_Method
$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
(
$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-engineering
sur les propriétés des classes.
Exemple 19.43. La classe ReflectionProperty
<?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
()
}
?>
Note: getDocComment() a été ajouté en PHP 5.1.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 19.44. 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
(
$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 de 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-engineering
sur les extensions. Vous pouvez connaître toutes les extensions chargées à l'exécution en utilisation la fonction get_loaded_extensions()
.
Exemple 19.45. La classe ReflectionExtension
<?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
()
}
?>
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 19.46. 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"
.
"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
)
);
?>
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 19.47. 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