Studenten, die PHP verwenden, wissen, dass die php.ini-Konfiguration während des gesamten SAPI-Lebenszyklus wirksam wird. Wenn Sie während der Ausführung eines PHP-Skripts die INI-Konfiguration manuell ändern, wird diese nicht wirksam. Wenn Sie Apache oder Nginx zu diesem Zeitpunkt nicht neu starten können, können Sie die Schnittstelle ini_set nur explizit im PHP-Code aufrufen. ini_set ist eine von PHP bereitgestellte Funktion zum dynamischen Ändern der Konfiguration. Es ist zu beachten, dass die von ini_set festgelegte Konfiguration und die in der INI-Datei festgelegte Konfiguration unterschiedliche effektive Zeitbereiche haben. Nachdem das PHP-Skript ausgeführt wurde, werden die ini_set-Einstellungen sofort ungültig.
Daher ist dieser Artikel in zwei Teile unterteilt. Der erste Teil erläutert das Prinzip der php.ini-Konfiguration und der zweite Teil befasst sich mit der dynamischen Änderung der PHP-Konfiguration.
Die Konfiguration von php.ini umfasst grob drei Datenelemente: „configuration_hash“, „EG“ (ini_directives) und „PG“, „BG“, „PCRE_G“, „JSON_G“, „XXX_G“ usw. Wenn Sie die Bedeutung dieser drei Arten von Daten nicht kennen, spielt es keine Rolle, sie werden im Folgenden ausführlich erläutert.
1, INI-Konfigurationsdatei analysieren
Da php.ini während des SAPI-Prozesses aktiv sein muss, muss die Arbeit des Parsens der INI-Datei und des entsprechenden Erstellens der PHP-Konfiguration der Beginn von SAPI sein. Mit anderen Worten: Es muss während des Startvorgangs von PHP erfolgen. PHP benötigt diese Konfigurationen, um intern generiert zu werden, bevor eine tatsächliche Anfrage eintrifft.
Wird im Kern von PHP widergespiegelt, der Funktion php_module_startup.
php_module_startup ist hauptsächlich für den Start von PHP verantwortlich. Es wird normalerweise beim Start von SAPI aufgerufen. Eine weitere gängige Funktion ist übrigens php_request_startup, die für die Initialisierung jeder Anfrage verantwortlich ist, wenn sie eintrifft. php_module_startup und php_request_startup sind zwei ikonische Aktionen, deren Analyse jedoch den Rahmen dieses Artikels sprengen würde.
Wenn beispielsweise PHP in ein Modul unter Apache eingebunden wird, werden beim Start von Apache alle diese Module aktiviert, einschließlich des PHP-Moduls. Beim Aktivieren des PHP-Moduls wird php_module_startup aufgerufen. Die Funktion php_module_startup erledigt eine Menge Arbeit. Sobald der Aufruf php_module_startup beendet ist, heißt das: OK, PHP wurde gestartet und kann nun Anfragen annehmen und antworten.
In der Funktion php_module_startup lautet die Implementierung zum Parsen der INI-Datei:
Wie Sie sehen können, wird die Funktion php_init_config tatsächlich aufgerufen, um die Analyse der INI-Datei abzuschließen. Die Analysearbeit führt hauptsächlich eine Lex- und Grammatikanalyse durch und extrahiert und speichert die Schlüssel-Wert-Paare in der INI-Datei. Das Format von php.ini ist sehr einfach, mit dem Schlüssel auf der linken Seite des Gleichheitszeichens und dem Wert auf der rechten Seite. Wo speichert PHP jedes Mal, wenn ein Paar KVs extrahiert wird? Die Antwort ist der zuvor erwähnte „configuration_hash“.
statische HashTable-Konfiguration_hash;
Configuration_hash wird in php_ini.c deklariert, einer Datenstruktur vom Typ HashTable. Wie der Name schon sagt, handelt es sich tatsächlich um eine Hash-Tabelle. Abgesehen davon kann „configuration_hash“ in Versionen vor php5.3 nicht abgerufen werden, da es sich um eine statische Variable in der Datei „php_ini.c“ handelt. Später fügte php5.3 die Schnittstelle php_ini_get_configuration_hash hinzu, die &configuration_hash direkt zurückgibt, sodass verschiedene PHP-Erweiterungen leicht einen Blick auf den Configuration_hash werfen können ... Was für ein großer Segen ...
Beachten Sie vier Punkte:
Erstens führt php_init_config keine andere Überprüfung als die lexikalische und syntaktische Überprüfung durch. Mit anderen Worten: Wenn wir der INI-Datei eine Zeile „hello=world“ hinzufügen, enthält der endgültige „configuration_hash“ ein Element mit dem Schlüssel „hello“ und dem Wert „world“, sofern es sich um ein korrekt formatiertes Konfigurationselement handelt, und der „configuration_hash“ spiegelt wider es im maximalen Umfang.
Zweitens ermöglicht uns die INI-Datei die Konfiguration in Form eines Arrays. Schreiben Sie beispielsweise die folgenden drei Zeilen in die INI-Datei:
Dann gibt es in der endgültig generierten Konfigurations-Hash-Tabelle ein Element mit dem Schlüssel drift.arr und sein Wert ist ein Array mit drei Zahlen: 1, 2 und 3. Dies ist eine äußerst seltene Konfigurationsmethode.
Drittens ermöglicht uns PHP auch, zusätzlich zur Standarddatei php.ini (genauer gesagt php-%s.ini) einige zusätzliche INI-Dateien zu erstellen. Diese INI-Dateien werden in einem zusätzlichen Verzeichnis abgelegt. Dieses Verzeichnis wird durch die Umgebungsvariable PHP_INI_SCAN_DIR angegeben. Nachdem php_init_config php.ini analysiert hat, scannt es dieses Verzeichnis erneut und findet alle .ini-Dateien im Verzeichnis zur Analyse. Die in diesen zusätzlichen INI-Dateien generierten kv-Schlüssel-Wert-Paare werden ebenfalls zum „configuration_hash“ hinzugefügt.
Dies ist eine gelegentlich nützliche Funktion. Angenommen, wir entwickeln selbst eine PHP-Erweiterung, möchten die Konfiguration jedoch nicht in php.ini mischen. Wir können PHP über PHP_INI_SCAN_DIR mitteilen, wo es zu finden ist. Natürlich liegen auch seine Nachteile auf der Hand und es müssen zusätzliche Umgebungsvariablen festgelegt werden, um dies zu unterstützen. Eine bessere Lösung besteht darin, dass Entwickler php_parse_user_ini_file oder zend_parse_ini_file selbst in der Erweiterung aufrufen, um die entsprechende INI-Datei zu analysieren.
Viertens ist der Schlüssel in „configuration_hash“ eine Zeichenfolge. Was ist also der Typ des Werts? Die Antwort ist ebenfalls ein String (mit Ausnahme des oben erwähnten sehr speziellen Arrays). Konkret etwa die folgende Konfiguration:
Dann ist das tatsächlich im endgültigen Konfigurations-Hash gespeicherte Schlüssel-Wert-Paar:
Schlüssel: „log_errors“
val: „“
Schlüssel: „log_errors_max_len“
Wert: „1024“
Achten Sie auf log_errors, der gespeicherte Wert ist nicht einmal „0“, es ist eine echte leere Zeichenfolge. Darüber hinaus ist log_errors_max_len keine Zahl, sondern eine Zeichenfolge von 1024.
An diesem Punkt der Analyse wurde im Grunde alles, was mit dem Parsen der INI-Datei zu tun hat, klar erklärt. Um es kurz zusammenzufassen:
1, das Parsen von INI erfolgt in der Phase php_module_startup
2. Die Parsing-Ergebnisse werden in „configuration_hash“ gespeichert.
2, Konfiguration gilt für Modul
Die allgemeine Struktur von PHP kann als Zend-Engine unten betrachtet werden, die für die Interaktion mit dem Betriebssystem, das Kompilieren von PHP-Code, die Bereitstellung von Speicherhosting usw. verantwortlich ist. Auf der oberen Ebene von Zend sind viele Module angeordnet Motor. Das Kernmodul ist das Kernmodul, und andere umfassen Standard, PCRE, Datum, Sitzung usw. Diese Module haben auch einen anderen Namen, der als PHP-Erweiterung bezeichnet wird. Wir können einfach verstehen, dass jedes Modul eine Reihe von Funktionsschnittstellen bereitstellt, die Entwickler aufrufen können. Beispielsweise werden häufig verwendete integrierte Funktionen wie Explodieren, Trimmen, Array usw. vom Standardmodul bereitgestellt.
Wir müssen darüber sprechen, weil es in php.ini zusätzlich zu einigen Konfigurationen für PHP selbst, also für das Core-Modul (wie Safe_mode, Display_errors, max_execution_time usw.), eine ganze Reihe gibt wenige Konfigurationen für andere verschiedene Module.
Zum Beispiel das Datumsmodul, das allgemeine Datums-, Uhrzeit-, Strtotime- und andere Funktionen bereitstellt. In php.ini sieht die zugehörige Konfiguration wie folgt aus:
Zusätzlich zu diesen Modulen mit unabhängigen Konfigurationen ist die Zend-Engine auch konfigurierbar, aber die Zend-Engine verfügt nur über sehr wenige konfigurierbare Elemente, nur error_reporting, zend.enable_gc und discover_unicode.
Wie wir im vorherigen Abschnitt erwähnt haben, ruft php_module_startup php_init_config auf, dessen Zweck darin besteht, die INI-Datei zu analysieren und den Konfigurations-Hash zu generieren. Was wird als nächstes in php_module_startup noch getan? Offensichtlich wird die Konfiguration in „configuration_hash“ auf verschiedene Module wie Zend, Core, Standard, SPL usw. angewendet. Natürlich ist dies kein Prozess über Nacht, da PHP normalerweise viele Module enthält und diese Module auch beim PHP-Start nacheinander gestartet werden. Anschließend erfolgt die Konfiguration von Modul A während des Startvorgangs von Modul A.
Studenten mit Erfahrung in der Erweiterungsentwicklung werden direkt darauf hinweisen, dass Modul A in PHP_MINIT_FUNCTION(A) gestartet wird, nicht wahr?
Ja, wenn Modul A konfiguriert werden muss, können Sie in PHP_MINIT_FUNCTION REGISTER_INI_ENTRIES() aufrufen, um es abzuschließen. REGISTER_INI_ENTRIES durchsucht den Konfigurations-Hash nach dem vom Benutzer festgelegten Konfigurationswert basierend auf dem Namen des vom aktuellen Modul benötigten Konfigurationselements und aktualisiert ihn im eigenen globalen Bereich des Moduls.
2.1, globaler Raum des Moduls
Um zu verstehen, wie die INI-Konfiguration von „configuration_hash“ auf jedes Modul angewendet wird, ist es notwendig, zunächst den globalen Raum des PHP-Moduls zu verstehen. Für verschiedene PHP-Module können Sie einen eigenen Speicherplatz eröffnen, der für das Modul global sichtbar ist. Im Allgemeinen wird es zum Speichern der vom Modul benötigten INI-Konfiguration verwendet. Mit anderen Worten: Die Konfigurationselemente in „configuration_hash“ werden schließlich im globalen Bereich gespeichert. Während der Ausführung des Moduls müssen Sie nur direkt auf diesen globalen Bereich zugreifen, um die Benutzereinstellungen für das Modul abzurufen. Natürlich wird es auch häufig verwendet, um Zwischendaten während der Ausführung des Moduls aufzuzeichnen.
Nehmen wir als Beispiel das bcmath-Modul, das eine Schnittstelle für mathematische Berechnungen bereitstellt. Schauen wir uns zunächst seine INI-Konfiguration an:
bcmath hat nur ein Konfigurationselement. Wir können bcmath.scale in php.ini verwenden, um das bcmath-Modul zu konfigurieren.
Schauen Sie sich als Nächstes weiterhin die globale Raumdefinition des bcmatch-Moduls an. In php_bcmath.h gibt es die folgende Anweisung:
Nachdem das Makro erweitert wurde, lautet es:
Tatsächlich ist der Typ zend_bcmath_globals der globale Raumtyp im bcmath-Modul. Hier wird nur die Struktur zend_bcmath_globals deklariert, und in bcmath.c gibt es eine spezifische Instanziierungsdefinition:
// Nach der Erweiterung ist es zend_bcmath_globals bcmath_globals;
ZEND_DECLARE_MODULE_GLOBALS(bcmath)
Es ist ersichtlich, dass die Definition der Variablen bcmath_globals mit ZEND_DECLARE_MODULE_GLOBALS abgeschlossen ist.
bcmath_globals ist ein echter globaler Raum, der vier Felder enthält. Sein letztes Feld, bc_precision, entspricht bcmath.scale in der INI-Konfiguration. Wir legen den Wert von bcmath.scale in php.ini fest und dann wird der Wert von bcmath.scale beim Starten des bcmath-Moduls auf bcmath_globals.bc_precision aktualisiert.
Aktualisieren Sie den Wert in „configuration_hash“ auf die Variable „xxx_globals“, die von jedem Modul definiert wird. Dies ist das sogenannte Anwenden der INI-Konfiguration auf das Modul. Sobald das Modul gestartet ist, sind diese Konfigurationen vorhanden. Daher muss das PHP-Modul in der nachfolgenden Ausführungsphase nicht erneut auf den Konfigurations-Hash zugreifen. Das Modul muss lediglich auf seine eigenen XXX_globals zugreifen, um die vom Benutzer festgelegte Konfiguration abzurufen.
bcmath_globals, was sind neben einem Feld für das INI-Konfigurationselement die anderen drei Felder? Dies ist die zweite Rolle des globalen Modulraums. Er wird nicht nur für die INI-Konfiguration verwendet, sondern kann auch einige Daten während der Modulausführung speichern.
Ein weiteres Beispiel ist das JSON-Modul, das ebenfalls ein sehr häufig verwendetes Modul in PHP ist:
Sie können sehen, dass das JSON-Modul keine INI-Konfiguration erfordert und sein globaler Bereich nur ein Feld error_code hat. error_code zeichnet die Fehler auf, die bei der letzten Ausführung von json_decode oder json_encode aufgetreten sind. Die Funktion json_last_error gibt diesen Fehlercode zurück, um Benutzern bei der Suche nach der Fehlerursache zu helfen.
Um einfach auf Modul-Globalspace-Variablen zugreifen zu können, hat PHP herkömmlicherweise einige Makros vorgeschlagen. Wenn wir beispielsweise auf den error_code in json_globals zugreifen möchten, können wir ihn natürlich direkt als json_globals.error_code schreiben (in einer Multithread-Umgebung nicht verfügbar), aber eine allgemeinere Schreibweise besteht darin, das JSON_G-Makro zu definieren:
Wir verwenden JSON_G(error_code), um auf json_globals.error_code zuzugreifen. Am Anfang dieses Artikels habe ich PG, BG, JSON_G, PCRE_G, XXX_G usw. erwähnt. Diese Makros kommen auch im PHP-Quellcode sehr häufig vor. Jetzt können wir sie leicht verstehen. Das PG-Makro kann auf die globalen Variablen des Kernmoduls zugreifen, BG kann auf die globalen Variablen des Standardmoduls zugreifen und PCRE_G kann auf die globalen Variablen des PCRE-Moduls zugreifen.
2.2. Wie ermittelt man, welche Konfiguration ein Modul benötigt?
Welche Art der INI-Konfiguration das Modul benötigt, wird in jedem Modul definiert. Für das Core-Modul gibt es beispielsweise die folgende Konfigurationselementdefinition:
Der obige Code befindet sich in der Datei php-srcmainmain.c in mehr als 450 Zeilen. Es sind viele Makros beteiligt, darunter ZEND_INI_BEGIN, ZEND_INI_END, PHP_INI_ENTRY_EX, STD_PHP_INI_BOOLEAN usw. In diesem Artikel wird nicht einzeln auf Details eingegangen. Interessierte Leser können sie selbst analysieren.
Nach der Makroerweiterung des obigen Codes erhalten wir:
Wir sehen, dass die Definition eines Konfigurationselements im Wesentlichen ein Array vom Typ zend_ini_entry definiert. Die spezifische Bedeutung der Felder der zend_ini_entry-Struktur ist:
char *value;
uint value_length;
uint orig_value_length;
int orig_modifiable; // Das ursprüngliche modifizierbare
des Konfigurationselements
int Modified; // Ob es geändert wurde, orig_value speichert den Wert vor der Änderung
};
2.3, wenden Sie die Konfiguration auf das Modul an – REGISTER_INI_ENTRIES
REGISTER_INI_ENTRIES ist oft in PHP_MINIT_FUNCTION verschiedener Erweiterungen zu sehen. REGISTER_INI_ENTRIES ist hauptsächlich dafür verantwortlich, zwei Dinge zu erledigen: Erstens, das Füllen des globalen Speicherplatzes XXX_G des Moduls und das Synchronisieren des Werts in „configuration_hash“ mit XXX_G. Zweitens generiert es auch EG(ini_directives).
REGISTER_INI_ENTRIES ist auch ein Makro. Nach der Erweiterung handelt es sich tatsächlich um die Methode zend_register_ini_entries. Schauen wir uns speziell die Implementierung von zend_register_ini_entries an:
hashed_ini_entry->on_modify(hashed_ini_entry, hashed_ini_entry->value, hashed_ini_entry->value_length, hashed_ini_entry->mh_arg1, hashed_ini_entry->mh_arg2, hashed_ini_entry-> m h_arg3, ZEND_INI_STAGE_STARTUP TSRMLS_CC);
}
p++;
}
Rückgabe ERFOLGREICH;
}
Einfach ausgedrückt kann die Logik des obigen Codes wie folgt ausgedrückt werden:
1. Fügen Sie die vom Modul deklarierten INI-Konfigurationselemente zu EG (ini_directives) hinzu. Beachten Sie, dass der Wert des INI-Konfigurationselements später geändert werden kann.
2. Versuchen Sie, die von jedem Modul benötigte INI in „configuration_hash“ zu finden.
Wenn es gefunden werden kann, bedeutet dies, dass der Wert in der INI-Datei des Benutzers konfiguriert ist und die Konfiguration des Benutzers verwendet wird.
Wenn es nicht gefunden wird, spielt es keine Rolle, da das Modul bei der Deklaration von ini den Standardwert mitbringt.
3. Synchronisieren Sie den Wert von ini mit XX_G. Schließlich spielen diese XXX_globals während der Ausführung von PHP immer noch eine Rolle. Der spezifische Prozess besteht darin, die on_modify-Methode aufzurufen, die jeder INI-Konfiguration entspricht. on_modify wird vom Modul beim Deklarieren der INI angegeben.
Schauen wir uns on_modify genauer an, das eigentlich ein Funktionszeiger ist. Schauen wir uns die Konfigurationsanweisungen von zwei spezifischen Kernmodulen an:
Für log_errors ist sein on_modify auf OnUpdateBool gesetzt, und für log_errors_max_len ist sein on_modify auf OnUpdateLong gesetzt.
Angenommen, unsere Konfiguration in php.ini lautet:
Sehen wir uns die OnUpdateBool-Funktion genauer an:
// p stellt die Adresse von core_globals plus den Offset des log_errors-Felds dar
//Die erhaltene Adresse ist die Adresse des log_errors-Felds
p = (zend_bool *) (base+(size_t) mh_arg1);
*p = (zend_bool) 1;
}
else if (new_value_length == 3 && strcasecmp("yes", new_value) == 0) {
*p = (zend_bool) 1;
}
else if (new_value_length == 4 && strcasecmp("true", new_value) == 0) {
*p = (zend_bool) 1;
}
sonst {
//Der in „configuration_hash“ gespeicherte Wert ist die Zeichenfolge „1“, nicht „On“
// Hier verwenden wir also atoi, um es in die Zahl 1 umzuwandeln
*p = (zend_bool) atoi(new_value);
}
Rückgabe ERFOLGREICH;
}
// log_errors_max_len의 주소를 가져옵니다
p = (long *) (base+(size_t) mh_arg1);
// "1024"를 long 타입으로 변환하여 core_globals.log_errors_max_len에 할당
*p = zend_atol(new_value, new_value_length);
성공을 반환합니다.
}
마지막으로 주의할 점은 zend_register_ini_entries 함수에서configuration_hash에 구성이 있는 경우 on_modify가 호출될 때 hashed_ini_entry의 값과 value_length가 업데이트된다는 것입니다. 즉, 사용자가 php.ini에서 구성한 경우 EG(ini_directives)는 실제 구성된 값을 저장합니다. 사용자가 구성되지 않은 경우 EG(ini_directives)는 zend_ini_entry 선언 시 제공된 기본값을 저장합니다.
zend_register_ini_entries의 default_value 변수는 이름이 잘못되어 오해를 일으키기 쉽습니다. 실제로 default_value는 기본값을 의미하는 것이 아니라 사용자가 실제로 설정한 값을 의미합니다.
3, 요약
이제 데이터 구성_hash, EG(ini_directives) 및 PG, BG, PCRE_G, JSON_G, XXX_G...의 세 가지 부분이 모두 명확하게 설명되었습니다.
요약하자면:
1,configuration_hash는 php.ini 파일에 구성을 저장하고 검증을 수행하지 않으며 값은 문자열입니다.
2. EG(ini_directives)는 각 모듈에 정의된 zend_ini_entry를 저장합니다. 사용자가 php.ini(configuration_hash에 존재)에서 구성한 경우 해당 값은configuration_hash의 값으로 대체되며 유형은 여전히 문자열입니다.
3. XXX_G, 이 매크로는 모듈의 전역 공간에 액세스하는 데 사용됩니다. 이 메모리 공간은 ini 구성을 저장하고 on_modify에 지정된 함수를 통해 업데이트될 수 있습니다. 해당 데이터 유형은 XXX_G의 필드 선언에 의해 결정됩니다.