Artikel ini dikaji semula oleh Younes Rafie. Terima kasih kepada semua pengulas rakan sebaya SitePoint untuk membuat kandungan SitePoint yang terbaik boleh!
Takeaways Key
lexing
lexing (atau tokenizing) adalah proses mengubah rentetan (kod sumber PHP, dalam kes ini) menjadi urutan token. Token hanyalah pengenal yang dinamakan untuk nilai yang dipadankannya. PHP menggunakan RE2C untuk menghasilkan lexernya dari fail definisi Zend_Language_Scanner.l.
output:
$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>
Terdapat beberapa mata yang patut diberi perhatian dari output di atas. Titik pertama ialah tidak semua kepingan kod sumber dinamakan token. Sebaliknya, beberapa simbol dianggap sebagai token dalam dan dari diri mereka (seperti =,,:,?, Dll). Titik kedua adalah bahawa Lexer sebenarnya melakukan sedikit lebih daripada sekadar mengeluarkan aliran token. Ia juga, dalam kebanyakan kes, menyimpan lexeme (nilai yang dipadankan dengan token) dan nombor garis token yang dipadankan (yang digunakan untuk perkara -perkara seperti jejak stack).
parser juga dihasilkan, kali ini dengan bison melalui fail tatabahasa BNF. PHP menggunakan tatabahasa bebas konteks LALR (1) (lihat ke hadapan, kiri-ke-kanan). Bahagian Look Ahead hanya bermakna bahawa parser dapat melihat token N di hadapan (1, dalam kes ini) untuk menyelesaikan kekaburan yang mungkin ditemui semasa parsing. Bahagian kiri-ke-kanan bermaksud bahawa ia mengasingkan aliran token dari kiri ke kanan.
Tahap parser yang dihasilkan mengambil aliran token dari lexer sebagai input dan mempunyai dua pekerjaan. Ia terlebih dahulu mengesahkan kesahihan perintah token dengan cuba memadankan mereka terhadap mana -mana peraturan tatabahasa yang ditakrifkan dalam fail tatabahasa BNFnya. Ini memastikan bahawa pembinaan bahasa yang sah sedang dibentuk oleh token dalam aliran token. Tugas kedua parser adalah untuk menjana Abstrak Sintaks Tree (AST) - pandangan pokok kod sumber yang akan digunakan pada peringkat seterusnya (kompilasi).
kita boleh melihat bentuk AST yang dihasilkan oleh parser menggunakan lanjutan PHP-CAS. AST dalaman tidak didedahkan secara langsung kerana ia tidak "bersih" untuk bekerja dengan (dari segi konsistensi dan kebolehgunaan umum), dan oleh itu lanjutan PHP-CAS melakukan beberapa transformasi di atasnya untuk menjadikannya lebih baik untuk bekerja dengan.
mari kita lihat AST untuk sekeping kod asas:
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) ";"
output:
$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>
Tahap kompilasi menggunakan AST, di mana ia memancarkan opcode dengan rekursif melintasi pokok. Tahap ini juga melakukan beberapa pengoptimuman. Ini termasuk menyelesaikan beberapa panggilan fungsi dengan hujah -hujah literal (seperti Strlen ("ABC") kepada Int (3)) dan melipat ekspresi matematik berterusan (seperti 60 * 60 * 24 ke int (86400)).
kita boleh memeriksa output opcode pada tahap ini dalam beberapa cara, termasuk dengan Opcache, VLD, dan PHPDBG. Saya akan menggunakan VLD untuk ini, kerana saya rasa output lebih mesra untuk dilihat.
Mari lihat apakah output untuk skrip file.php berikut:
$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>
Melaksanakan perintah berikut:
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) ";"
output kami ialah:
$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>
jenis opcodes menyerupai kod sumber asal, cukup untuk diikuti bersama dengan operasi asas. (Saya tidak akan menyelidiki butir -butir opcode dalam artikel ini, kerana itu akan mengambil beberapa artikel keseluruhannya.) Tiada pengoptimuman digunakan pada tahap opcode dalam skrip di atas - tetapi seperti yang dapat kita lihat, fasa penyusunan telah membuat beberapa dengan menyelesaikan keadaan malar (php_version === '7.1.0-dev') kepada benar.
Opcache lebih daripada sekadar caching opcodes (dengan itu melangkaui tahap lexing, parsing, dan kompilasi). Ia juga membungkus dengan banyak tahap pengoptimuman yang berbeza. Mari kita tukar tahap pengoptimuman kepada empat pas untuk melihat apa yang keluar:
Command:
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 ) ) ) )
output:
<span>if (PHP_VERSION === '7.1.0-dev') { </span> <span>echo 'Yay', PHP_EOL; </span><span>} </span>
Peringkat 4 - Tafsiran
jadi bukannya menggali apa -apa yang kompleks pada tahap ini, inilah fakta yang menyeronokkan: PHP memerlukan dirinya sebagai kebergantungan apabila menghasilkan VM sendiri. Ini kerana VM dihasilkan oleh skrip PHP, kerana ia lebih mudah untuk menulis dan lebih mudah untuk dikekalkan.
Kesimpulan
Saya harap artikel ini telah membantu memberi anda pemahaman holistik yang lebih baik mengenai penterjemah PHP, serta menunjukkan kepentingan pelanjutan Opcache (untuk kedua -dua kebolehan caching dan pengoptimumannya).
Soalan Lazim (Soalan Lazim) Mengenai Proses Pelaksanaan PHPBagaimana PHP mengoptimumkan proses pelaksanaan? Salah satu daripada teknik ini ialah Caching Opcode, yang melibatkan menyimpan bytecode yang dihasilkan oleh enjin PHP dalam ingatan supaya ia boleh digunakan semula dalam eksekusi berikutnya. Ini menghapuskan keperluan untuk menghuraikan dan menyusun skrip PHP setiap kali ia dilaksanakan, mengakibatkan peningkatan prestasi yang signifikan. PHP juga menggunakan kompilasi Just-in-Time (JIT), yang melibatkan penyusun bytecode ke dalam kod mesin semasa runtime untuk meningkatkan prestasi. PHP mempunyai pengurus memori terbina dalam yang mengendalikan peruntukan memori dan deallocation semasa proses pelaksanaan. Pengurus memori memperuntukkan memori untuk pembolehubah dan struktur data seperti yang diperlukan, dan memori deallocates apabila ia tidak lagi diperlukan. PHP juga mempunyai pemungut sampah yang secara automatik membebaskan memori yang tidak lagi digunakan. Ini membantu mengelakkan kebocoran memori dan mengekalkan penggunaan memori di bawah kawalan.
Atas ialah kandungan terperinci Bagaimana PHP dijalankan - dari kod sumber untuk diberikan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!