Tham khảo: So sánh bản in và tiếng vang của PHP


182

Sự khác biệt giữa PHP printvà là echogì?

Stack Overflow có nhiều câu hỏi về việc sử dụng từ khóa printechotừ khóa của PHP .

Mục đích của bài viết này là cung cấp một câu hỏi và câu trả lời tham khảo chính tắc về PHP printechocác từ khóa và so sánh sự khác biệt và các trường hợp sử dụng của chúng.


3
Tôi nghĩ ở đây là không đủ về giá trị trả về in. In rất hữu ích cho đầu ra gỡ lỗi nhanh hoặc một cái gì đó tương tự: strpose ($ x, $ y)! == FALSE HOẶC in "cái gì đó". Nhanh để gõ và tốt để đọc. Và "In một chức năng" rất khó đọc vì một số lý do (lập luận của bạn có vẻ ... lạ và không rõ ràng) - đó là cấu trúc ngôn ngữ, có một điều tồi tệ hơn nhiều bạn không thể làm với nó: các hàm biến.
XzKto

1
Để giữ cho điều này mở những gì cần phải làm ở đây là: 1. chia thành một câu hỏi và trả lời. 2. Tham khảo / liên kết đến nội dung hiện có về vấn đề chủ đề trên Stack Overflow (giống như ở đây: stackoverflow.com/questions/3737139/ trộm ) nhưng trong câu trả lời. 3. Cần phải được CW.
Kev

"Cột liên quan" là tốt, nhưng nó không tập trung lắm. Để tăng giá trị của nó như là một câu hỏi và câu trả lời tham khảo chính tắc thì nó cũng cần được nghiên cứu kỹ và liên kết đến các câu trả lời tốt cụ thể khác sẽ tăng thêm giá trị.
Kev

Câu hỏi thực sự cần phải là một câu hỏi thực tế . Tôi có thể thêm một biểu ngữ cho nó liên quan đến phần chính tắc sau khi bạn hoàn thành.
Tim Post

Câu trả lời:


185

Tại sao hai công trình?

Sự thật về in ấntiế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, ... exprlà một câu lệnh trong khi print exprlà 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 exprmộ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ĩ printnhư 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 printchung 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 printtrả về một giá trị và echokhông?

Biểu thức đánh giá các giá trị. Ví dụ 2 + 3đánh giá 5abs(-10)đánh giá 10. Vì print exprbả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 1biể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.

printmộ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); // 28dấu ngoặc đơn giúp đánh giá biểu thức. Không giống như tên hàm, printvề 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ư issethoặ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. printtì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ó printchức năng được liệt kê. (Mặc dù printfvà 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)echo(1,2,3)dẫn đến lỗi cú pháp?

Cú pháp là print expr, echo exprhoặ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 eluôn có cùng tác dụng phụ như print evà giá trị trả về của print ebị bỏ qua khi được sử dụng như một câu lệnh, chúng ta có thể hiểu echo elà đườ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á:

  1. đánh giá đối số duy nhất của nó eloại giá trị kết quả thành một chuỗi s. (Như vậy, print etương đương với print (string) e.)
  2. 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).
  3. Đá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

echobiên dịch đơn thành một opcode:

echo 125;

ECHO 125

echobiê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ị echokhô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 1vào biến kết quả và ủy thác công việc thực sự cho người ZEND_ECHOxử lý. ZEND_ECHOlà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 xvsprint 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,cvsecho 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 echonó, nên có vẻ hợp lý để bám echovào các phần khác của mã. echocó 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.


5
Tôi đã chỉnh sửa rộng rãi và làm rõ điều này, và thêm một phần về ngữ nghĩa. Tôi khá tự tin về nó, nhưng ai đó có thể kiểm tra lại.
jameshfisher

1
Điều này có nghĩa là tốt hơn khi sử dụng echo $a,$b,$cđể nối các chuỗi chuỗi? Tôi thực sự chưa bao giờ thấy điều này trong sử dụng.
geoff

1
Làm thế nào về một phần TL; DR mô tả sự khác biệt chức năng? Giống như: printchỉ cho phép một đối số, echocó thể có nhiều đối số; echokhông thể là một phần của biểu thức trong khi printcó thể và trả về ...; và có thể là một số tóm tắt hiệu suất. Và tất cả những phần "tại sao" và "dưới mui xe" sau đó
YakovL

0

Tôi biết tôi đến muộn nhưng một điều tôi muốn thêm là trong mã của tôi

 $stmt = mysqli_stmt_init($connection) or echo "error at init"; 

đưa ra lỗi "lỗi cú pháp, lỗi 'echo' (T_ECHO)" không mong muốn

trong khi

 $stmt = mysqli_stmt_init($connection) or print "error at init";

hoạt động tốt

Tài liệu về echo nói "echo (không giống như một số cấu trúc ngôn ngữ khác) không hoạt động như một chức năng" ở đây nhưng về tài liệu in cũng nói "print không thực sự là một chức năng thực sự (nó là một cấu trúc ngôn ngữ)" ở đây . Vì vậy, tôi không chắc tại sao. Và cũng về tiếng vang và tài liệu in cho biết "Sự khác biệt chính đối với tiếng vang là việc in chỉ chấp nhận một đối số duy nhất và luôn trả về 1." đây

Tôi sẽ rất vui nếu ai đó có thể làm sáng tỏ hành vi này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.