Cet article a été revu par des pairs par Younes Rafie. Merci à tous les pairs examinateurs de SitePoint pour avoir fait du contenu SitePoint le meilleur possible!
Inspiré par un article récent sur la façon dont le code Ruby s'exécute, cet article couvre le processus d'exécution du code PHP.
Il se passe beaucoup de choses sous le capot lorsque nous exécutons un morceau de code PHP. D'une manière générale, l'interprète PHP passe par quatre étapes lors de l'exécution du code:
Cet article parcourra ces étapes et montrera comment nous pouvons voir la sortie de chaque étape pour vraiment voir ce qui se passe. Notez que bien que certaines des extensions utilisées devraient déjà faire partie de votre installation PHP (comme le tokenizer et l'opcache), d'autres devront être installés et activées manuellement (comme PHP-AST et VLD).
lexing (ou tokenising) est le processus de transformation d'une chaîne (code source PHP, dans ce cas) en une séquence de jetons. Un jeton est simplement un identifiant nommé pour la valeur qu'il a égalée. PHP utilise RE2C pour générer son lexer à partir du fichier de définition zend_language_scanner.l.
Nous pouvons voir la sortie de l'étape de lexing via l'extension du tokenizer:
$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>
Sorties:
$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>
Il y a quelques points notables de la sortie ci-dessus. Le premier point est que toutes les pièces du code source ne sont pas nommées de jetons. Au lieu de cela, certains symboles sont considérés comme des jetons en eux-mêmes (tels que =, ;,:,?, Etc.). Le deuxième point est que le Lexer fait en fait un peu plus que simplement la sortie d'un flux de jetons. Il est également, dans la plupart des cas, stocke le lexème (la valeur correspondante par le jeton) et le numéro de ligne du jeton correspondant (qui est utilisé pour des choses comme les traces de pile).
L'analyseur est également généré, cette fois avec le bison via un fichier de grammaire BNF. PHP utilise une grammaire sans contexte LALR (1) (regardez vers l'avenir, gauche à droite). La partie à l'avance signifie simplement que l'analyseur est en mesure de regarder n jetons à venir (1, dans ce cas) pour résoudre les ambiguïtés qu'elle peut rencontrer pendant l'analyse. La partie de gauche à droite signifie qu'elle analyse le flux de jeton de gauche à droite.
L'étape d'analyse générée prend le flux de jetons du Lexer en entrée et a deux travaux. Il vérifie d'abord la validité de l'ordre des jetons en essayant de les faire correspondre à l'une des règles de grammaire définies dans son fichier de grammaire BNF. Cela garantit que des constructions de langage valides sont formées par les jetons dans le flux de jetons. Le deuxième travail de l'analyseur consiste à générer l'arbre de syntaxe abstrait (AST) - une vue d'arbre du code source qui sera utilisé au cours de la prochaine étape (compilation).
Nous pouvons afficher une forme de l'AST produit par l'analyseur en utilisant l'extension PHP-AST. L'AST interne n'est pas directement exposé car il n'est pas particulièrement «propre» pour fonctionner (en termes de cohérence et d'utilisation générale), et donc l'extension PHP-AST effectue quelques transformations sur elle pour rendre plus agréable à travailler.
Jetons un coup d'œil à l'AST pour un morceau de code rudimentaire:
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) ";"
Sortie:
$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>
Les nœuds d'arborescence (qui sont généralement de type ASTNode) ont plusieurs propriétés:
La sortie AST de cette étape est prête à travailler pour des outils tels que les analyseurs de code statique (par exemple Phan).
L'étape de compilation consomme l'AST, où il émet des opcodes en traversant récursivement l'arbre. Cette étape effectue également quelques optimisations. Il s'agit notamment de résoudre certains appels de fonction avec des arguments littéraux (tels que Strlen ("ABC") à int (3)) et le pliage des expressions mathématiques constantes (telles que 60 * 60 * 24 à int (86400)).
Nous pouvons inspecter la sortie OPCode à ce stade de plusieurs façons, y compris avec Opcache, VLD et PHPDBG. Je vais utiliser VLD pour cela, car je pense que la sortie est plus sympathique à regarder.
Voyons quelle est la sortie pour le script file.php suivant:
$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>
Exécution de la commande suivante:
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) ";"
Notre sortie est:
$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>
Les OPCodes ressemblent au code source d'origine, suffisamment pour suivre les opérations de base. (Je ne vais pas plonger dans les détails d'Opcodes dans cet article, car cela prendrait plusieurs articles entiers en soi.) en a fait en résolvant la condition constante (php_version === '7.1.0-dev') à true.
OPCACHE fait plus que la simple mise en cache OPCodes (en contournant ainsi les étapes de lexing, d'analyse et de compilation). Il regorge également de nombreux niveaux d'optimisations différents. Passons au niveau d'optimisation à quatre passes pour voir ce qui sort:
Commande:
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 ) ) ) )
Sortie:
<span>if (PHP_VERSION === '7.1.0-dev') { </span> <span>echo 'Yay', PHP_EOL; </span><span>} </span>
Nous pouvons voir que la condition constante a été supprimée et que les deux instructions d'écho ont été compactées en une seule instruction. Ce ne sont qu'un avant-goût des nombreuses optimisations que l'opcache s'applique lors de l'exécution de passes sur les opcodes d'un script. Je ne passerai pas par les différents niveaux d'optimisation dans cet article, car ce serait également un article en soi.
L'étape finale est l'interprétation des opcodes. C'est là que les opcodes sont exécutés sur la machine virtuelle Zend Engine (ZE). Il y a en fait très peu de choses à dire sur cette étape (d'un point de vue de haut niveau, au moins). La sortie est à peu près tout ce que votre script PHP sort via des commandes telles que Echo, Print, Var_dump, etc.
Donc, au lieu de creuser dans quelque chose de complexe à ce stade, voici un fait amusant: PHP se demande comme une dépendance lors de la génération de sa propre machine virtuelle. En effetConclusion
J'espère que cet article a contribué à vous fournir une meilleure compréhension holistique de l'interprète de PHP, ainsi que l'importance de l'extension Opcache (pour ses capacités de mise en cache et d'optimisation).
L'interprète PHP joue un rôle crucial dans le processus d'exécution PHP. Il est responsable de la conversion du code source PHP en code lisible par machine. L'interprète lit la ligne de script PHP ligne par ligne, interprète chaque ligne et effectue les opérations nécessaires. Il est également responsable de la gestion des erreurs et des exceptions pendant le processus d'exécution. L'interprète PHP est un composant clé de l'environnement d'exécution PHP, qui comprend également le serveur Web et les extensions PHP.
Le moteur PHP est le cœur de le processus d'exécution PHP. Il est responsable de l'analyse du script PHP, de la compilation en bytecode, puis de l'exécution du bytecode. Le moteur PHP utilise un processus en deux étapes pour exécuter des scripts PHP. Tout d'abord, il analyse le script PHP et le convertit en une arborescence de syntaxe abstraite (AST). Ensuite, il compile l'AST en bytecode et l'exécute. Le moteur PHP comprend également un gestionnaire de mémoire et un collecteur de déchets pour gérer l'utilisation de la mémoire pendant le processus d'exécution.
La commande PHP de PHP - Interface de ligne (CLI) et l'interface du serveur Web sont deux façons différentes d'exécuter des scripts PHP. La CLI est utilisée pour exécuter des scripts PHP à partir de la ligne de commande, tandis que l'interface du serveur Web est utilisée pour exécuter des scripts PHP en réponse aux demandes Web. La principale différence entre les deux interfaces est la façon dont ils gèrent l'entrée et la sortie. Dans la CLI, l'entrée est lue à partir de la ligne de commande et la sortie est écrite sur la console. Dans l'interface du serveur Web, l'entrée est lue à partir de la demande HTTP et la sortie est écrite dans la réponse HTTP.
PHP a une gestion des erreurs robuste mécanisme qui lui permet de gérer les erreurs pendant le processus d'exécution. Lorsqu'une erreur se produit, PHP génère un message d'erreur et l'envoie au gestionnaire d'erreur. Le gestionnaire d'erreur peut afficher le message d'erreur, le enregistrer ou l'ignorer, en fonction des paramètres de rapport d'erreur. PHP prend également en charge la manipulation des exceptions, ce qui lui permet de gérer les erreurs de manière plus structurée et gérable.
Les extensions PHP sont des modules qui ajoutent de nouvelles fonctionnalités et fonctionnalités au langage PHP. Ils sont chargés dans l'environnement d'exécution PHP pendant le processus d'exécution et peuvent être utilisés pour effectuer une large gamme de tâches, de l'accès à la base de données au traitement d'image. Les extensions de PHP sont écrites en C et sont compilées en code machine, ce qui les rend très rapides et efficaces. Ils sont un composant clé de l'écosystème PHP et contribuent à sa flexibilité et à sa puissance.
PHP utilise plusieurs techniques pour optimiser le processus d'exécution. L'une de ces techniques est la mise en cache Opcode, qui implique le stockage du bytecode généré par le moteur PHP en mémoire afin qu'il puisse être réutilisé dans les exécutions suivantes. Cela élimine la nécessité d'analyser et de compiler le script PHP à chaque fois qu'il est exécuté, ce qui entraîne des améliorations de performances significatives. PHP utilise également la compilation JIT-Time (JIT), qui consiste à compiler des bytecodes dans le code machine à l'exécution pour améliorer davantage les performances.
PHP possède un gestionnaire de mémoire intégré qui gère l'allocation de mémoire et la transmission pendant le processus d'exécution. Le gestionnaire de mémoire alloue la mémoire pour les variables et les structures de données selon les besoins, et traite la mémoire lorsqu'elle n'est plus nécessaire. PHP a également un collecteur de déchets qui libère automatiquement de la mémoire qui n'est plus utilisée. Cela aide à prévenir les fuites de mémoire et à maintenir l'utilisation de la mémoire sous contrôle.
Le serveur Web joue un rôle clé dans l'exécution PHP processus. Il est responsable de la gestion des demandes HTTP, de l'exécution de scripts PHP en réponse à ces demandes et de renvoyer des réponses HTTP au client. Le serveur Web travaille en étroite collaboration avec l'interprète PHP et le moteur PHP pour exécuter des scripts PHP et générer des pages Web dynamiques. Les serveurs Web les plus couramment utilisés pour PHP sont Apache et Nginx.
PHP a une prise en charge intégrée pour une large gamme de bases de données, y compris MySQL, PostgreSQL et SQLite. Il utilise des extensions spécifiques à la base de données pour interagir avec ces bases de données pendant le processus d'exécution. Ces extensions fournissent un ensemble de fonctions qui peuvent être utilisées pour se connecter à la base de données, exécuter les requêtes SQL, récupérer les résultats et gérer les erreurs. PHP prend également en charge l'extension PDO (PHP Data Objectts), qui fournit une interface de base de données-indésirable pour les interactions de base de données.
PHP a une prise en charge intégrée pour la gestion de session, ce qui lui permet de maintenir l'état entre les différentes demandes HTTP. Lorsqu'une session est démarrée, PHP crée un identifiant de session unique et le stocke dans un cookie sur le navigateur du client. Cet ID de session est ensuite renvoyé au serveur avec chaque demande suivante, permettant à PHP d'identifier le client et de récupérer les données de session correspondantes. Les fonctionnalités de gestion de session de PHP facilitent la mise en œuvre de l'authentification des utilisateurs, des paniers d'achat et d'autres fonctionnalités d'état dans les applications Web.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!