Heim >Backend-Entwicklung >PHP-Tutorial >Wie PHP ausführt - vom Quellcode zum Rendern
Dieser Artikel wurde von Younes Rafie geprüft. Vielen Dank an alle Peer -Rezensenten von SitePoint, die SitePoint -Inhalte so gut wie möglich gemacht haben!
Inspiriert von einem kürzlich durchgeführten Artikel darüber, wie Ruby Code ausgeführt wird, deckt dieser Artikel den Ausführungsprozess für PHP -Code ab.
, wenn wir ein Stück PHP -Code ausführen, ist viel los. Im Großen und Ganzen durchläuft der PHP -Interpreter vier Stufen, wenn der Code ausgeführt wird:
Dieser Artikel wird durch diese Phasen überfliegen und zeigen, wie wir die Ausgabe aus jeder Phase anzeigen können, um wirklich zu sehen, was los ist. Beachten Sie, dass einige der verwendeten Erweiterungen bereits Teil Ihrer PHP-Installation sein sollten (z.
Stufe 1 - Lexingwir können die Ausgabe der Lexingstufe über die Tokenizer -Erweiterung sehen:
$code = <<<'code' <span><span><?php </span></span><span><span>$a = 1; </span></span><span>code<span>; </span></span><span> </span><span><span>$tokens = token_get_all($code); </span></span><span> </span><span><span>foreach ($tokens as $token) { </span></span><span> <span>if (is_array($token)) { </span></span><span> <span>echo "Line <span><span>{$token[2]}</span>: "</span>, token_name($token[0]), " ('<span><span>{$token[1]}</span>')"</span>, PHP_EOL; </span></span><span> <span>} else { </span></span><span> <span>var_dump($token); </span></span><span> <span>} </span></span><span><span>} </span></span>Ausgänge:
$code = <<<'code' <span><span><?php </span></span><span><span>$a = 1; </span></span><span>code<span>; </span></span><span> </span><span><span>$tokens = token_get_all($code); </span></span><span> </span><span><span>foreach ($tokens as $token) { </span></span><span> <span>if (is_array($token)) { </span></span><span> <span>echo "Line <span><span>{$token[2]}</span>: "</span>, token_name($token[0]), " ('<span><span>{$token[1]}</span>')"</span>, PHP_EOL; </span></span><span> <span>} else { </span></span><span> <span>var_dump($token); </span></span><span> <span>} </span></span><span><span>} </span></span>
Es gibt ein paar bemerkenswerte Punkte aus der obigen Ausgabe. Der erste Punkt ist, dass nicht alle Teile des Quellcodes als Token bezeichnet werden. Stattdessen werden einige Symbole an und für sich als Token angesehen (wie z. B. =,;,:,? Usw.). Der zweite Punkt ist, dass der Lexer tatsächlich etwas mehr tut, als nur einen Tokenstrom auszugeben. In den meisten Fällen speichert es auch das Lexem (der vom Token übereinstimmende Wert) und die Zeilennummer des übereinstimmenden Tokens (der für Stapelspuren verwendet wird).
Der Parser wird ebenfalls generiert, diesmal mit Bison über eine BNF -Grammatikdatei. PHP verwendet eine kontextfreie Grammatik von LALR (1) (nach vorne, links nach rechts). Der Aussichtsteil bedeutet einfach, dass der Parser in der Lage ist, N -Token (1, in diesem Fall) voranzutreiben, um Unklarheiten zu lösen, auf die er während der Parsen begegnen kann. Der Teil von links nach rechts bedeutet, dass er den Token-Stream von links nach rechts analysiert.
Die erzeugte Parserstufe nimmt den Token -Stream aus dem Lexer als Eingabe und hat zwei Jobs. Es überprüft zunächst die Gültigkeit des Token -Ordens, indem versucht wird, sie gegen eine der in seiner BNF -Grammatikdatei definierten Grammatikregeln zu vereinen. Dies stellt sicher, dass die Token im Token -Strom gültige Sprachkonstrukte gebildet werden. Der zweite Auftrag des Parsers besteht darin, die abstrakte Syntaxbaum (AST) zu generieren - eine Baumansicht des Quellcodes, der während der nächsten Stufe verwendet wird (Zusammenstellung).
Wir können eine Form von den vom Parser erzeugten AST mit der PHP-Ast-Erweiterung anzeigen. Der interne AST ist nicht direkt freigelegt, da es nicht besonders „sauber“ ist, mit (in Bezug auf Konsistenz und allgemeine Benutzerfreundlichkeit) zu arbeiten, und daher führt die PHP-Ast-Erweiterung einige Transformationen durch, um es schöner zu machen, mit zu arbeiten.
Schauen wir uns das AST für ein rudimentäres Stück Code an:
Line 1: T_OPEN_TAG ('<?php ') Line 2: T_VARIABLE ('$a') Line 2: T_WHITESPACE (' ') string(1) "=" Line 2: T_WHITESPACE (' ') Line 2: T_LNUMBER ('1') string(1) ";"
Ausgabe:
$code = <<<'code' <span><span><?php </span></span><span><span>$a = 1; </span></span><span>code<span>; </span></span><span> </span><span><span>print_r(ast<span>\parse_code</span>($code, 30)); </span></span>
Die Baumknoten (die typischerweise vom Typ Astnode sind) haben mehrere Eigenschaften:
Die AST -Ausgabe dieser Phase ist praktisch für Tools wie statische Codeanalysatoren (z. B. Phan).
Die Kompilierungsphase verbraucht das AST, wo sie Opcodes durch rekursives Durchqueren des Baumes emittiert. Diese Phase führt auch einige Optimierungen durch. Dazu gehören die Auflösung einiger Funktionsaufrufe mit wörtlichen Argumenten (wie Strlen ("ABC") bis int (3)) und Faltungskonstante mathematische Ausdrücke (wie 60 * 60 * 24 bis int (86400)).
Wir können die Opcode -Ausgabe in dieser Phase in vielerlei Hinsicht inspizieren, einschließlich Opcache, VLD und PHPDBG. Ich werde VLD dafür verwenden, da ich der Meinung bin
Lassen Sie uns sehen, wie die Ausgabe für das folgende Datei -Skript ist:
$code = <<<'code' <span><span><?php </span></span><span><span>$a = 1; </span></span><span>code<span>; </span></span><span> </span><span><span>$tokens = token_get_all($code); </span></span><span> </span><span><span>foreach ($tokens as $token) { </span></span><span> <span>if (is_array($token)) { </span></span><span> <span>echo "Line <span><span>{$token[2]}</span>: "</span>, token_name($token[0]), " ('<span><span>{$token[1]}</span>')"</span>, PHP_EOL; </span></span><span> <span>} else { </span></span><span> <span>var_dump($token); </span></span><span> <span>} </span></span><span><span>} </span></span>Ausführen des folgenden Befehls:
Line 1: T_OPEN_TAG ('<?php ') Line 2: T_VARIABLE ('$a') Line 2: T_WHITESPACE (' ') string(1) "=" Line 2: T_WHITESPACE (' ') Line 2: T_LNUMBER ('1') string(1) ";"Unsere Ausgabe ist:
$code = <<<'code' <span><span><?php </span></span><span><span>$a = 1; </span></span><span>code<span>; </span></span><span> </span><span><span>print_r(ast<span>\parse_code</span>($code, 30)); </span></span>Die Opcodes ähneln irgendwie dem ursprünglichen Quellcode, um den Grundvorgängen zu folgen. (Ich werde mich nicht mit den Details von Opcodes in diesem Artikel befassen, da dies mehrere gesamte Artikel an sich annehmen würde.) Auf Opcode -Ebene im obigen Skript wurden keine Optimierungen angewendet - aber wie wir sehen können, können wir die Kompilierungsphase sehen hat einige gemacht, indem er die konstante Bedingung auflöst (php_version === '7.1.0-dev') zu true.
opcache kann mehr als nur Opcodes zwischengespeichern (damit die Lexing-, Parsen- und Kompilierungsstufen umgehen). Es packt auch viele verschiedene Optimierungsstufen mit sich. Lassen Sie uns die Optimierungsstufe auf vier Pässe erheben, um zu sehen, was herauskommt:
Befehl:
ast\Node Object ( [kind] => 132 [flags] => 0 [lineno] => 1 [children] => Array ( [0] => ast\Node Object ( [kind] => 517 [flags] => 0 [lineno] => 2 [children] => Array ( [var] => ast\Node Object ( [kind] => 256 [flags] => 0 [lineno] => 2 [children] => Array ( [name] => a ) ) [expr] => 1 ) ) ) )Ausgabe:
<span>if (PHP_VERSION === '7.1.0-dev') { </span> <span>echo 'Yay', PHP_EOL; </span><span>} </span>Wir können sehen, dass der konstante Zustand entfernt wurde und die beiden Echo -Anweisungen in eine einzige Anweisung zusammengefasst wurden. Dies sind nur ein Vorgeschmack auf die vielen Optimierungen, die Opcache bei der Ausführung von Passer über die Opcodes eines Skripts gilt. Ich werde die verschiedenen Optimierungsstufen in diesem Artikel jedoch nicht durchgehen, da dies auch ein Artikel für sich wäre.
Stufe 4 - Interpretation
, anstatt zu diesem Zeitpunkt in irgendetwas Komplexes zu graben, ist hier eine lustige Tatsache: PHP benötigt sich selbst als Abhängigkeit, wenn er seine eigene VM generiert. Dies liegt daran
Schlussfolgerung
Ich hoffe, dieser Artikel hat Ihnen dazu beigetragen, Ihnen ein besseres ganzheitliches Verständnis des Interpreters von PHP zu vermitteln und die Bedeutung der Opcache -Erweiterung (sowohl für seine Ausgeschnittene als auch für Optimierungsfähigkeiten) zu zeigen.
Wie optimiert PHP den Ausführungsprozess? Eine dieser Techniken ist das Opcode -Caching, bei dem die von der PHP -Engine im Speicher erzeugte Bytecode gespeichert werden, damit sie in nachfolgenden Ausführungen wiederverwendet werden kann. Dadurch wird die Notwendigkeit beseitigt, das PHP -Skript jedes Mal zu analysieren und zu kompilieren, wenn es zu erheblichen Leistungsverbesserungen führt. PHP verwendet auch Just-in-Time-Zusammenstellung (Just-in-Time), bei der Bytecode zur Laufzeit in den Maschinencode kompiliert wird, um die Leistung weiter zu verbessern. PHP verfügt über einen integrierten Speichermanager, der während des Ausführungsprozesses die Speicherzuweisung und Deallokation übernimmt. Der Speichermanager weist nach Bedarf den Speicher für Variablen und Datenstrukturen zu und bearbeitet den Speicher, wenn er nicht mehr benötigt wird. PHP hat auch einen Müllsammler, der den Speicher automatisch befreit, der nicht mehr verwendet wird. Dies hilft, Speicherlecks zu verhindern und die Speicherverwendung unter Kontrolle zu halten.
Das obige ist der detaillierte Inhalt vonWie PHP ausführt - vom Quellcode zum Rendern. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!