Trop de guillemets ou pas, telle est la question !

王林
Libérer: 2024-08-16 16:34:49
original
433 Les gens l'ont consulté

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 CPU. cycles!

"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).

De quoi s'agit-il?

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.
Copier après la connexion

Cette fonctionnalité est limitée aux chaînes entre guillemets doubles et heredoc. 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.
Copier après la connexion

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';
Copier après la connexion

.. 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 content, affaire classée.

Affaire close ?

É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 ?

Le lexeur

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 (<?php )
T_ECHO (echo)
T_WHITESPACE ( )
T_CONSTANT_ENCAPSED_STRING ("")
Copier après la connexion
Copier après la connexion

Nous pouvons voir cela en action et jouer avec dans cet extrait de 3v4l.org.

L'analyseur

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": ""
        }
      ]
    }
  ]
}
Copier après la connexion

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 les deux vous montrent l'AST d'un morceau de code PHP donné.

Le compilateur

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à) :

  1. utilisez l'extension de dumper logique vulcan. Il est également intégré à 3v4l.org
  2. utilisez phpdbg -p script.php pour vider les opcodes
  3. ou utilisez le paramètre INI opcache.opt_debug_level pour OPcache pour lui faire imprimer les opcodes
    • une valeur de 0x10000 génère les opcodes avant l'optimisation
    • une valeur de 0x20000 génère des opcodes après optimisation
$ echo '<?php echo "";' > foo.php
$ php -dopcache.opt_debug_level=0x10000 foo.php
$_main:
...
0000 ECHO string("")
0001 RETURN int(1)




</p>
<h2>
  
  
  Hypothèse
</h2>

<p>Pour en revenir à l'idée initiale d'économiser les cycles CPU 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.</p>

<h2>
  
  
  Que se passe-t-il au moment de l'exécution ?
</h2>

<p>Voyons donc quels opcodes PHP crée pour les deux versions différentes.</p>

<p>Guillemets doubles :<br>
</p>

<pre class="brush:php;toolbar:false"><?php echo "apple";
Copier après la connexion
0000 ECHO string("apple")
0001 RETURN int(1)
Copier après la connexion
Copier après la connexion

contre. guillemets simples :

<?php echo 'apple';
Copier après la connexion
0000 ECHO string("apple")
0001 RETURN int(1)
Copier après la connexion
Copier après la connexion

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 :

Guillemets doubles :

T_OPEN_TAG (<?php )
T_ECHO (echo)
T_WHITESPACE ( )
T_CONSTANT_ENCAPSED_STRING ("")
Copier après la connexion
Copier après la connexion

contre. guillemets simples :

Line 1: T_OPEN_TAG (<?php )
Line 1: T_ECHO (echo)
Line 1: T_WHITESPACE ( )
Line 1: T_CONSTANT_ENCAPSED_STRING ('')
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.

Nouvelle hypothèse

Se pourrait-il que l'interpolation des chaînes soit réellement effectuée au moment de la compilation ?

Vérifions avec un exemple un peu plus « sophistiqué » :

<?php
$juice="apple";
echo "juice: $juice";
Copier après la connexion

Les jetons pour ce fichier sont :

T_OPEN_TAG (<?php)
T_VARIABLE ($juice)
T_CONSTANT_ENCAPSED_STRING ("apple")
T_WHITESPACE ()
T_ECHO (echo)
T_WHITESPACE ( )
T_ENCAPSED_AND_WHITESPACE (juice: )
T_VARIABLE ($juice)
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.

Too double quote or not, that

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)
Copier après la connexion

This is different opcode than we had in our simple

Get to the point: should I concat or interpolate?

Let's have a look at these three different versions:

<?php
$juice = "apple";
echo "juice: $juice $juice";
echo "juice: ", $juice, " ", $juice;
echo "juice: ".$juice." ".$juice;
Copier après la connexion
  • the first version is using string interpolation
  • the second is using a comma separation (which AFAIK only works with echo and not with assigning variables or anything else)
  • and the third option uses string concatenation

The first opcode assigns the string "apple" to the variable $juice:

0000 ASSIGN CV0($juice) string("apple")
Copier après la connexion

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
Copier après la connexion

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)
Copier après la connexion

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
Copier après la connexion

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.

TLDR

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.

Final advice

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!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!