Der Anfang von allem: SAPI-Schnittstelle
SAPI (Server Application Programming Interface) bezeichnet die Programmierschnittstelle für bestimmte PHP-Anwendungen, egal welches Betriebssystem installiert ist, solange es vorhanden ist entspricht den Schnittstellenspezifikationen des PCs. Alle können normal auf einem PC ausgeführt werden. Es gibt viele Möglichkeiten, PHP-Skripte über einen Webserver, direkt auf der Befehlszeile oder eingebettet in andere Programme auszuführen.
Normalerweise verwenden wir Webserver wie Apache oder Nginx, um PHP-Skripte zu testen oder sie über ein PHP-Interpreterprogramm in der Befehlszeile auszuführen. Nachdem das Skript ausgeführt wurde, antwortet der Webserver und der Browser zeigt die Antwortinformationen an oder zeigt den Inhalt in der Standardausgabe der Befehlszeile an.
Es ist uns egal, wo der PHP-Interpreter ist. Obwohl die Ausführung von Skripten über einen Webserver und ein Befehlszeilenprogramm sehr unterschiedlich aussieht, ist der Arbeitsablauf tatsächlich derselbe. Die Befehlszeilenparameter werden an das vom PHP-Interpreter auszuführende Skript übergeben, was dem Anfordern einer PHP-Seite über die URL entspricht. Nach Abschluss der Skriptausführung wird das Antwortergebnis zurückgegeben, das Antwortergebnis der Befehlszeile wird jedoch auf dem Terminal angezeigt.
Die Skriptausführung beginnt mit der Implementierung der SAPI-Schnittstelle. Es ist nur so, dass verschiedene SAPI-Schnittstellenimplementierungen ihre spezifische Arbeit erledigen. Beispielsweise muss die SAPI-Implementierung mod_php von Apache einige von Apache erhaltene Informationen initialisieren und den Inhalt bei der Ausgabe von Inhalten an Apache zurückgeben.
Die folgenden Abschnitte bieten eine ausführlichere Einführung in einige gängige SAPI-Implementierungen.
Start und Ende
Nachdem PHP mit der Ausführung begonnen hat, durchläuft es zwei Hauptphasen: die Startphase vor der Verarbeitung der Anfrage und die Endphase nach der Anfrage. In der Anfangsphase gibt es zwei Prozesse: Der erste Prozess ist die Modulinitialisierungsphase (MINIT). Dieser Prozess wird nur einmal während des gesamten SAPI-Lebenszyklus durchgeführt (z. B. während des gesamten Lebenszyklus nach dem Start von Apache oder während des gesamten Ausführungsprozesses). Kommandozeilenprogramm). Der zweite Prozess ist die Modulaktivierungsphase (RINIT), die in der Anforderungsphase stattfindet. Wenn beispielsweise eine Seite über eine URL angefordert wird, wird vor jeder Anforderung eine Modulaktivierung (RINIT-Anforderungsstart) durchgeführt. Wenn PHP beispielsweise einige Erweiterungsmodule registriert, wird die MINIT-Funktion aller Module während der MINIT-Phase zurückgerufen. Das Modul kann in dieser Phase einige Initialisierungsarbeiten durchführen, z. B. das Registrieren von Konstanten, das Definieren der vom Modul verwendeten Klassen usw. Wenn das Modul implementiert ist, können diese Rückruffunktionen über die folgenden Makros implementiert werden:
<span>PHP_MINIT_FUNCTION(myphpextension) { </span><span>//</span><span> 注册常量或者类等初始化操作</span><span>return</span><span> SUCCESS; }</span>
Nachdem die Anforderung eintrifft, initialisiert PHP die grundlegende Umgebung zum Ausführen des Skripts, z. B. zum Erstellen eine Ausführungsumgebung, einschließlich Speichern des PHP-Laufs. Die Symboltabelle der Variablennamen und Werteinhalte im Prozess sowie die Symboltabelle aller aktuellen Funktionen, Klassen und anderer Informationen. Dann ruft PHP die RINIT-Funktion aller Module auf. Zu diesem Zeitpunkt kann jedes Modul auch einige verwandte Vorgänge ausführen. Die RINIT-Funktion des Moduls ähnelt der MINIT-Rückruffunktion:
<span>PHP_RINIT_FUNCTION(myphpextension) { </span><span>//</span><span> 例如记录请求开始时间 </span><span>//</span><span> 随后在请求结束的时候记录结束时间。这样我们就能够记录下处理请求所花费的时间了</span><span>return</span><span> SUCCESS; }</span>
Anforderungsverarbeitung Danach tritt PHP in die Endphase ein, wenn das Skript bis zum Ende ausgeführt wird oder indem die Funktion „exit()“ oder „die()“ aufgerufen wird. Entsprechend der Anfangsphase ist auch die Endphase in zwei Phasen unterteilt: Eine besteht darin, das Modul nach Abschluss der Anforderung zu deaktivieren (RSHUTDOWN, entsprechend RINIT), und die andere darin, das Modul zu schließen, wenn der SAPI-Lebenszyklus endet ( Der Webserver wird beendet oder das Befehlszeilenskript wird ausgeführt und beendet (MSHUTDOWN, entsprechend MINIT).
<span>PHP_RSHUTDOWN_FUNCTION(myphpextension) { </span><span>//</span><span> 例如记录请求结束时间,并把相应的信息写入到日至文件中。</span><span>return</span><span> SUCCESS; }</span>
Einzelprozess-SAPI-Lebenszyklus
CLI/CGI-Modus PHP gehört zum Einzelprozess-SAPI-Modus. Diese Art von Anfrage wird nach einmaliger Bearbeitung der Anfrage geschlossen. Das heißt, es werden nur die folgenden Links durchlaufen: Start – Anforderung startet – Anforderung schließt – Ende Die SAPI-Schnittstellenimplementierung schließt ihren Lebenszyklus ab. Wie in der folgenden Abbildung dargestellt:
Einzelprozess-SAPI-Lebenszyklus
Die obige Abbildung ist sehr einfach und leicht zu verstehen . Es ist nur so, dass PHP auch zwischen den verschiedenen Phasen viel Arbeit leistet. Hier sind einige Ergänzungen:
Startup
Vor dem Aufruf der Modulinitialisierung jedes Moduls findet ein Initialisierungsprozess statt, der Folgendes umfasst:
In den meisten Fällen werden die initialisierten globalen Variablen hier auf NULL gesetzt, mit einigen Ausnahmen, wie z. B. dem Festlegen von zuf (zend_utility_functions), wobei zuf.printf_function = php_printf als Beispiel genommen wird, wobei php_printf das zend_startup ist Die Funktion wird zend_printf als globaler Funktionszeiger zugewiesen, und die Funktion zend_printf wird normalerweise als reguläre Zeichenfolgenausgabe verwendet. Beispielsweise verwendet debug_print_backtrace, der den Programmaufrufstapel anzeigt, relevante Informationen.
Die Konstanten hier sind einige von PHPs eigenen Konstanten. Diese Konstanten sind entweder im Programm fest codiert, z. B. PHP_VERSION, oder in geschrieben Konfigurations-Header-Dateien wie PEAR_EXTENSION_DIR werden in die Datei config.w32.h geschrieben.
前面提到的zend_startup函数的作用就是初始化Zend引擎,这里的初始化操作包括内存管理初始化、 全局使用的函数指针初始化(如前面所说的zend_printf等),对PHP源文件进行词法分析、语法分析、 中间代码执行的函数指针的赋值,初始化若干HashTable(比如函数表,常量表等等),为ini文件解析做准备, 为PHP源文件解析做准备,注册内置函数(如strlen、define等),注册标准常量(如E_ALL、TRUE、NULL等)、注册GLOBALS全局变量等。
php_init_config函数的作用是读取php.ini文件,设置配置参数,加载zend扩展并注册PHP扩展函数。此函数分为如下几步: 初始化参数配置表,调用当前模式下的ini初始化配置,比如CLI模式下,会做如下初始化:
INI_DEFAULT(<span>"</span><span>report_zend_debug</span><span>"</span>, <span>"</span><span>0</span><span>"</span><span>); INI_DEFAULT(</span><span>"</span><span>display_errors</span><span>"</span>, <span>"</span><span>1</span><span>"</span>);
不过在其它模式下却没有这样的初始化操作。接下来会的各种操作都是查找ini文件:
php_startup_auto_globals函数会初始化在用户空间所使用频率很高的一些全局变量,如:$_GET、$_POST、$_FILES等。 这里只是初始化,所调用的zend_register_auto_global函数也只是将这些变量名添加到CG(auto_globals)这个变量表。
php_startup_sapi_content_types函数用来初始化SAPI对于不同类型内容的处理函数, 这里的处理函数包括POST数据默认处理函数、默认数据处理函数等。
php_register_internal_extensions_func函数用来注册静态构建的模块,也就是默认加载的模块, 我们可以将其认为内置模块。在PHP5.3.0版本中内置的模块包括PHP标准扩展模块(/ext/standard/目录, 这里是我们用的最频繁的函数,比如字符串函数,数学函数,数组操作函数等等),日历扩展模块、FTP扩展模块、 session扩展模块等。这些内置模块并不是一成不变的,在不同的PHP模板中,由于不同时间的需求或其它影响因素会导致这些默认加载的模块会变化, 比如从代码中我们就可以看到mysql、xml等扩展模块曾经或将来会作为内置模块出现。
模块初始化会执行两个操作: 1. 将这些模块注册到已注册模块列表(module_registry),如果注册的模块已经注册过了,PHP会报Module XXX already loaded的错误。 1. 将每个模块中包含的函数注册到函数表( CG(function_table) ),如果函数无法添加,则会报 Unable to register functions, unable to load。
在注册了静态构建的模块后,PHP会注册附加的模块,不同的模式下可以加载不同的模块集,比如在CLI模式下是没有这些附加的模块的。
在内置模块和附加模块后,接下来是注册通过共享对象(比如DLL)和php.ini文件灵活配置的扩展。
在所有的模块都注册后,PHP会马上执行模块初始化操作(zend_startup_modules)。 它的整个过程就是依次遍历每个模块,调用每个模块的模块初始化函数, 也就是在本小节前面所说的用宏PHP_MINIT_FUNCTION包含的内容。
php_disable_functions函数用来禁用PHP的一些函数。这些被禁用的函数来自PHP的配置文件的disable_functions变量。 其禁用的过程是调用zend_disable_function函数将指定的函数名从CG(function_table)函数表中删除。
php_disable_classes函数用来禁用PHP的一些类。这些被禁用的类来自PHP的配置文件的disable_classes变量。 其禁用的过程是调用zend_disable_class函数将指定的类名从CG(class_table)类表中删除。
ACTIVATION
Nach der Verarbeitung des dateibezogenen Inhalts ruft PHP php_request_startup auf, um den Anforderungsinitialisierungsvorgang durchzuführen. Der Anforderungsinitialisierungsvorgang erledigt neben dem Aufruf der Anforderungsinitialisierungsfunktion jedes in der Abbildung gezeigten Moduls auch viele andere Aufgaben. Der Hauptinhalt lautet wie folgt:
Die Funktion gc_reset wird verwendet, um den Garbage-Collection-Mechanismus zurückzusetzen. Dies ist natürlich erst nach PHP5.3 verfügbar.
Die Funktion init_compiler wird zum Initialisieren des Compilers verwendet, z. B. zum Löschen des Arrays, in dem der Opcode während des Kompilierungsprozesses platziert wird, zum Vorbereiten der für die Kompilierung verwendeten Datenstruktur usw.
Die Funktion init_executor wird verwendet, um den Zwischencode-Ausführungsprozess zu initialisieren. Während des Kompilierungsprozesses werden Funktionslisten, Klassenlisten usw. zur Kompilierungszeit in globalen Variablen gespeichert. Bei der Vorbereitung des Ausführungsprozesses werden diese Listen den ausgeführten globalen Variablen zugewiesen, z. B.: EG(function_table) = CG( function_table) ; Der Zwischencode wird im virtuellen Ausführungsstapel von PHP ausgeführt und diese Stapel werden während der Initialisierung gemeinsam initialisiert. Zusätzlich zum Stapel wird die Symboltabelle (EG (symbol_table)), in der Variablen gespeichert werden, mit einer Hashtabelle mit 50 Elementen initialisiert, und die EG (objects_store), in der Objekte gespeichert werden, wird mit 1024 Elementen initialisiert. Zusätzlich zu einigen der oben genannten Variablen verfügt die Ausführungsumgebung von PHP auch über Fehlerbehandlung, Ausnahmebehandlung usw., die alle hier initialisiert werden. Die über php.ini konfigurierten zend_extensions werden ebenfalls durchlaufen und die Aktivierungsfunktion wird hier aufgerufen.
Die Funktion sapi_activate wird verwendet, um SG (sapi_headers) und SG (request_info) zu initialisieren und einige Inhalte für die HTTP-Anforderungsmethode festzulegen, z. B. wann Die Anforderungsmethode lautet: Wenn HEAD, setze SG(request_info).headers_only=1; Die wichtigste Operation dieser Funktion besteht darin, die angeforderten Daten zu verarbeiten, die schließlich sapi_module.default_post_reader aufrufen. Der sapi_module.default_post_reader wurde über die Funktion php_startup_sapi_content_types in der vorherigen Modulinitialisierung registriert. Die Standardverarbeitungsfunktion ist die Funktion php_default_post_reader in der Datei main/php_content_types.c. Diese Funktion schreibt die Rohdaten von POST in die Variable $HTTP_RAW_POST_DATA.
Nach der Verarbeitung der Post-Daten liest PHP den Cookie-Wert über sapi_module.read_cookies. Im CLI-Modus lautet die Implementierung dieser Funktion sapi_cli_read_cookies, es gibt jedoch nur eine Rückgabe NULL im Funktionskörper;
Wenn die Aktivierungsfunktion im aktuellen Modus eingestellt ist, führen Sie diese Funktion aus, um SAPI zu aktivieren. Im CLI-Modus ist dieser Funktionszeiger auf NULL gesetzt.
Die Umgebungsinitialisierung bezieht sich hier auf die Initialisierung einiger Umgebungsvariablen, die im Benutzerbereich verwendet werden müssen. Die Umgebung umfasst hier die Serverumgebung, Anforderung Datenumgebung usw. . Die tatsächlichen Variablen, die wir verwenden, sind $_POST, $_GET, $_COOKIE, $_SERVER, $_ENV und $_FILES. Wie sapi_module.default_post_reader wird auch der Wert von sapi_module.treat_data über die Funktion php_startup_sapi_content_types während der Modulinitialisierung registriert. Die Standarddatenverarbeitungsfunktion ist die Funktion php_default_treat_data in der Datei main/php_variables.c.
Am Beispiel von $_COOKIE teilt die Funktion php_default_treat_data alle Cookies anhand des Trennzeichens auf und weist sie den entsprechenden Variablen zu.
PHP implementiert die Modulanforderungsinitialisierung über die Funktion zend_activate_modules, das heißt, wir sehen, wie wir im Bild die RINIT jeder Erweiterung aufrufen. Diese Funktion implementiert die Anforderungsinitialisierungsoperation des Moduls, indem sie alle in der Variablen module_registry registrierten Module durchläuft und ihre RINIT-Methode aufruft.
Ausführen
Die Funktion php_execute_script umfasst den gesamten Prozess der Ausführung eines PHP-Skripts.
Wenn eine PHP-Datei analysiert und ausgeführt werden muss, müssen möglicherweise drei Dateien ausgeführt werden, darunter eine Datei vor der Ausführung, die Hauptdatei, die derzeit ausgeführt werden muss, und eine Datei nach der Ausführung. Die beiden nicht aktuellen Dateien können in der Datei php.ini über die Parameter auto_prepend_file und auto_append_file festgelegt werden. Wenn diese beiden Parameter auf leer gesetzt sind, ist die entsprechende ausführbare Datei deaktiviert.
Für Dateien, die analysiert und ausgeführt werden müssen, werden lexikalische Analysen, Syntaxanalysen und Zwischencode-Generierungsvorgänge über zend_compile_file (compile_file-Funktion) durchgeführt und alle Zwischencodes dieser Datei zurückgegeben. Wenn die analysierte Datei einen gültigen Zwischencode generiert, rufen Sie zend_execute (Ausführungsfunktion) auf, um den Zwischencode auszuführen. Wenn bei der Ausführung Ausnahmen auftreten und der Benutzer die Behandlung dieser Ausnahmen definiert hat, werden diese Ausnahmebehandlungsfunktionen aufgerufen. Nachdem alle Vorgänge verarbeitet wurden, gibt PHP das Ergebnis über EG (return_value_ptr_ptr) zurück.
DEAKTIVIERUNG
Der Prozess des PHP-Schließens einer Anfrage besteht aus mehreren Schließvorgängen, und dieser Satz ist in der Funktion php_request_shutdown vorhanden. Diese Sammlung umfasst Folgendes:
zend_shutdown fährt die Zend-Engine herunter 🎜>Entsprechend dem Prozess in der Abbildung sollten wir den Modul-Schließvorgang für jedes Modul durchführen Nur eine zend_hash_graceful_reverse_destroy-Funktion, die die module_registry zerstört. Die Ursache liegt darin, dass sie beim Initialisieren der module_registry auch das Makro ZEND_MODULE_DTOR aufruft, das der Funktion module_destructor entspricht In dieser Funktion wird die Methode module_shutdown_func aufgerufen, bei der es sich um die vom Makro PHP_RSHUTDOWN_FUNCTION generierte Funktion handelt. Nach dem Schließen aller Module zerstört PHP weiterhin die globale Funktionstabelle, zerstört die globale Klassentabelle, verkauft die globale Variablentabelle usw durchläuft alle Elemente von zend_extensions bis zend_shutdown_extensions und ruft die Shutdown-Funktion jeder Erweiterung auf.
Auszug aus: http://www.php-internals.com/book/?p=chapt02/02-01-php-life-cycle -and-zend-engine
Das Obige stellt das Erlernen des PHP-Kernel-PHP-Lebenszyklus vor, einschließlich einiger Aspekte des Inhalts. Ich hoffe, dass es für Freunde hilfreich sein wird, die sich für PHP-Tutorials interessieren.