2. Quelques points divers
Ce dernier chapitre, qui n’en est en fait pas vraiment un, est destiné à regrouper quelques sujets distincts les uns des autres, qui ne se seraient pas en l’état actuel intégrés dans d’autres chapitres de ce livre et ne sont pas assez longs pour constituer des chapitres à part entière, mais dont je tenais à parler – et qui méritent mieux qu’un classement en tant qu’annexe.
Il est possible que les sections qui figurent ici dans la version courante de ce livre soient revues, peut-être en profondeur, dans une prochaine version. Il est même possible que certaines soient intégrées à un autre chapitre, ou disparaissent complètement.
2.1 Déclarer des constantes
Il est possible de définir des constantes depuis du code PHP utilisateur en utilisant l’instruction define(). Bien entendu, une extension peut elle aussi exposer des constantes.
2.1.1 Déclarer une constante
Une constante aura généralement toujours la même valeur – constante. Elle est donc définie, dans une extension PHP, au moment du chargement de celle-ci ; c’est-à-dire au moment de la phase MINIT, depuis la fonction branchée sur celle-ci.
Si une constante doit avoir une valeur qui peut être différente pour chaque requête (mais qui reste la même tout au long du traitement de chaque requête, bien sûr – ça ne serait pas une constante, sinon !), elle peut être définie depuis la fonction branchée sur la phase RINIT de votre extension.
Le plus souvent, une constante sera définie en utilisant une des macros REGISTER_*_CONSTANT() :
PHP_MINIT_FUNCTION(monext)
{
REGISTER_STRING_CONSTANT("MONEXT_CTE_STR_1", "1ère constante",
CONST_PERSISTENT);
REGISTER_STRING_CONSTANT("MONEXT_CTE_STR_2", "2nde constante",
CONST_CS | CONST_PERSISTENT);
return SUCCESS;
}
Pour définir une constante de type chaîne de caractères, ici, nous avons utilisé la macro REGISTER_STRING_CONSTANT(), qui attend trois paramètres :
- le nom de la constante, qui doit être unique, une constante ne pouvant être définie qu’une seule fois,
- la valeur de cette constante,
- et une combinaison de drapeaux.
Trois valeurs peuvent être combinées, pour ce dernier paramètre :
-
CONST_PERSISTENT: indique que la constante doit être persistante, conservée d’une requête à l’autre. Cette valeur sera utilisée pour les constantes définies au moment de la phaseMINIT, et pas pour celles qui seraient définies depuis la phaseRINIT. -
CONST_CS: indique que le nom de la constante est sensible à la casse. En général, un nom de constante se trouve en majuscules et est sensible à la casse, et ce drapeau est donc utilisé. Au niveau des constantes déclarées par PHP, ce flag est activé pour toutes, sauf pourTRUE,FALSE, etNULL, dont les noms sont insensibles à la casse. -
CONST_CT_SUBST: cette valeur n’est que rarement utilisée et permet une optimisation (substitution lors de la compilation d’un script PHP) lorsqueCONST_CSn’est pas utilisée.
À titre d’exemple, voici un script PHP utilisant les deux constantes définies un peu plus haut :
<?php
// Nom de constante insensible à la casse
var_dump(MONEXT_CTE_STR_1);
var_dump(MonExt_cTe_StR_1);
// Nom de constante sensible à la casse
var_dump(MONEXT_CTE_STR_2);
// Erreur
var_dump(MonExt_cTe_StR_2);
?>
En exécutant ce script, voici la sortie que nous obtiendrions :
$ $HOME/bin/php-5.4-debug/bin/php -dextension=modules/monext.so -f ./test.php
string(15) "1ère constante"
string(15) "1ère constante"
string(14) "2nde constante"
PHP Notice: Use of undefined constant MonExt_cTe_StR_2
- assumed 'MonExt_cTe_StR_2' in .../test.php on line 10
Notice: Use of undefined constant MonExt_cTe_StR_2
- assumed 'MonExt_cTe_StR_2' in .../test.php on line 10
string(16) "MonExt_cTe_StR_2"
N’importe quelle combinaison de majuscules et minuscules peut être utilisée pour désigner notre première constante, dont le nom n’est pas sensible à la casse, alors que le nom de la seconde ne peut être écrit qu’en majuscules, puisqu’elle a été déclarée avec le flag CONST_CS.
PHP fournit les macros suivantes pour déclarer des constantes :
-
REGISTER_LONG_CONSTANT(): permet de déclarer une constante entière, -
REGISTER_DOUBLE_CONSTANT(): pour définir une constante en tant que nombre à virgule à flottante, -
REGISTER_STRING_CONSTANT(): nous l’avons utilisée un peu plus haut : elle permet de définir une constante de type chaîne de caractères, -
REGISTER_STRINGL_CONSTANT(): cette dernière macro permet elle aussi de définir une constante chaîne de caractères, mais en spécifiant la longueur de cette chaîne de troisième paramètre, les drapeaux étant alors le quatrième paramètre.
2.1.2 Noms et valeurs dynamiques
Les macros vues juste au-dessus utilisent sizeof() pour déterminer la longueur de la chaîne de caractères correspondant au nom de la constante à définir, ce qui empêche de les utiliser avec un nom de constante qui ne soit pas écrit en dur dans le code, lors de leur appel : il n’est pas possible d’utiliser quelque chose de ce type :
char * nom = "...";
REGISTER_LONG_CONSTANT(nom, 123456, CONST_CS | CONST_PERSISTENT);
sizeof(nom) serait évalué comme le nombre d’octets utilisés pour représenter un pointeur char *, et pas comme le nombre de caractères du nom de la constante.
À la place, pour déclarer une constante dont le nom n’est pas connu à la compilation de l’extension, vous allez devoir directement faire appel aux fonctions qui sont finalement utilisées par ces macros : chaque macro correspond à une fonction nommée sous la forme zend_register_*_constant().
Par exemple, pour définir cinq constantes, toutes de type chaîne de caractères, nommées de MONEXT_CTE_STR_1 à MONEXT_CTE_STR_5, nous pourrions utiliser une portion de code ressemblant à celle-ci :
PHP_MINIT_FUNCTION(monext)
{
int i;
for (i=1 ; i<=5 ; i++)
{
char *nom;
/* longueur = prefixe + longueur(i) + octet null de fin de chaine */
nom = emalloc(strlen("MONEXT_CTE_STR_") + 1 + 1);
char *valeur = pemalloc(strlen("constante n°") + 1 + 1, 1);
sprintf(nom, "MONEXT_CTE_STR_%d", i);
sprintf(valeur, "constante n°%d", i);
zend_register_string_constant(
nom,
strlen(nom) + 1,
valeur,
CONST_CS | CONST_PERSISTENT,
module_number TSRMLS_CC
);
efree(nom);
}
return SUCCESS;
}
Les fonctions zend_register_*_constant() s’utilisent de manière assez similaire à celle des macros correspondantes ; elles attendent quelques paramètres supplémentaires :
- un second paramètre est intercalé après le nom de la constante : la longueur de ce nom. Attention, le caractère nul de fin de chaîne doit être compté – d’où le
strlen() + 1ici, - le numéro de l’extension, reçu par toutes fonctions branchées sur les phases
MINITetRINITlors de l’expansion des macrosPHP_MINIT_FUNCTION()etPHP_RINIT_FUNCTION(), doit être passé après les drapeaux, pour que le moteur PHP sache rattacher la constante à notre extension.
Utilisons le script test.php suivant pour tester l’affichage de nos cinq constantes créées avec des noms et valeurs dynamiques :
<?php
var_dump(MONEXT_CTE_STR_1);
var_dump(MONEXT_CTE_STR_2);
var_dump(MONEXT_CTE_STR_3);
var_dump(MONEXT_CTE_STR_4);
var_dump(MONEXT_CTE_STR_5);
?>
Exécuter ce script nous donnera la sortie reproduite ci-dessous :
$ $HOME/bin/php-5.4-debug/bin/php -dextension=modules/monext.so -f ./test.php
string(14) "constante n°1"
string(14) "constante n°2"
string(14) "constante n°3"
string(14) "constante n°4"
string(14) "constante n°5"
2.1.3 Constantes d’autres types
Les macros REGISTER_*_CONSTANT() et fonctions zend_register_*_constant() correspondantes que nous avons vues jusqu’à présent permettent de définir des constantes de types numériques entier et flottant et chaîne de caractères ; mais il est tout à fait possible de créer des constantes manuellement en construisant une variable de type zend_constant1, qui est une structure définie comme suit :
typedef struct _zend_constant {
zval value;
int flags;
char *name;
uint name_len;
int module_number;
} zend_constant;
Ensuite, à nous de renseigner ces champs un par un et d’utiliser la fonction zend_register_constant() pour enregistrer la constante auprès du moteur de PHP.
Par exemple, pour définir une constante de type booléen, nous pourrions utiliser une portion de code similaire à celle-ci :
PHP_MINIT_FUNCTION(monext)
{
/* Définition d’une constante booléenne */
zend_constant c;
c.flags = CONST_CS | CONST_PERSISTENT | CONST_CT_SUBST;
c.module_number = module_number;
c.name = zend_strndup(ZEND_STRL("MONEXT_CTE_BOOL"));
c.name_len = sizeof("MONEXT_CTE_BOOL");
c.value.value.lval = 1;
c.value.type = IS_BOOL;
zend_register_constant(&c TSRMLS_CC);
return SUCCESS;
}
La constante est ensuite accessible, comme celles créées précédemment, depuis un script PHP :
<?php
var_dump(MONEXT_CTE_BOOL);
?>
Et l’exécution de ce script donne la sortie attendue :
$ $HOME/bin/php-5.4-debug/bin/php -dextension=modules/monext.so -f ./test.php
bool(true)
2.1.4 Lire la valeur d’une constante
La valeur d’une constante peut être lue, depuis une extension PHP, en appelant la fonction zend_get_constant(), qui prend en paramètres le nom de celle-ci et un pointeur vers une zval qui permettra de stocker une copie de la valeur recherchée.
Par exemple, nous pourrions écrire la fonction suivante, qui prend elle-même en paramètre le nom d’une constante à lire et affiche la valeur correspondante :
PHP_FUNCTION(monext_get_constant)
{
/* zval vers laquelle sera stockée la valeur de la constante */
zval val;
char *name;
int name_len;
/* Nom de la constante, passé en paramètre */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
&name, &name_len) == FAILURE) {
return;
}
if (zend_get_constant(name, name_len, &val TSRMLS_CC))
{
/* Pour affichage, il est plus facile d’avoir une chaine de caractères */
convert_to_string(&val);
PUTS("Constante ");
PHPWRITE(name, name_len);
PUTS(" : ");
PHPWRITE(Z_STRVAL(val), Z_STRLEN(val));
PUTS("\n");
zval_dtor(&val);
}
}
Notez que, en vue de faciliter l’affichage, nous avons converti en chaîne de caractères la zval renseignée par zend_get_constant(). Cette zval correspondant à une copie de la valeur de la constante et non directement à cette valeur, nous pouvons la manipuler comme bon nous semble.
Notre fonction monext_get_constant() peut être appelée depuis PHP, pour afficher la valeur d’une constante définie par notre extension (cf plus haut), d’une constante définie depuis le script PHP, ou d’une constante inexistante :
define('PHP_CTE_DOUBLE_1', 3.1415);
monext_get_constant('MONEXT_CTE_STR_1');
monext_get_constant('PHP_CTE_DOUBLE_1');
monext_get_constant('CONSTANTE_INEXISTANTE');
La sortie obtenue en exécutant ces quelques lignes sera la suivante :
Constante MONEXT_CTE_STR_1 : Constante depuis extension
Constante PHP_CTE_DOUBLE_1 : 3.1415
La fonction zend_get_constant() est capable de lire aussi bien les valeurs de constantes définies depuis PHP que depuis une extension, et n’affiche pas d’avertissement en cas de constante inexistante. Elle retourne 1 en cas de succès et 0 en cas d’échec.
PHP fournit aussi la fonction zend_get_constant_ex() qui va un peu plus loin, en permettant de spécifier la classe à laquelle une constante doit appartenir, ainsi qu’une série d’options.
2.1.5 Constantes namespacées
Pour déclarer des constantes dans un espace de noms, PHP fournit les macros REGISTER_NS_*_CONSTANT().
Ces macros font appel aux mêmes fonctions que REGISTER_*_CONSTANT(), à la différence près que le nom de la constante est calculé à l’aide de la macro ZEND_NS_NAME() – qui concatène le nom de l’espace de noms et le nom de la constante, en intercalant entre les deux le séparateur d’espaces de noms.
2.2 Variables super-globales
PHP inclut un mécanisme de variables appelées super-globales, qui sont accessibles depuis n’importe quelle fonction, sans avoir à être déclarées manuellement comme globales à l’aide du mot-clef global. Ces variables, traditionnellement, ont un nom en majuscules, qui commence par un underscore, comme $_POST, $_GET, ou $_FILES.
Une super-globale doit être connue du moteur de PHP avant que celui-ci ne commence à exécuter un script. En conséquence, il n’est pas possible d’en déclarer depuis du code PHP utilisateur. Par contre, une extension peut tout à fait créer des super-globales – d’ailleurs, $_SESSION est définie par l’extension ext/session !
2.2.1 Déclarer une variable super-globale
Déclarer une variable super-globale se fait depuis la fonction de votre extension branchée sur la phase MINIT de PHP, en appelant zend_register_auto_global() :
PHP_MINIT_FUNCTION(monext)
{
zend_register_auto_global("_MAVAR", sizeof("_MAVAR") - 1, 0, NULL TSRMLS_CC);
return SUCCESS;
}
Cette fonction attend en paramètres :
- le nom de la variable super-globale,
- la longueur de ce nom – sans compter le caractère nul de fin de chaîne,
- un paramètre booléen indiquant si la variable doit être initialisée “juste à temps” (JIT : Just In Time) ; spécifions pour l’instant
0et nous verrons un peu plus loin ce que ce paramètre permet lorsqu’on lui passe1, - et finalement et optionnellement, une fonction qui sera appelée lors de la phase de compilation du script PHP, à chaque fois que le nom de la variable sera rencontré.
Cette déclaration aurait aussi pu s’écrire en appelant la macro ZEND_STRL(), plutôt que d’écrire manuellement les deux premiers paramètres :
zend_register_auto_global(ZEND_STRL("_MAVAR"), 0, NULL TSRMLS_CC);
Pour illustrer l’utilisation de cette variable super-globale, faisons appel au script test.php suivant :
<?php
ma_fonction_init();
ma_fonction_affiche();
function ma_fonction_init()
{
// Initialisation de la variable,
// sans la spécifier comme "global"
$_MAVAR = "Bonjour, Monde !";
}
function ma_fonction_affiche()
{
// Utilisation de la variable,
// toujours sans la noter "global"
var_dump($_MAVAR);
}
?>
Ce script utilise la variable $_MAVAR depuis deux fonctions, une fois en écriture et une fois en lecture, sans jamais utiliser le mot-clef global ni le tableau $GLOBALS. Ceci ne fonctionne qu’avec une variable super-globale et ne serait pas possible avec une variable normale, chaque fonction ayant son espace de variables propre.
Exécuter cette portion de code donnera la sortie reproduite ci-dessous :
$ $HOME/bin/php-5.4-debug/bin/php -dextension=modules/monext.so -f ./test.php
string(16) "Bonjour, Monde !"
La première fonction a renseigné une valeur dans notre variable, et la seconde a pu la lire – nous avons donc bien créé une variable super-globale !
2.2.2 Détecter les utilisations d’une super-globale
Plus haut, nous avons passé NULL en quatrième paramètre à la fonction zend_register_auto_global(). Celle-ci attend, pour ce quatrième paramètre optionnel, une fonction qui sera appelée lors de la compilation des scripts PHP, à chaque fois que la variable super-globale sera rencontrée.
En reprenant le code écrit précédemment, nous pouvons le modifier de la manière suivante :
PHP_MINIT_FUNCTION(monext)
{
zend_register_auto_global("_MAVAR", sizeof("_MAVAR") - 1, 0,
php_monext_auto_globals_create_mavar TSRMLS_CC);
return SUCCESS;
}
Déclarons ensuite la fonction correspondante : elle reçoit en paramètres le nom de la variable super-globale et la longueur de ce nom, et retourne un booléen.
Ici, nous utilisons cette fonction pour initialiser notre variable $_MAVAR, en lui faisant correspondre un tableau associatif qui contiendra un élément :
static zend_bool php_monext_auto_globals_create_mavar(const char *name,
uint name_len TSRMLS_DC)
{
zval *valeur;
php_printf("CREATE_MAVAR\n");
ALLOC_ZVAL(valeur);
array_init(valeur);
INIT_PZVAL(valeur);
add_assoc_string(valeur, "plop", "Bonjour", 1);
zend_hash_update(&EG(symbol_table), "_MAVAR", sizeof("_MAVAR"),
&valeur, sizeof(zval *), NULL);
return 0;
}
Si cette fonction retourne 1, alors, elle sera à nouveau appelée si la variable super-globale est à nouveau rencontrée plus loin dans le script PHP, lors de sa phase de compilation. Par contre, si cette fonction a retourné 0, alors, elle ne sera plus appelée.
Pour tester cette modification apportée à notre extension, utilisons le script test.php suivant :
<?php
ma_fonction();
function ma_fonction()
{
var_dump($_MAVAR);
}
?>
Puisque, désormais, notre extension initialise la variable $_MAVAR, exécuter ce script PHP donnera la sortie suivante :
$ $HOME/bin/php-5.4-debug/bin/php -dextension=modules/monext.so -f ./test.php
array(1) {
["plop"]=>
string(7) "Bonjour"
}
Autrement dit, nous pouvons à présent, depuis du code utilisateur, accéder à la variable super-globale en lecture sans avoir auparavant écrit dedans : ceci est maintenant fait directement depuis l’extension !
2.2.3 Et avec JIT ?
Jusqu’à présent, lorsque nous avons appelé zend_register_auto_global() en lui passant en paramètre un nom de fonction à appeler lorsque la variable serait rencontrée lors de la phase de compilation d’un script PHP, nous avons toujours passé la valeur 0 en troisième paramètre.
Ce troisième paramètre est nommé jit (JIT, Just In Time – Juste à Temps). Si nous passons 0 comme nous l’avons fait à présent :
- la fonction sera appelée une première fois,
- puis elle sera appelée à chaque fois que la variable sera rencontrée lors de la compilation du script PHP, tant que la fonction n’a pas retourné
0pour désactiver ces appels.
Dans le cas où la variable globale n’est pas du tout utilisée dans le script PHP exécuté, la fonction sera jouée une première fois, typiquement pour initialiser la variable – alors qu’il y a de fortes chances que cela soit complètement inutile (puisque la variable en question ne figure même pas dans le script).
Par exemple, modifions un peu notre extension et notre script PHP pour :
- que l’extension affiche un message depuis la fonction branchée sur la phase
MINITet depuis la fonction qui initialise la variable super-globale (la fonction de rappel branchée en quatrième paramètre lors de la déclaration de celle-ci), - et que le script PHP affiche
AVANTetAPRESautour de l’appel dema_fonction()
Exécuter ce script avec l’extension affichant plus d’informations de débogage donnera la sortie suivante :
$ $HOME/bin/php-5.4-debug/bin/php -dextension=modules/monext.so -f ./test.php
MINIT
CREATE_MAVAR
AVANT
array(1) {
["plop"]=>
string(7) "Bonjour"
}
APRES
Ici, la variable super-globale est utilisée dans le script PHP et le paramètre jit est passé à 0 comme précédemment. La fonction utilisée pour initialiser la super-globale est donc appelée.
Par contre, si le code de l’extension est modifié pour que le paramètre jit soit passé à 1, alors, la fonction de rappel ne sera appelée que si la variable super-globale figure effectivement dans le code du script PHP.
Autrement dit, si le nom de la variable $_MAVAR ne figure pas du tout dans le script PHP, la sortie obtenue sera la suivante :
$ $HOME/bin/php-5.4-debug/bin/php -dextension=modules/monext.so -f ./test.php
MINIT
AVANT
APRES
Et si la variable est présente dans le code PHP au moins une fois, nous retrouvons la même sortie que précédemment, lorsque le JIT était désactivé :
$ $HOME/bin/php-5.4-debug/bin/php -dextension=modules/monext.so -f ./test.php
MINIT
CREATE_MAVAR
AVANT
array(1) {
["plop"]=>
string(7) "Bonjour"
}
APRES
Et, pour insister un peu sur ce que je disais plus haut, si la variable est présente dans le code PHP, mais sans être utilisée, nous obtenons ceci, qui prouve que la fonction de rappel définie dans l’extension est bien appelée :
$ $HOME/bin/php-5.4-debug/bin/php -dextension=modules/monext.so -f ./test.php
MINIT
CREATE_MAVAR
AVANT
APRES
Ce troisième paramètre jit accepté par la fonction zend_register_auto_global() permet d’éviter une initialisation, potentiellement coûteuse, de variables super-globales, dans le cas où elles ne sont absolument pas utilisées dans un script PHP.
Il est utilisé au niveau du moteur de PHP qui spécifie une valeur de 1 pour la variable $GLOBALS (qui n’est que rarement utilisée – son initialisation n’est donc souvent pas utile), valeur qui peut aussi être spécifiée pour $_SERVER, $_ENV, et $_REQUEST en fonction de la valeur de la directive de configuration auto_globals_jit (la première étant coûteuse à construire, et les deux suivantes peu utilisées).
2.3 Personnaliser la sortie de phpinfo()
La section de votre extension sur la page générée par la fonction phpinfo(), ou via php -i en ligne de commandes, est à ne pas négliger : elle sera vue par une bonne partie des développeurs utilisant les fonctionnalités de votre extension, mais aussi probablement par ceux qui devront installer, paramétrer ou administrer leurs applications.
Elle doit donc reprendre les informations principales de votre extension ; à savoir, en général :
- le nom de l’extension, pour indiquer qu’elle est chargée correctement,
- sa version,
- ses paramètres de configuration
.ini, - et, si l’extension dépend d’une bibliothèque externe, de la version de cette bibliothèque contre laquelle l’extension a été compilée.
Par défaut, si vous n’effectuez aucun développement spécifique à ce niveau, la sortie de phpinfo() reprendra les trois premiers points ; par exemple, pour une extension n’exposant aucune directive de configuration .ini :
phpinfo()Mais il est possible de personnaliser cette sortie, pour ajouter ou ne pas faire figurer certaines informations.
2.3.1 Personnaliser phpinfo()
Pour déclarer au moteur de PHP que votre extension contient une fonction qui se chargera de générer la section de phpinfo() lui correspondant, il faut ajouter une référence à celle-ci dans la structure zend_module_entry de l’extension, à l’aide de la macro PHP_MINFO() :
zend_module_entry monext_module_entry = {
STANDARD_MODULE_HEADER,
"monext",
NULL, /* Function entries */
NULL, /* Module init */
NULL, /* Module shutdown */
NULL, /* Request init */
NULL, /* Request shutdown */
PHP_MINFO(monext), /* Module information */
"0.1", /* Replace with version number for your extension */
STANDARD_MODULE_PROPERTIES
};
Le prototype de la fonction correspondante est ajouté dans le fichier .h de notre extension :
PHP_MINFO_FUNCTION(monext);
Et sa définition trouve sa place dans le fichier .c correspondant :
PHP_MINFO_FUNCTION(monext)
{
php_info_print_table_start();
php_info_print_table_colspan_header(2, "Extension de test 'monext'");
php_info_print_table_row(2, "Version", "0.1");
php_info_print_table_header(2, "Première colonne", "Seconde colonne");
php_info_print_table_row(2, "Hello", "World");
php_info_print_table_row(2, "Bonjour", "Monde");
php_info_print_table_end();
php_info_print_table_start();
php_info_print_table_header(1, "Autre tableau");
php_info_print_table_row(1, "Ligne d’informations");
php_info_print_table_end();
}
Les sorties correspondant à phpinfo() sont généralement générées par le biais de fonctions dont le nom est de la forme php_info_print_*().
Ici, la sortie générée se compose d’un premier tableau, délimité par les appels à php_info_print_table_start() et php_info_print_table_end(), à deux colonnes :
- la première ligne est une ligne de titres, contenant une seule colonne occupant la largeur de deux, via un attribut HTML
colspanque l’on retrouve dans le nom de la fonctionphp_info_print_table_colspan_header(), - la seconde ligne est composée de deux colonnes, affichant la version de notre extension,
- vient ensuite une nouvelle ligne de titre, de deux colonnes cette fois-ci,
- Suivie de deux lignes de deux colonnes contenant quelques mots.
La sortie se poursuit ensuite avec un second tableau, et le rendu en sortie HTML serait le suivant :
phpinfo() personnalisé : affichage de tableauxIl est cela dit possible de ne pas utiliser ces fonctions de tableaux et d’écrire du texte sur la sortie standard, comme le fait la fonction ci-dessous, qui sépare deux lignes de texte par une ligne horizontale (balise HTML <hr />) :
PHP_MINFO_FUNCTION(monext)
{
/* Texte en dehors de tout conteneur */
php_printf("Voici un peu de texte ;-)");
/* <hr /> ; ou 31 "_" si sortie non HTML */
php_info_print_hr();
/* Texte en dehors de tout conteneur (suite) */
php_printf("Et un peu plus ^^");
}
La sortie HTML ressemblerait alors à la capture d’écran reproduite ci-dessous :
phpinfo() personnalisé : affichage sur la sortie standardEn sortie texte, si le script PHP contenant l’appel à phpinfo() est invoqué en ligne de commandes, ou via php -l, nous obtiendrions la sortie suivante :
monext
Voici un peu de texte ;-)
_______________________________________________________________________
Et un peu plus ^^
Notez que la ligne horizontale a automatiquement été transformée par PHP en 71 caractères "_", correspondant à une ligne.
Dans le cas où une sortie sous forme de tableaux ne vous conviendrait pas, et pour éviter cette sortie brute visuellement peu satisfaisante, il est possible de positionner des boites dans la sortie de phpinfo() : des zones de texte, dont les styles sont en accord avec le reste de la sortie, où les affichages sont libres.
La création d’une telle zone est entourée d’un appel aux fonctions php_info_print_box_start() et php_info_print_box_end(), la première acceptant 0 ou 1 en paramètre, en fonction du style souhaité pour la boite. La sortie textuelle entre ces deux appels se fait vers la sortie standard, et vous êtes libre d’y positionner ce que vous souhaitez. Par exemple :
PHP_MINFO_FUNCTION(monext)
{
char html[] = "Bonjour, <strong>Monde</strong> !";
char *html_escaped = php_info_html_esc(html TSRMLS_CC);
php_info_print_box_start(1);
php_printf("%s", html_escaped);
/*
php_info_html_esc() retourne une chaîne dont l’espace a été alloué
en mémoire => il faut le libérer nous-mêmes
*/
efree(html_escaped);
php_info_print_box_end();
php_info_print_box_start(0);
php_printf("Boite d’un autre style.");
php_info_print_box_end();
}
Notez que nous avons ici utilisé la fonction php_info_html_esc() pour encoder une chaîne de caractères à afficher, qui contenait des balises HTML que nous ne souhaitions pas voir interprétées ; cette fonction peut être considérée comme équivalente à la fonction utilisateur html_entities(), adaptée à une sortie de phpinfo().
La sortie HTML obtenue avec cette portion de code ressemblerait à ceci :
phpinfo() personnalisé : boites et sortie HTML échappéeVous pouvez remarquer les deux styles différents pour chacune des deux zones de texte et le fait que les balises HTML présentes dans le texte de la première zone ont bien été échappées et ne sont donc pas interprétées.
2.3.2 Sortie HTML / sortie textuelle
La fonction phpinfo() n’est pas toujours appelée pour générer une sortie HTML : en fonction de la SAPI, il se peut qu’une sortie textuelle doive être générée – typiquement, lorsque phpinfo() est appelée depuis un script exécuté en ligne de commande, ou directement via php -i.
Si la sortie que vous cherchez à générer est susceptible de contenir du HTML, il vous faudra donc veiller à prévoir une version textuelle, qui restera lisible lorsqu’une sortie plus brute sera générée.
Le fichier SAPI.h contient ce qu’il nous faut pour déterminer si la SAPI courante correspond à une génération de phpinfo() en mode textuel ; incluons donc ce fichier d’en-têtes depuis le fichier .h de notre extension :
#include "SAPI.h"
Et ensuite, dans la fonction MINFO() de notre extension, nous pouvons accéder à sapi_module.phpinfo_as_text, qui sera vraie s’il nous faut générer une sortie textuelle. Par exemple, nous pourrions envisager l’utilisation d’un portion de code ressemblant à celle-ci :
PHP_MINFO_FUNCTION(monext)
{
php_info_print_table_start();
if (sapi_module.phpinfo_as_text)
{
php_info_print_table_row(2, "Texte", "Bonjour, **Monde** !");
}
else
{
/* Echappement HTML déjà fait en interne !!! */
php_info_print_table_row(2, "Texte",
"Bonjour, <strong>Monde</strong> !");
}
php_info_print_table_end();
}
La sortie obtenue en mode HTML serait la suivante :
phpinfo() personnalisé : sortie HTMLNotez que la fonction php_info_print_table_row() a elle-même pris le soin d’échapper les balises HTML que nous avions positionnées dans la chaîne de caractères affichée ! La condition utilisée ici, basée sur sapi_module.phpinfo_as_text, aurait donc plutôt tendance à être utile lorsque la sortie est affichée sans passer par les fonctions php_info_*(), comme lorsque nous produisons un affichage au sein d’un boite.
En mode textuel, c’est la première chaîne qui remonterait :
monext
Texte => Bonjour, **Monde** !
Pour rebondir sur ce que je disais quelques lignes plus haut, voici un exemple de code où nous générons une sortie dans une boite :
PHP_MINFO_FUNCTION(monext)
{
php_info_print_box_start(0);
if (sapi_module.phpinfo_as_text)
{
php_printf("Bonjour, **Monde** !");
}
else
{
php_printf("Bonjour, <strong>Monde</strong> !");
}
php_info_print_box_end();
}
Ici, la sortie HTML correspond à ce que nous attendions :
phpinfo() personnalisé : boite et sortie HTML
2.3.3 Affichage des directives .ini
À partir du moment où votre extension définit des directives de configuration .ini, il est recommandé de les faire remonter dans la sortie de phpinfo(). C’est même le fonctionnement par défaut si vous ne définissez pas de fonction MINFO().
Pour plus d’informations, notamment sur la personnalisation de l’affichage des valeurs pour chaque directive, consultez la section phpinfo() du Chapitre « Configuration par fichier .ini »
- La structure
zend_constantest définie dans le fichierZend/zend_constants.h.↩ - Ces deux macros et ces deux fonctions ont été commitées sur la branche
masterde PHP après la création de la branche correspondant à PHP 5.5. Il y a donc de bonnes chances qu’elles fassent leur apparition pour PHP 5.6 – et que vous ne puissiez pas les utiliser si votre extension est compilée avec une version inférieure de PHP.↩