Tại sao hai công trình?
Sự thật về in ấn và tiếng vang là mặc dù chúng xuất hiện với người dùng như hai cấu trúc riêng biệt, nhưng cả hai đều thực sự là sắc thái của tiếng vang nếu bạn đi xuống những điều cơ bản, tức là nhìn vào mã nguồn bên trong. Mã nguồn đó liên quan đến trình phân tích cú pháp cũng như các trình xử lý opcode. Hãy xem xét một hành động đơn giản như hiển thị số không. Cho dù bạn sử dụng echo hay print, trình xử lý tương tự "ZEND_ECHOinksEC_CONST_HANDLER" sẽ được gọi. Trình xử lý để in thực hiện một việc trước khi gọi trình xử lý cho tiếng vang, nó đảm bảo rằng giá trị trả về cho bản in là 1, như sau:
ZVAL_LONG(&EX_T(opline->result.var).tmp_var, 1);
(xem tại đây để tham khảo )
Giá trị trả về là sự thuận tiện nếu người ta muốn sử dụng in trong biểu thức điều kiện. Tại sao 1 mà không phải 100? Trong PHP, tính trung thực của 1 hoặc 100 là như nhau, nghĩa là đúng, trong khi 0 trong bối cảnh boolean tương đương với giá trị sai. Trong PHP tất cả các giá trị khác không (dương và âm) là các giá trị trung thực và điều này xuất phát từ di sản Perl của PHP.
Nhưng, nếu đây là trường hợp, thì người ta có thể tự hỏi tại sao echo lại có nhiều đối số trong khi in chỉ có thể xử lý một. Để có câu trả lời này, chúng ta cần chuyển sang trình phân tích cú pháp, cụ thể là tệp zend_lingu_parser.y . Bạn sẽ lưu ý rằng echo có tính linh hoạt được tích hợp để nó có thể in một hoặc nhiều biểu thức (xem tại đây ). trong khi in bị hạn chế chỉ in một biểu thức (xem ở đó ).
Cú pháp
Trong ngôn ngữ lập trình C và các ngôn ngữ chịu ảnh hưởng của nó như PHP, có sự phân biệt giữa các câu lệnh và biểu thức. Cú pháp, echo expr, expr, ... expr
là một câu lệnh trong khi print expr
là một biểu thức vì nó đánh giá một giá trị. Do đó, giống như các tuyên bố khác, tự echo expr
đứng vững và không có khả năng đưa vào một biểu thức:
5 + echo 6; // syntax error
Ngược lại, print expr
một mình có thể tạo thành một tuyên bố:
print 5; // valid
Hoặc, là một phần của biểu thức:
$x = (5 + print 5); // 5
var_dump( $x ); // 6
Người ta có thể bị cám dỗ để nghĩ print
như thể đó là một toán tử đơn nguyên, giống như !
hoặc ~
tuy nhiên nó không phải là một toán tử. Điểm !, ~ and print
chung là tất cả chúng đều được tích hợp vào PHP và mỗi cái chỉ mất một đối số. Bạn có thể sử dụng print
để tạo mã kỳ lạ nhưng hợp lệ sau đây:
<?php
print print print print 7; // 7111
Thoạt nhìn, kết quả có vẻ kỳ lạ khi câu lệnh in cuối cùng in toán hạng của nó là '7' đầu tiên . Nhưng, nếu bạn đào sâu hơn và nhìn vào các opcodes thực tế thì có ý nghĩa:
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 đầu tiên được tạo ra tương ứng với 'in 7'. '~ 0' là một biến tạm thời có giá trị là 1. Biến đó trở thành và toán hạng cho opcode in tiếp theo, lần lượt trả về một biến tạm thời và quá trình lặp lại. Biến tạm thời cuối cùng không được sử dụng, vì vậy nó được giải phóng.
Tại sao print
trả về một giá trị và echo
không?
Biểu thức đánh giá các giá trị. Ví dụ 2 + 3
đánh giá 5
và abs(-10)
đánh giá 10
. Vì print expr
bản thân nó là một biểu thức, nên nó sẽ giữ một giá trị và nó, một giá trị nhất quán 1
biểu thị một kết quả trung thực và bằng cách trả về giá trị khác không, biểu thức trở nên hữu ích để đưa vào biểu thức khác. Ví dụ: trong đoạn mã này, giá trị trả về của bản in rất hữu ích trong việc xác định chuỗi chức năng:
<?php
function bar( $baz ) {
// other code
}
function foo() {
return print("In and out ...\n");
}
if ( foo() ) {
bar();
}
Bạn có thể tìm thấy bản in có giá trị cụ thể khi gỡ lỗi khi đang di chuyển, như ví dụ tiếp theo minh họa:
<?php
$haystack = 'abcde';
$needle = 'f';
strpos($haystack,$needle) !== FALSE OR print "$needle not in $haystack";
// output: f not in abcde
Như một lưu ý phụ, nói chung, các câu lệnh không phải là biểu thức; họ không trả lại một giá trị. Tất nhiên, ngoại lệ là các câu lệnh biểu thức sử dụng các biểu thức in và thậm chí các biểu thức đơn giản được sử dụng làm câu lệnh, chẳng hạn như 1;
cú pháp mà PHP thừa hưởng từ C. Câu lệnh biểu thức có thể trông kỳ lạ nhưng nó rất hữu ích, giúp có thể chuyển các đối số sang chức năng.
Là print
một chức năng?
Không, nó là một cấu trúc ngôn ngữ. Trong khi tất cả các lệnh gọi hàm là biểu thức, print (expr)
là một biểu thức, mặc dù hình ảnh xuất hiện như thể nó đang sử dụng cú pháp gọi hàm. Trong thực tế, các dấu ngoặc đơn này là cú pháp dấu ngoặc đơn-expr, hữu ích cho việc đánh giá biểu thức. Điều đó giải thích cho thực tế rằng đôi khi chúng là tùy chọn nếu biểu thức là một đơn giản, chẳng hạn như print "Hello, world!"
. Với một biểu thức phức tạp hơn như print (5 ** 2 + 6/2); // 28
dấu ngoặc đơn giúp đánh giá biểu thức. Không giống như tên hàm, print
về mặt cú pháp , về mặt ngữ nghĩa là "cấu trúc ngôn ngữ" .
Thuật ngữ "xây dựng ngôn ngữ" trong PHP thường dùng để chỉ các hàm "giả" như isset
hoặc empty
. Mặc dù các "cấu trúc" này trông giống hệt như các hàm, nhưng chúng thực sự là fexprs , nghĩa là, các đối số được truyền cho chúng mà không được đánh giá, đòi hỏi phải có sự xử lý đặc biệt từ trình biên dịch. print
tình cờ là một fexpr chọn cách đánh giá đối số của nó giống như một hàm.
Sự khác biệt có thể được nhìn thấy bằng cách in get_defined_functions()
: không có print
chức năng được liệt kê. (Mặc dù printf
và bạn bè là: không giống như print
, chúng là các chức năng thực sự.)
Tại sao in (foo) sau đó làm việc?
Vì lý do tương tự mà echo(foo)
làm việc. Các dấu ngoặc đơn này khá khác với dấu ngoặc đơn hàm vì chúng liên quan đến biểu thức thay thế. Đó là lý do tại sao một người có thể viết mã echo ( 5 + 8 )
và có thể mong đợi kết quả là 13 sẽ hiển thị (xem tài liệu tham khảo ). Các dấu ngoặc đơn này có liên quan đến việc đánh giá một biểu thức thay vì gọi một hàm. Lưu ý: có các cách sử dụng khác cho dấu ngoặc đơn trong PHP, chẳng hạn như if - biểu thức điều kiện, danh sách gán, khai báo hàm, v.v.
Tại sao làm print(1,2,3)
và echo(1,2,3)
dẫn đến lỗi cú pháp?
Cú pháp là print expr
, echo expr
hoặc echo expr, expr, ..., expr
. Khi gặp PHP (1,2,3)
, nó cố phân tích nó thành một biểu thức duy nhất và thất bại, vì không giống như C, PHP không thực sự có toán tử dấu phẩy nhị phân; dấu phẩy phục vụ nhiều hơn như một dấu phân cách. (Tuy nhiên, bạn có thể tìm thấy dấu phẩy nhị phân trong các vòng lặp for của PHP, cú pháp được thừa hưởng từ C.)
Ngữ nghĩa
Các tuyên bố echo e1, e2, ..., eN;
có thể được hiểu là đường cú pháp cho echo e1; echo e2; ...; echo eN;
.
Vì tất cả các biểu thức là các câu lệnh và echo e
luôn có cùng tác dụng phụ như print e
và giá trị trả về của print e
bị bỏ qua khi được sử dụng như một câu lệnh, chúng ta có thể hiểu echo e
là đường cú pháp cho print e
.
Hai quan sát này có nghĩa là echo e1, e2, ..., eN;
có thể được coi là đường cú pháp cho print e1; print e2; ... print eN;
. (Tuy nhiên, lưu ý sự khác biệt thời gian chạy phi ngữ nghĩa bên dưới.)
Do đó, chúng tôi chỉ phải xác định ngữ nghĩa cho print
. print e
, khi được đánh giá:
- đánh giá đối số duy nhất của nó
e
và loại giá trị kết quả thành một chuỗi s
. (Như vậy, print e
tương đương với print (string) e
.)
- Truyền phát chuỗi
s
đến bộ đệm đầu ra (cuối cùng sẽ được truyền đến đầu ra tiêu chuẩn).
- Đánh giá theo số nguyên
1
.
Sự khác biệt ở cấp độ mã byte
print
liên quan đến một chi phí nhỏ trong việc điền vào biến trả về (mã giả)
print 125;
PRINT 125,$temp ; print 125 and place 1 in $temp
UNSET $temp ; remove $temp
echo
biên dịch đơn thành một opcode:
echo 125;
ECHO 125
echo
biên dịch đa giá trị thành nhiều opcodes
echo 123, 456;
ECHO 123
ECHO 456
Lưu ý rằng đa giá trị echo
không nối các đối số của nó, nhưng xuất ra từng đối số một.
Tham khảo: zend_do_print
, zend_do_echo
.
Sự khác biệt thời gian chạy
ZEND_PRINT
được thực hiện như sau (mã giả)
PRINT var, result:
result = 1
ECHO var
Vì vậy, về cơ bản, nó đặt 1
vào biến kết quả và ủy thác công việc thực sự cho người ZEND_ECHO
xử lý. ZEND_ECHO
làm như sau
ECHO var:
if var is object
temp = var->toString()
zend_print_variable(temp)
else
zend_print_variable(var)
trong đó zend_print_variable()
thực hiện "in" thực tế (trên thực tế, nó chỉ chuyển hướng đến một chức năng SAPI chuyên dụng).
Tốc độ: echo x
vsprint x
Không giống như echo , print phân bổ một biến tạm thời. Tuy nhiên, lượng thời gian dành cho hoạt động này là rất nhỏ, do đó sự khác biệt giữa hai cấu trúc ngôn ngữ này là không đáng kể.
Tốc độ: echo a,b,c
vsecho a.b.c
Cái đầu tiên biên dịch thành ba câu riêng biệt. Thứ hai đánh giá toàn bộ biểu thức a.b.c.
, in kết quả và xử lý nó ngay lập tức. Vì ghép nối liên quan đến phân bổ bộ nhớ và sao chép, tùy chọn đầu tiên sẽ hiệu quả hơn.
Vậy nên dùng cái nào?
Trong các ứng dụng web, đầu ra chủ yếu tập trung trong các mẫu. Vì các mẫu sử dụng <?=
, là bí danh của echo
nó, nên có vẻ hợp lý để bám echo
vào các phần khác của mã. echo
có một lợi thế bổ sung là có thể in nhiều biểu thức mà không cần ghép chúng và không liên quan đến việc sử dụng biến trả về tạm thời. Vì vậy, sử dụng echo
.