1, Konfiguration während der Laufzeit ändern
Wie im vorherigen Artikel erwähnt, kann die Funktion ini_set einige PHP-Konfigurationen während der Ausführung von PHP dynamisch ändern. Beachten Sie, dass dies nur ein Teil davon ist und nicht alle Konfigurationen dynamisch geändert werden können. Informationen zur änderbaren INI-Konfiguration finden Sie unter: http://php.net/manual/zh/configuration.changes.modes.php
Wir betreten direkt die Implementierung von ini_set. Obwohl die Funktion etwas lang ist, ist die Logik sehr klar:
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &varname, &varname_len, &new_value, &new_value_len) == FAILURE) {
zurück;
}
// Den konfigurierten Wert von EG (ini_directives) abrufen
Old_value = zend_ini_string(varname, varname_len + 1, 0);
/* kopieren, um hierher zurückzukehren, da alter es möglicherweise freigibt */
If (old_value) {
RETVAL_STRING(old_value, 1);
} sonst {
RETVAL_FALSE;
}
// Wenn der abgesicherte Modus aktiviert ist, können die folgenden INI-Konfigurationen Dateivorgänge beinhalten und Sie müssen die UID überprüfen
#define _CHECK_PATH(var, var_len, ini) php_ini_check_path(var, var_len, ini, sizeof(ini))
/* Safe_Mode & BasedIR-Prüfung */
If (PG(safe_mode) || PG(open_basedir)) {
If (_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")) {
If (PG(safe_mode) && (!php_checkuid(new_value, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
zval_dtor(return_value);
RETURN_FALSE;
}
If (php_check_open_basedir(new_value TSRMLS_CC)) {
zval_dtor(return_value);
RETURN_FALSE;
}
}
}
// Im abgesicherten Modus sind die folgenden INIs geschützt und werden nicht dynamisch geändert
If (PG(safe_mode)) {
If (!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);
RETURN_FALSE;
}
}
// Zend_alter_ini_entry_ex aufrufen, um die INI-Konfiguration dynamisch zu ändern
If (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);
RETURN_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ée à ce moment a été mise à 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 :
Von zend_hash_apply fällt die eigentliche Aufgabe der Wiederherstellung von INI schließlich auf die Callback-Funktion zend_restore_ini_entry_wrapper.
static int zend_restore_ini_entry_cb(zend_ini_entry *ini_entry, int stage TSRMLS_DC)
{
int result = FAILURE;
// Nur geänderte INI-Elemente anzeigen
If (ini_entry->modified) {
If (ini_entry->on_modify) {
//Orig_value verwenden, um die relevanten Felder in XXX_G
zurückzusetzen
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();
}
If (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
/* Laufzeitfehler ist in Ordnung */
return 1;
}
If (ini_entry->value != ini_entry->orig_value) {
efree(ini_entry->value);
}
// ini_entry selbst wird auf seinen ursprünglichen Wert zurückgesetzt
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->modified = 0;
ini_entry->orig_value = NULL;
ini_entry->orig_value_length = 0;
ini_entry->orig_modifiable = 0;
}
Rückgabe 0;
}
Die Logik ist ziemlich klar, ich glaube, dass die Leser sie verstehen können. Um den Wiederherstellungsprozess der INI-Konfiguration zusammenzufassen:
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: