Mengapa dua konstruksi?
Kebenaran tentang cetak dan gema adalah bahwa meskipun mereka tampak oleh pengguna sebagai dua konstruksi berbeda, keduanya sama-sama bernuansa gema jika Anda sampai pada dasar-dasarnya, yaitu melihat kode sumber internal. Kode sumber itu melibatkan parser serta penangan opcode. Pertimbangkan tindakan sederhana seperti menampilkan angka nol. Apakah Anda menggunakan gema atau cetak, penangan yang sama "ZEND_ECHO_SPEC_CONST_HANDLER" akan dipanggil. Pawang untuk mencetak melakukan satu hal sebelum memanggil pawang untuk gema, ia memastikan bahwa nilai balik untuk cetakan adalah 1, sebagai berikut:
ZVAL_LONG(&EX_T(opline->result.var).tmp_var, 1);
(lihat di sini untuk referensi )
Nilai baliknya adalah kenyamanan jika seseorang ingin menggunakan cetakan dalam ekspresi bersyarat. Kenapa 1 dan bukan 100? Baik di PHP kebenaran dari 1 atau 100 adalah sama, yaitu benar, sedangkan 0 dalam konteks boolean sama dengan nilai yang salah. Dalam PHP semua nilai bukan nol (positif dan negatif) adalah nilai kebenaran dan ini berasal dari warisan Perl PHP.
Tapi, jika ini masalahnya, maka orang mungkin bertanya-tanya mengapa gema mengambil beberapa argumen sedangkan cetak hanya dapat menangani satu. Untuk jawaban ini kita perlu beralih ke parser, khususnya file zend_language_parser.y . Anda akan mencatat bahwa echo memiliki fleksibilitas bawaan sehingga dapat mencetak satu atau beberapa ekspresi (lihat di sini ). sedangkan cetak dibatasi untuk mencetak hanya satu ekspresi (lihat di sana ).
Sintaksis
Dalam bahasa pemrograman C dan bahasa yang dipengaruhi olehnya seperti PHP, ada perbedaan antara pernyataan dan ekspresi. Secara sintaksis, echo expr, expr, ... expr
adalah pernyataan sementara print expr
adalah ekspresi karena ia mengevaluasi suatu nilai. Oleh karena itu, seperti pernyataan lainnya, echo expr
berdiri sendiri dan tidak mampu dimasukkan dalam ekspresi:
5 + echo 6; // syntax error
Sebaliknya,, print expr
dapat sendirian membentuk pernyataan:
print 5; // valid
Atau, jadilah bagian dari ekspresi:
$x = (5 + print 5); // 5
var_dump( $x ); // 6
Seseorang mungkin tergoda untuk berpikir print
seolah-olah itu adalah operator yang tidak waspada, seperti !
atau ~
bagaimanapun bukan operator. Apa yang !, ~ and print
sama adalah bahwa mereka semua dibangun ke dalam PHP dan masing-masing hanya membutuhkan satu argumen. Anda dapat menggunakan print
untuk membuat kode aneh tapi valid berikut:
<?php
print print print print 7; // 7111
Pada pandangan pertama hasilnya mungkin tampak aneh bahwa pernyataan cetak terakhir mencetak operan dari '7' pertama . Tetapi, jika Anda menggali lebih dalam dan melihat opcodes yang sebenarnya masuk akal:
line # * op fetch ext return operands
---------------------------------------------------------------------------------
3 0 > PRINT ~0 7
1 PRINT ~1 ~0
2 PRINT ~2 ~1
3 PRINT ~3 ~2
4 FREE ~3
5 > RETURN 1
Opcode pertama yang dihasilkan adalah yang sesuai dengan 'print 7'. '~ 0' adalah variabel sementara yang nilainya 1. Variabel itu menjadi dan operan untuk opcode cetak berikutnya yang pada gilirannya mengembalikan variabel sementara dan proses berulang. Variabel sementara terakhir tidak digunakan sama sekali jadi, itu akan dibebaskan.
Mengapa print
mengembalikan nilai dan echo
tidak?
Ekspresi mengevaluasi nilai. Misalnya 2 + 3
mengevaluasi ke 5
, dan abs(-10)
mengevaluasi ke 10
. Karena print expr
itu sendiri merupakan ekspresi, maka ia harus memiliki nilai dan itu memang, nilai yang konsisten 1
menunjukkan hasil yang benar dan dengan mengembalikan nilai yang tidak nol ekspresi menjadi berguna untuk dimasukkan dalam ekspresi lain. Misalnya dalam cuplikan ini, nilai pengembalian cetak berguna dalam menentukan urutan fungsi:
<?php
function bar( $baz ) {
// other code
}
function foo() {
return print("In and out ...\n");
}
if ( foo() ) {
bar();
}
Anda mungkin menemukan hasil cetak dengan nilai tertentu ketika melakukan debug dengan cepat, seperti contoh berikut menggambarkan:
<?php
$haystack = 'abcde';
$needle = 'f';
strpos($haystack,$needle) !== FALSE OR print "$needle not in $haystack";
// output: f not in abcde
Sebagai catatan tambahan, umumnya, pernyataan bukan ekspresi; mereka tidak mengembalikan nilai. Pengecualian, tentu saja adalah pernyataan ekspresi yang menggunakan cetak dan bahkan ekspresi sederhana yang digunakan sebagai pernyataan, seperti1;
, sintaksis yang diwariskan PHP dari C. Pernyataan ekspresi mungkin terlihat aneh tetapi sangat membantu, sehingga memungkinkan untuk meneruskan argumen ke fungsi.
Apakah print
suatu fungsi?
Tidak, ini adalah konstruksi bahasa. Sementara semua panggilan fungsi adalah ekspresi, print (expr)
adalah ekspresi, meskipun visual yang muncul seolah-olah menggunakan sintaks panggilan fungsi. Sebenarnya tanda kurung ini adalah sintaks tanda kurung-expr, berguna untuk evaluasi ekspresi. Itu menjelaskan fakta bahwa kadang-kadang mereka opsional jika ekspresi itu sederhana, seperti print "Hello, world!"
. Dengan ekspresi yang lebih kompleks seperti print (5 ** 2 + 6/2); // 28
tanda kurung membantu evaluasi ekspresi. Tidak seperti nama fungsi, print
secara sintaksis kata kunci , dan secara semantik merupakan "konstruksi bahasa" .
Istilah "konstruksi bahasa" dalam PHP biasanya merujuk pada fungsi "pseudo" seperti isset
atau empty
. Meskipun "konstruksi" ini terlihat persis seperti fungsi, mereka sebenarnya adalah fexprs , yaitu argumen yang diteruskan kepada mereka tanpa dievaluasi, yang memerlukan perlakuan khusus dari kompiler. print
kebetulan menjadi fexpr yang memilih untuk mengevaluasi argumennya dengan cara yang sama sebagai fungsi.
Perbedaannya dapat dilihat dengan mencetak get_defined_functions()
: tidak ada print
fungsi yang terdaftar. (Padahal printf
dan teman adalah: tidak seperti print
, mereka adalah fungsi sebenarnya.)
Mengapa print (foo) berfungsi saat itu?
Untuk alasan yang sama itu echo(foo)
berhasil. Tanda kurung ini sangat berbeda dari tanda kurung fungsi karena mereka berkaitan dengan ekspresi. Itulah sebabnya seseorang dapat mengkodekan echo ( 5 + 8 )
dan dapat mengharapkan hasil 13 untuk ditampilkan (lihat referensi ). Tanda kurung ini terlibat dalam mengevaluasi ekspresi daripada memanggil fungsi. Catatan: ada kegunaan lain untuk tanda kurung di PHP, seperti jika if-conditional expressions, daftar tugas, deklarasi fungsi, dll.
Mengapa melakukan print(1,2,3)
dan echo(1,2,3)
menghasilkan kesalahan sintaksis?
Sintaksnya adalah print expr
, echo expr
atau echo expr, expr, ..., expr
. Ketika PHP bertemu (1,2,3)
, ia mencoba untuk menguraikannya sebagai ekspresi tunggal dan gagal, karena tidak seperti C, PHP tidak benar-benar memiliki operator koma biner; koma lebih berfungsi sebagai pemisah. (Anda dapat menemukan koma biner dalam for-loop PHP, sintaksisnya diwarisi dari C.)
Semantik
Pernyataan itu echo e1, e2, ..., eN;
dapat dipahami sebagai gula sintaksis untuk echo e1; echo e2; ...; echo eN;
.
Karena semua ekspresi adalah pernyataan, dan echo e
selalu memiliki efek samping yang sama dengan print e
, dan nilai pengembalian print e
diabaikan ketika digunakan sebagai pernyataan, kita dapat memahami echo e
sebagai gula sintaksis untuk print e
.
Kedua pengamatan ini berarti yang echo e1, e2, ..., eN;
dapat dilihat sebagai gula sintaksis untuk print e1; print e2; ... print eN;
. (Namun, perhatikan perbedaan runtime non-semantik di bawah ini.)
Karena itu kita hanya perlu mendefinisikan semantik untuk print
. print e
, ketika dievaluasi:
- mengevaluasi argumen tunggal
e
dan ketik-gips nilai yang dihasilkan ke string s
. (Dengan demikian, print e
setara dengan print (string) e
.)
- Streaming string
s
ke buffer output (yang akhirnya akan dialirkan ke output standar).
- Mengevaluasi ke integer
1
.
Perbedaan pada level bytecode
print
melibatkan overhead kecil untuk mengisi variabel return (pseudocode)
print 125;
PRINT 125,$temp ; print 125 and place 1 in $temp
UNSET $temp ; remove $temp
echo
kompilasi tunggal ke satu opcode:
echo 125;
ECHO 125
echo
kompilasi multi-nilai ke beberapa opcodes
echo 123, 456;
ECHO 123
ECHO 456
Perhatikan bahwa multi-nilai echo
tidak menyatukan argumennya, tetapi mengeluarkannya satu per satu.
Referensi: zend_do_print
, zend_do_echo
.
Perbedaan runtime
ZEND_PRINT
diimplementasikan sebagai berikut (pseudocode)
PRINT var, result:
result = 1
ECHO var
Jadi pada dasarnya menempatkan 1
variabel hasil dan mendelegasikan pekerjaan nyata ke ZEND_ECHO
pawang. ZEND_ECHO
melakukan hal berikut
ECHO var:
if var is object
temp = var->toString()
zend_print_variable(temp)
else
zend_print_variable(var)
di mana zend_print_variable()
melakukan "pencetakan" yang sebenarnya (pada kenyataannya, itu hanya mengalihkan ke fungsi SAPI khusus).
Kecepatan: echo x
vsprint x
Tidak seperti gema , hasil cetak mengalokasikan variabel sementara. Namun, jumlah waktu yang dihabiskan untuk kegiatan ini sangat kecil, sehingga perbedaan antara dua konstruksi bahasa ini dapat diabaikan.
Kecepatan: echo a,b,c
vsecho a.b.c
Yang pertama dikompilasi menjadi tiga pernyataan terpisah. Yang kedua mengevaluasi seluruh ekspresi a.b.c.
, mencetak hasilnya dan langsung membuangnya. Karena penggabungan melibatkan alokasi memori dan penyalinan, opsi pertama akan lebih efisien.
Jadi yang mana yang harus digunakan?
Dalam aplikasi web, output sebagian besar terkonsentrasi di template. Karena templat digunakan <?=
, yang merupakan alias echo
, tampaknya logis untuk tetap echo
di bagian lain dari kode juga. echo
memiliki keuntungan tambahan karena mampu mencetak banyak ekspresi tanpa menggabungkannya dan tidak melibatkan overhead pengisian variabel pengembalian sementara. Jadi, gunakan echo
.