Rumah > Artikel > pembangunan bahagian belakang > Petikan yang terlalu berganda atau tidak, itulah persoalannya!
Baru-baru ini saya mendengar lagi bahawa orang PHP masih bercakap tentang petikan tunggal berbanding petikan berganda dan bahawa menggunakan petikan tunggal hanyalah pengoptimuman mikro tetapi jika anda terbiasa menggunakan petikan tunggal sepanjang masa anda akan menjimatkan banyak CPU kitaran!
"Semuanya telah dikatakan, tetapi belum oleh semua orang" – Karl Valentin
Dengan semangat inilah saya menulis artikel tentang topik yang sama yang dilakukan Nikita Popov 12 tahun yang lalu (jika anda membaca artikelnya, anda boleh berhenti membaca di sini).
PHP melakukan interpolasi rentetan, di mana ia mencari penggunaan pembolehubah dalam rentetan dan menggantikannya dengan nilai pembolehubah yang digunakan:
$juice = "apple"; echo "They drank some $juice juice."; // will output: They drank some apple juice.
Ciri ini terhad kepada rentetan dalam petikan berganda dan heredoc. Menggunakan petikan tunggal (atau nowdoc) akan menghasilkan hasil yang berbeza:
$juice = "apple"; echo 'They drank some $juice juice.'; // will output: They drank some $juice juice.
Lihat itu: PHP tidak akan mencari pembolehubah dalam rentetan petikan tunggal itu. Jadi kita boleh mula menggunakan petikan tunggal di mana-mana sahaja. Jadi orang mula mencadangkan perubahan seperti ini..
- $juice = "apple"; + $juice = 'apple';
.. kerana ia akan menjadi lebih pantas dan ia akan menjimatkan sekumpulan kitaran CPU dengan setiap pelaksanaan kod itu kerana PHP tidak mencari pembolehubah dalam rentetan petikan tunggal (yang tidak wujud dalam contoh pula) dan semua orang gembira, kes ditutup.
Jelas sekali terdapat perbezaan dalam menggunakan petikan tunggal berbanding petikan berganda, tetapi untuk memahami perkara yang sedang berlaku, kita perlu menggali lebih mendalam.
Walaupun PHP ialah bahasa yang ditafsirkan, ia menggunakan langkah penyusunan di mana bahagian tertentu bermain bersama untuk mendapatkan sesuatu yang sebenarnya boleh dilaksanakan oleh mesin maya, iaitu opcode. Jadi bagaimana kita boleh mendapatkan daripada kod sumber PHP kepada kod op?
Lexer mengimbas fail kod sumber dan memecahkannya kepada token. Contoh ringkas tentang maksud ini boleh didapati dalam dokumentasi fungsi token_get_all(). Kod sumber PHP hanya
T_OPEN_TAG (<?php ) T_ECHO (echo) T_WHITESPACE ( ) T_CONSTANT_ENCAPSED_STRING ("")
Kita boleh melihat tindakan ini dan bermain dengannya dalam coretan 3v4l.org ini.
Penghurai mengambil token ini dan menjana pepohon sintaks abstrak daripadanya. Perwakilan AST bagi contoh di atas kelihatan seperti ini apabila diwakili sebagai 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": "" } ] } ] }
Sekiranya anda mahu bermain dengan ini juga dan melihat bagaimana rupa AST untuk kod lain, saya dapati https://phpast.com/ oleh Ryan Chandler dan https://php-ast-viewer.com/ yang kedua-duanya menunjukkan kepada anda AST bagi sekeping kod PHP yang diberikan.
Pengkompil mengambil AST dan mencipta opcode. Opcode ialah perkara yang dilaksanakan oleh mesin maya, ia juga akan disimpan dalam OPcache jika anda mempunyai persediaan dan didayakan itu (yang saya sangat mengesyorkan).
Untuk melihat opcode, kami mempunyai berbilang pilihan (mungkin lebih banyak, tetapi saya tahu tiga ini):
$ echo '<?php echo "";' > foo.php $ php -dopcache.opt_debug_level=0x10000 foo.php $_main: ... 0000 ECHO string("") 0001 RETURN int(1) </p> <h2> Hipotesis </h2> <p>Berbalik kepada idea awal untuk menyimpan kitaran CPU apabila menggunakan petikan tunggal berbanding petikan berganda, saya rasa kita semua bersetuju bahawa ini hanya akan benar jika PHP akan menilai rentetan ini pada masa jalan untuk setiap permintaan tunggal.</p> <h2> Apa yang berlaku pada masa jalan? </h2> <p>Jadi mari kita lihat opcode yang dibuat oleh PHP untuk dua versi berbeza.</p> <p>Petikan berganda:<br> </p> <pre class="brush:php;toolbar:false"><?php echo "apple";
0000 ECHO string("apple") 0001 RETURN int(1)
lwn. petikan tunggal:
<?php echo 'apple';
0000 ECHO string("apple") 0001 RETURN int(1)
Hei tunggu, sesuatu yang pelik berlaku. Ini kelihatan sama! Ke manakah perginya pengoptimuman mikro saya?
Baiklah mungkin, hanya mungkin pelaksanaan pengendali opcode ECHO menghuraikan rentetan yang diberikan, walaupun tiada penanda atau sesuatu yang lain yang menyuruhnya berbuat demikian ... hmm ?
Mari cuba pendekatan yang berbeza dan lihat apa yang dilakukan oleh lexer untuk dua kes tersebut:
Petikan berganda:
T_OPEN_TAG (<?php ) T_ECHO (echo) T_WHITESPACE ( ) T_CONSTANT_ENCAPSED_STRING ("")
lwn. petikan tunggal:
Line 1: T_OPEN_TAG (<?php ) Line 1: T_ECHO (echo) Line 1: T_WHITESPACE ( ) Line 1: T_CONSTANT_ENCAPSED_STRING ('')
Token masih membezakan antara petikan berganda dan tunggal, tetapi menyemak AST akan memberi kita hasil yang sama untuk kedua-dua kes - satu-satunya perbezaan ialah rawValue dalam atribut nod Scalar_String, yang masih mempunyai petikan tunggal/ganda, tetapi nilai menggunakan petikan berganda dalam kedua-dua kes.
Mungkinkah, interpolasi rentetan itu sebenarnya dilakukan pada masa penyusunan?
Mari kita semak dengan contoh yang lebih "canggih":
<?php $juice="apple"; echo "juice: $juice";
Token untuk fail ini ialah:
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)
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:
<?php $juice = "apple"; echo "juice: $juice $juice"; echo "juice: ", $juice, " ", $juice; echo "juice: ".$juice." ".$juice;
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!
Atas ialah kandungan terperinci Petikan yang terlalu berganda atau tidak, itulah persoalannya!. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!