1, modifier la configuration pendant l'exécution
Comme mentionné dans l'article précédent, la fonction ini_set permet de modifier dynamiquement certaines configurations de PHP lors de l'exécution de PHP. Notez que ce n'est qu'une partie, toutes les configurations ne peuvent pas être modifiées dynamiquement. Concernant la configuration ini modifiable, voir : http://php.net/manual/zh/configuration.changes.modes.php
On entre directement dans l'implémentation de ini_set Bien que la fonction soit un peu longue, la logique est très claire :
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &varname, &varname_len, &new_value, &new_value_len) == FAILURE) {
revenir ;
>
// Récupère la valeur configurée depuis EG (ini_directives)
Old_value = zend_ini_string(varname, varname_len + 1, 0);
/* copie à retourner ici, car une modification pourrait la libérer */
Si (ancienne_valeur) {
RETVAL_STRING(ancienne_valeur, 1);
} autre {
RETVAL_FALSE;
>
// Si le mode sans échec est activé, les configurations ini suivantes peuvent impliquer des opérations sur les fichiers et vous devez vérifier l'uid
#define _CHECK_PATH(var, var_len, ini) php_ini_check_path(var, var_len, ini, sizeof(ini))
/* vérification du mode sécurisé et du répertoire basé */
Si (PG(safe_mode) || PG(open_basedir)) {
Si (_CHECK_PATH(varname, varname_len, "error_log") ||
_CHECK_PATH(varname, varname_len, "java.class.path") ||
_CHECK_PATH(varname, varname_len, "java.home") ||
_CHECK_PATH(varname, varname_len, "mail.log") ||
_CHECK_PATH(varname, varname_len, "java.library.path") ||
_CHECK_PATH(varname, varname_len, "vpopmail.directory")) {
Si (PG(safe_mode) && (!php_checkuid(new_value, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
zval_dtor(return_value);
RETOUR_FALSE;
}
Si (php_check_open_basedir(new_value TSRMLS_CC)) {
zval_dtor(return_value);
RETOUR_FALSE;
}
>
>
// En mode sans échec, les ini suivants sont protégés et ne seront pas modifiés dynamiquement
Si (PG(safe_mode)) {
Si (!strncmp("max_execution_time", varname, sizeof("max_execution_time")) ||
!strncmp("memory_limit", varname, sizeof("memory_limit")) ||
!strncmp("child_terminate", varname, sizeof("child_terminate"))
) {
zval_dtor(return_value);
RETOUR_FALSE;
>
>
// Appelez zend_alter_ini_entry_ex pour modifier dynamiquement la configuration ini
Si (zend_alter_ini_entry_ex(varname, varname_len + 1, new_value, new_value_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC) == FAILURE) {
zval_dtor(return_value);
RETOUR_FALSE;
>
>
Comme vous pouvez le constater, en plus de quelques travaux de vérification nécessaires, l'essentiel est d'appeler zend_alter_ini_entry_ex.
Nous continuons le suivi dans la fonction zend_alter_ini_entry_ex :
// Trouver l'ini_entry
correspondant dans EG (ini_directives)
If (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry) == FAILURE) {
return FAILURE ;
>
// S'il a été modifié et s'il peut être modifié
Modifiable = ini_entry->modifiable;
Modifié = ini_entry->modifié;
if (stage == ZEND_INI_STAGE_ACTIVATE && modifier_type == ZEND_INI_SYSTEM) {
ini_entry->modifiable = ZEND_INI_SYSTEM;
>
// Faut-il forcer la modification
Si (!force_change) {
Si (!(ini_entry->modifiable & modifier_type)) {
return FAILURE ;
>
>
// EG (modified_ini_directives) est utilisé pour stocker l'ini_entry modifié
// Principalement utilisé pour la récupération
Si (!EG(modified_ini_directives)) {
ALLOC_HASHTABLE(EG(modified_ini_directives));
zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
>
//Réservez la valeur dans ini_entry, la longueur de la valeur et la plage modifiable dans orig_xxx
// Pour que ini_entry puisse être restauré à la fin de la requête
Si (!modifié) {
ini_entry->orig_value = ini_entry->value;
ini_entry->orig_value_length = ini_entry->value_length;
ini_entry->orig_modifiable = modifiable;
ini_entry->modifié = 1;
zend_hash_add(EG(modified_ini_directives), nom, nom_longueur, &ini_entry, sizeof(zend_ini_entry*), NULL);
>
duplicate = estrndup(new_value, new_value_length);
// Appel modifier pour mettre à jour la configuration ini correspondante dans XXX_G
if (!ini_entry->on_modify || ini_entry->on_modify(ini_entry, duplicate, new_value_length, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage TSRMLS_CC) == SUCCÈS) {
// Comme ci-dessus, si elle est modifiée plusieurs fois, la valeur précédemment modifiée doit être libérée
Si (modifié && ini_entry->orig_value != ini_entry->value) {
efree(ini_entry->value);
>
ini_entry->value = dupliquer;
ini_entry->value_length = new_value_length;
} autre {
efree(duplicata);
return FAILURE ;
>
retour SUCCÈS ;
>
Il y a 3 logiques que nous devons bien comprendre :
1) Le champ modifié dans ini_entry est utilisé pour indiquer si la configuration a été modifiée dynamiquement. Une fois la configuration ini modifiée, modifié sera défini sur 1. Il y a une section cruciale dans le code ci-dessus :
Ce code signifie que peu importe le nombre de fois que nous appelons ini_set dans le code php, seul le premier ini_set entrera dans cette logique et définira la valeur orig_value. A partir du deuxième appel à ini_set, cette branche ne sera plus exécutée, car modifié à ce moment a été mis à 1. Par conséquent, ini_entry->orig_value enregistre toujours la valeur de configuration avant la première modification (c'est-à-dire la configuration la plus originale).
2) Afin que la configuration modifiée par ini_set prenne effet immédiatement, la fonction de rappel on_modify est requise.
Comme mentionné dans l'article précédent, on_modify est appelé pour pouvoir mettre à jour les variables globales du module. Rappelez-vous encore une fois, tout d'abord, la configuration dans les variables globales du module n'est plus de type chaîne. Utilisez bool lorsqu'il doit utiliser bool, et int lorsqu'il doit utiliser int. Deuxièmement, chaque ini_entry stocke l'adresse de la variable globale du module et le décalage correspondant, afin que on_modify puisse modifier rapidement la mémoire. De plus, n'oubliez pas qu'après l'appel de on_modify, ini_entry->value doit encore être mis à jour afin que la valeur de configuration dans EG (ini_directives) soit la plus récente.
3) Une nouvelle table de hachage apparaît ici, EG (modified_ini_directives).
EG (modified_ini_directives) est uniquement utilisé pour stocker les configurations ini modifiées dynamiquement. Si une configuration ini est modifiée dynamiquement, alors elle existe à la fois dans EG (ini_directives) et EG (modified_ini_directives). Puisque chaque ini_entry est marqué d'un champ modifié, n'est-il pas possible de parcourir EG (ini_directives) pour obtenir toutes les configurations modifiées ?
La réponse est oui. Personnellement, je pense que l'EG (modified_ini_directives) ici sert principalement à améliorer les performances. Il suffit de parcourir directement l'EG (modified_ini_directives). De plus, en différant l'initialisation de EG (modified_ini_directives) à zend_alter_ini_entry_ex, vous pouvez également voir les points d'optimisation des performances de PHP en détail.
2, restaurer la configuration
Le temps d'action de ini_set est différent de celui du fichier php.ini. Une fois l'exécution de la requête terminée, ini_set deviendra invalide. De plus, lorsque la fonction ini_restore est appelée dans notre code, la configuration précédemment définie via ini_set deviendra également invalide.
Après l'exécution de chaque requête php, php_request_shutdown sera déclenché et php_request_startup sont deux processus correspondants. Si php est connecté sous Apache/nginx, php_request_shutdown sera appelé à chaque fois qu'une requête http est traitée ; si php est exécuté en mode CLI, php_request_shutdown sera également appelé après l'exécution du script.
Dans php_request_shutdown, nous pouvons voir le processus de récupération pour ini :
Entrez zend_deactivate, vous pouvez voir en outre que la fonction zend_ini_deactivate est appelée et que zend_ini_deactivate est responsable de la restauration de la configuration php.
Regardons de plus près l'implémentation de zend_ini_deactivate :
Depuis zend_hash_apply, la vraie tâche de restaurer ini incombe finalement à la fonction de rappel zend_restore_ini_entry_wrapper.
static int zend_restore_ini_entry_cb(zend_ini_entry *ini_entry, int stage TSRMLS_DC)
{
int résultat = ÉCHEC;
// Afficher uniquement les éléments ini modifiés
Si (ini_entry->modifié) {
Si (ini_entry->on_modify) {
//Utilisez orig_value pour réinitialiser les champs pertinents dans XXX_G
zend_try {
result = ini_entry->on_modify(ini_entry, ini_entry->orig_value, ini_entry->orig_value_length, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage TSRMLS_CC);
} zend_end_try();
>
Si (étape == ZEND_INI_STAGE_RUNTIME && résultat == FAILURE) {
/* L'échec de l'exécution est OK */
return 1 ;
>
Si (ini_entry->value != ini_entry->orig_value) {
efree(ini_entry->value);
>
// ini_entry lui-même est restauré à sa valeur d'origine
ini_entry->value = ini_entry->orig_value;
ini_entry->value_length = ini_entry->orig_value_length;
ini_entry->modifiable = ini_entry->orig_modifiable;
ini_entry->modifié = 0;
ini_entry->orig_value = NULL;
ini_entry->orig_value_length = 0;
ini_entry->orig_modifiable = 0;
>
Renvoie 0 ;
>
La logique est assez claire, je pense que les lecteurs peuvent la comprendre. Pour résumer le processus de récupération de la configuration ini :
3. Zerstörung der Konfiguration
Am Ende des Sapi-Lebenszyklus wird beispielsweise Apache heruntergefahren, das CLI-Programm ausgeführt usw. Sobald diese Phase erreicht ist, müssen der zuvor erwähnte Konfigurations-Hash, EG (ini_directives) usw. zerstört und der von ihnen verwendete Speicherplatz freigegeben werden.
1. PHP beendet alle Module nacheinander und ruft UNREGISTER_INI_ENTRIES in PHP_MSHUTDOWN_FUNCTION jedes Moduls auf. UNREGISTER_INI_ENTRIES entspricht REGISTER_INI_ENTRIES, aber UNREGISTER_INI_ENTRIES ist nicht für die Freigabe des globalen Speicherplatzes des Moduls verantwortlich. Der Speicher von XXX_globals wird im statischen Datenbereich platziert und muss nicht manuell recycelt werden.
Die Hauptaufgabe von UNREGISTER_INI_ENTRIES besteht darin, die ini_entry-Konfiguration eines bestimmten Moduls aus der EG-Tabelle (ini_directives) zu löschen. Nach dem Löschen wird der Speicherplatz von ini_entry selbst zurückgefordert, ini_entry->value darf jedoch nicht zurückgefordert werden.
Nachdem PHP_MSHUTDOWN_FUNCTION aller Module einmal UNREGISTER_INI_ENTRIES aufgerufen hat, bleibt nur noch die INI-Konfiguration des Core-Moduls in EG übrig (ini_directives). Zu diesem Zeitpunkt müssen Sie UNREGISTER_INI_ENTRIES manuell aufrufen, um das Löschen der Kernmodulkonfiguration abzuschließen.
// Zu diesem Zeitpunkt ist nur noch die Konfiguration des Core-Moduls in EG (ini_directives) übrig
// Hier manuell bereinigen
UNREGISTER_INI_ENTRIES();
// Konfigurationshash recyceln
php_shutdown_config();
// EG(ini_directives) recyceln
zend_ini_shutdown(TSRMLS_C);
...
}
Nachdem der manuelle Aufruf von UNREGISTER_INI_ENTRIES abgeschlossen ist, enthält EG (ini_directives) keine Elemente mehr. Theoretisch ist EG (ini_directives) zu diesem Zeitpunkt eine leere Hash-Tabelle.
2. Das Recycling von „configuration_hash“ erfolgt nach EG (ini_directives). Der oben gepostete Code enthält den Funktionsaufruf über php_shutdown_config. php_shutdown_config ist hauptsächlich für das Recycling von Configuration_Hash verantwortlich.
Beachten Sie, dass zend_hash_destroy den Speicherplatz von „configuration_hash“ selbst nicht freigibt. Ebenso wie der globale Speicherplatz des Moduls, auf den XXX_G zugreift, ist auch „configuration_hash“ eine globale Variable und muss nicht manuell recycelt werden.
3. Wenn php_shutdown_config abgeschlossen ist, wurde nur der eigene Speicherplatz von EG (ini_directives) nicht freigegeben. Der letzte Schritt ruft also zend_ini_shutdown auf. zend_ini_shutdown wird verwendet, um EG (ini_directives) freizugeben. Wie oben erwähnt, handelt es sich bei EG (ini_directives) zu diesem Zeitpunkt theoretisch um eine leere Hash-Tabelle, sodass der von der HashTable selbst belegte Speicherplatz freigegeben werden muss.
4, Zusammenfassung
Verwenden Sie ein Bild, um den Prozess im Zusammenhang mit der INI-Konfiguration grob zu beschreiben: