Tout récemment, j'ai encore entendu dire que les gens de PHP parlent encore de guillemets simples et de guillemets doubles et que l'utilisation de guillemets simples n'est qu'une micro-optimisation, mais si vous vous habituez à utiliser des guillemets simples tout le temps, vous économiserez beaucoup de cycles CPU !
"Tout a déjà été dit, mais pas encore par tout le monde" – Karl Valentin
C'est dans cet esprit que j'écris un article sur le même sujet que Nikita Popov faisait déjà il y a 12 ans (si vous lisez son article, vous pouvez arrêter de lire ici).
PHP effectue une interpolation de chaîne, dans laquelle il recherche l'utilisation de variables dans une chaîne et les remplace par la valeur de la variable utilisée :
$juice = "apple"; echo "They drank some $juice juice."; // will output: They drank some apple juice.
Cette fonctionnalité est limitée aux chaînes entre guillemets doubles et hérédoc. L'utilisation de guillemets simples (ou nowdoc) donnera un résultat différent :
$juice = "apple"; echo 'They drank some $juice juice.'; // will output: They drank some $juice juice.
Regardez ça : PHP ne recherchera pas de variables dans cette chaîne entre guillemets simples. Nous pourrions donc simplement commencer à utiliser des guillemets simples partout. Alors les gens ont commencé à suggérer des changements comme celui-ci ..
- $juice = "apple"; + $juice = 'apple';
.. parce que ce sera plus rapide et cela économiserait beaucoup de cycles CPU à chaque exécution de ce code car PHP ne recherche pas les variables dans les chaînes entre guillemets simples (qui sont de toute façon inexistantes dans l'exemple) et tout le monde est heureux, affaire classée.
Évidemment, il y a une différence entre l'utilisation de guillemets simples et de guillemets doubles, mais pour comprendre ce qui se passe, nous devons creuser un peu plus profondément.
Même si PHP est un langage interprété, il utilise une étape de compilation dans laquelle certaines parties jouent ensemble pour obtenir quelque chose que la machine virtuelle peut réellement exécuter, à savoir les opcodes. Alors, comment passer du code source PHP aux opcodes ?
Lexer analyse le fichier de code source et le décompose en jetons. Un exemple simple de ce que cela signifie peut être trouvé dans la documentation de la fonction token_get_all(). Un code source PHP de juste
T_OPEN_TAG (Copier après la connexion
Nous pouvons voir cela en action et jouer avec dans cet extrait de 3v4l.org.
L'analyseur prend ces jetons et génère à partir d'eux un arbre de syntaxe abstrait. Une représentation AST de l'exemple ci-dessus ressemble à ceci lorsqu'elle est représentée sous forme de JSON :
{ "data": [ { "nodeType": "Stmt_Echo", "attributes": { "startLine": 1, "startTokenPos": 1, "startFilePos": 6, "endLine": 1, "endTokenPos": 4, "endFilePos": 13 }, "exprs": [ { "nodeType": "Scalar_String", "attributes": { "startLine": 1, "startTokenPos": 3, "startFilePos": 11, "endLine": 1, "endTokenPos": 3, "endFilePos": 12, "kind": 2, "rawValue": "\"\"" }, "value": "" } ] } ] }
Au cas où vous voudriez également jouer avec cela et voir à quoi ressemble l'AST pour d'autres codes, j'ai trouvé https://phpast.com/ de Ryan Chandler et https://php-ast-viewer.com/ qui affichent tous deux vous l'AST d'un morceau de code PHP donné.
Le compilateur prend l'AST et crée des opcodes. Les opcodes sont les choses que la machine virtuelle exécute, c'est aussi ce qui sera stocké dans l'OPcache si vous avez cette configuration et activé (ce que je recommande fortement).
Pour afficher les opcodes, nous avons plusieurs options (peut-être plus, mais je connais ces trois-là) :
$ echo ' foo.php $ php -dopcache.opt_debug_level=0x10000 foo.php $_main: ... 0000 ECHO string("") 0001 RETURN int(1)Hypothèse
Pour en revenir à l'idée initiale d'économiser les cycles du processeur lors de l'utilisation de guillemets simples ou de guillemets doubles, je pense que nous sommes tous d'accord sur le fait que cela ne serait vrai que si PHP évaluait ces chaînes au moment de l'exécution pour chaque requête.
Que se passe-t-il au moment de l'exécution ?
Voyons donc quels opcodes PHP crée pour les deux versions différentes.
Citations doubles :
Copier après la connexion
0000 ECHO string("apple") 0001 RETURN int(1)
contre. guillemets simples :
Copier après la connexion
0000 ECHO string("apple") 0001 RETURN int(1)
Hé, attends, quelque chose de bizarre s'est produit. Cela a l'air identique ! Où est passée ma micro optimisation ?
Eh bien peut-être, juste peut-être que l'implémentation du gestionnaire d'opcode ECHO analyse la chaîne donnée, bien qu'il n'y ait pas de marqueur ou quelque chose d'autre qui lui dit de le faire... hmm ?
Essayons une approche différente et voyons ce que fait le lexer dans ces deux cas :
Citations doubles :
T_OPEN_TAG (Copier après la connexion
contre. guillemets simples :
Line 1: T_OPEN_TAG (Copier après la connexion
Les jetons font toujours la distinction entre les guillemets doubles et simples, mais vérifier l'AST nous donnera un résultat identique dans les deux cas - la seule différence est la valeur rawValue dans les attributs du nœud Scalar_String, qui a toujours les guillemets simples/doubles, mais la valeur utilise des guillemets doubles dans les deux cas.
Se pourrait-il que l'interpolation de chaîne soit réellement effectuée au moment de la compilation ?
Vérifions avec un exemple un peu plus "sophistiqué" :
Copier après la connexion
Les jetons pour ce fichier sont :
T_OPEN_TAG (Copier après la connexion
Look at the last two tokens! String interpolation is handled in the lexer and as such is a compile time thing and has nothing to do with runtime.
For completeness, let's have a look at the opcodes generated by this (after optimisation, using 0x20000):
0000 ASSIGN CV0($juice) string("apple") 0001 T2 = FAST_CONCAT string("juice: ") CV0($juice) 0002 ECHO T2 0003 RETURN int(1)
This is different opcode than we had in our simple
Let's have a look at these three different versions:
Copier après la connexion
The first opcode assigns the string "apple" to the variable $juice:
0000 ASSIGN CV0($juice) string("apple")
The first version (string interpolation) is using a rope as the underlying data structure, which is optimised to do as little string copies as possible.
0001 T2 = ROPE_INIT 4 string("juice: ") 0002 T2 = ROPE_ADD 1 T2 CV0($juice) 0003 T2 = ROPE_ADD 2 T2 string(" ") 0004 T1 = ROPE_END 3 T2 CV0($juice) 0005 ECHO T1
The second version is the most memory effective as it does not create an intermediate string representation. Instead it does multiple calls to ECHO which is a blocking call from an I/O perspective so depending on your use case this might be a downside.
0006 ECHO string("juice: ") 0007 ECHO CV0($juice) 0008 ECHO string(" ") 0009 ECHO CV0($juice)
The third version uses CONCAT/FAST_CONCAT to create an intermediate string representation and as such might use more memory than the rope version.
0010 T1 = CONCAT string("juice: ") CV0($juice) 0011 T2 = FAST_CONCAT T1 string(" ") 0012 T1 = CONCAT T2 CV0($juice) 0013 ECHO T1
So ... what is the right thing to do here and why is it string interpolation?
String interpolation uses either a FAST_CONCAT in the case of echo "juice: $juice"; or highly optimised ROPE_* opcodes in the case of echo "juice: $juice $juice";, but most important it communicates the intent clearly and none of this has been bottle neck in any of the PHP applications I have worked with so far, so none of this actually matters.
String interpolation is a compile time thing. Granted, without OPcache the lexer will have to check for variables used in double quoted strings on every request, even if there aren't any, waisting CPU cycles, but honestly: The problem is not the double quoted strings, but not using OPcache!
However, there is one caveat: PHP up to 4 (and I believe even including 5.0 and maybe even 5.1, I don't know) did string interpolation at runtime, so using these versions ... hmm, I guess if anyone really still uses PHP 5, the same as above applies: The problem is not the double quoted strings, but the use of an outdated PHP version.
Update to the latest PHP version, enable OPcache and live happily ever after!
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!