อ้างอิง: เปรียบเทียบการพิมพ์และเสียงสะท้อนของ PHP


182

PHP printและPHP ต่างกันechoอย่างไร

Stack Overflow มีคำถามมากมายที่ถามเกี่ยวกับการใช้งานPHP printและechoคำหลัก

จุดประสงค์ของโพสต์นี้คือเพื่อให้ คำถามอ้างอิงที่เป็นที่ยอมรับและคำตอบเกี่ยวกับ PHP printและechoคำหลักและเปรียบเทียบความแตกต่างและกรณีการใช้งานของพวกเขา


3
ฉันคิดว่าที่นี่ไม่เพียงพอเกี่ยวกับค่าส่งคืนที่พิมพ์ การพิมพ์มีประโยชน์สำหรับการแก้ไขข้อบกพร่องอย่างรวดเร็วหรือสิ่งที่ชอบ: strpos ($ x, $ y)! == FALSE หรือพิมพ์ "บางอย่าง" พิมพ์เร็วและอ่านง่าย และ "พิมพ์ฟังก์ชั่น" เป็นเรื่องที่น่าอึดอัดใจที่จะอ่านด้วยเหตุผลบางอย่าง (อาร์กิวเมนต์ของคุณดูเหมือนว่า ... แปลกและไม่ชัดเจน) - มันเป็นโครงสร้างภาษา
XzKto

1
เพื่อให้เปิดสิ่งที่ต้องทำที่นี่คือ: 1. แบ่งออกเป็นคำถามและคำตอบ 2. การอ้างอิง / ลิงก์ไปยังเนื้อหาที่มีอยู่เกี่ยวกับเนื้อหาใน Stack Overflow (โปรดดูที่นี่: stackoverflow.com/questions/3737139/… ) แต่ในคำตอบ 3. ต้องเป็น CW
Kev

"คอลัมน์ที่เกี่ยวข้อง" นั้นใช้ได้ แต่ไม่เน้นมาก เพื่อเพิ่มมูลค่าของมันเป็นคำถามอ้างอิงและคำตอบที่ยอมรับได้ก็ควรได้รับการวิจัยอย่างดีและการเชื่อมโยงไปยังคำตอบที่ดีอื่น ๆ จะเพิ่มมูลค่า
Kev

คำถามจริงๆจะต้องเกิดขึ้นจริงคำถาม ฉันสามารถเพิ่มแบนเนอร์ลงในส่วนที่เกี่ยวกับบัญญัติของคุณเมื่อคุณทำเสร็จแล้ว
ทิมโพสต์

คำตอบ:


185

ทำไมสองคนสร้าง?

ความจริงเกี่ยวกับ พิมพ์และเสียงก้องคือแม้ว่าพวกเขาจะปรากฏต่อผู้ใช้เป็นสองโครงสร้างที่แตกต่างกันพวกเขาทั้งคู่เป็นเงาของเสียงก้องถ้าคุณลงไปถึงพื้นฐานเช่นดูที่รหัสที่มาภายใน ซอร์สโค้ดนั้นเกี่ยวข้องกับตัวแยกวิเคราะห์รวมถึงตัวจัดการ opcode พิจารณาการกระทำง่ายๆเช่นการแสดงจำนวนศูนย์ ไม่ว่าคุณจะใช้ echo หรือพิมพ์ระบบจะเรียกใช้ตัวจัดการ "ZEND_ECHO_SPEC_CONST_HANDLER" เดียวกัน ตัวจัดการสำหรับการพิมพ์ทำสิ่งหนึ่งก่อนที่จะเรียกใช้ตัวจัดการสำหรับ echo ตรวจสอบให้แน่ใจว่าค่าที่ส่งคืนสำหรับการพิมพ์คือ 1 ดังนี้:

ZVAL_LONG(&EX_T(opline->result.var).tmp_var, 1);

(ดูที่นี่สำหรับการอ้างอิง )

ค่าที่ส่งคืนคือความสะดวกสบายที่คุณควรใช้ในการพิมพ์แบบมีเงื่อนไข ทำไม 1 และไม่ใช่ 100 ใน PHP ความจริงของ 1 หรือ 100 เหมือนกันคือจริงในขณะที่ 0 ในบริบทบูลีนเปรียบเสมือนค่าเท็จ ใน PHP ค่าที่ไม่เป็นศูนย์ทั้งหมด (บวกและลบ) เป็นค่าความจริงและสิ่งนี้มาจากมรดก Perl ของ PHP

แต่ถ้าเป็นกรณีนี้แล้วบางคนอาจสงสัยว่าทำไมเสียงก้องใช้อาร์กิวเมนต์หลายข้อในขณะที่พิมพ์สามารถจัดการหนึ่ง สำหรับคำตอบนี้เราต้องหันไปแยกวิเคราะห์โดยเฉพาะไฟล์zend_language_parser.y คุณจะทราบว่าเสียงก้องมีความยืดหยุ่นในตัวเพื่อให้สามารถพิมพ์ได้หนึ่งหรือหลายนิพจน์ (ดูที่นี่ ) ในขณะที่การพิมพ์ถูก จำกัด ให้พิมพ์เพียงหนึ่งนิพจน์ (ดูที่นั่น )

วากยสัมพันธ์

ในภาษาโปรแกรม C และภาษาที่ได้รับอิทธิพลเช่น PHP มีความแตกต่างระหว่างคำสั่งและการแสดงออก วากยสัมพันธ์echo expr, expr, ... exprเป็นคำสั่งในขณะที่print exprเป็นนิพจน์เพราะมันประเมินค่า ดังนั้นเช่นเดียวกับข้อความอื่น ๆecho exprยืนอยู่บนตัวของมันเองและไม่สามารถรวมอยู่ในการแสดงออก:

5 + echo 6;   // syntax error

ในทางตรงกันข้ามprint exprสามารถสร้างคำแถลงได้คนเดียว:

print 5; // valid

หรือเป็นส่วนหนึ่งของการแสดงออก:

   $x = (5 + print 5); // 5 
   var_dump( $x );     // 6 

บางคนอาจถูกล่อลวงให้คิดprintว่ามันเป็นโอเปอเรเตอร์แบบเอกนารี!หรือ~ไม่ก็เป็นโอเปอเรเตอร์ สิ่งที่!, ~ and printมีเหมือนกันคือพวกมันทั้งหมดถูกสร้างขึ้นใน PHP และแต่ละข้อนั้นมีเพียงหนึ่งอาร์กิวเมนต์เท่านั้น คุณสามารถใช้printเพื่อสร้างรหัสแปลก แต่ถูกต้องต่อไปนี้:

    <?php 
    print print print print 7; // 7111

ได้อย่างรวดเร็วก่อนผลที่อาจดูเหมือนแปลกที่พิมพ์คำสั่งสุดท้ายที่พิมพ์ตัวถูกดำเนินการของ '7' ครั้งแรก แต่ถ้าคุณขุดลึกลงไปและดู opcodes จริงมันสมเหตุสมผล:

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 แรก ๆ ที่สร้างขึ้นนั้นสอดคล้องกับ 'print 7' '~ 0' เป็นตัวแปรชั่วคราวที่มีค่าเป็น 1 ตัวแปรนั้นจะกลายเป็นและถูกดำเนินการสำหรับ opcode การพิมพ์ครั้งต่อไปซึ่งจะส่งกลับตัวแปรชั่วคราวและกระบวนการซ้ำ ตัวแปรชั่วคราวตัวสุดท้ายไม่ได้ใช้เลยดังนั้นมันจะได้รับการปลดปล่อย

เหตุใดจึงprintคืนค่าและechoไม่ได้

นิพจน์ประเมินค่า ยกตัวอย่างเช่น2 + 3ประเมิน5และประเมินabs(-10) 10เนื่องจากprint exprเป็นนิพจน์เองดังนั้นจึงควรเก็บค่าและเป็นค่าที่สอดคล้องกันของ1บ่งชี้ผลลัพธ์ที่เป็นความจริงและโดยการส่งคืนค่าที่ไม่เป็นศูนย์นิพจน์จะมีประโยชน์สำหรับการรวมในนิพจน์อื่น ตัวอย่างเช่นในตัวอย่างนี้ค่าส่งคืนของการพิมพ์มีประโยชน์ในการกำหนดลำดับฟังก์ชั่น:

<?php

function bar( $baz ) { 
   // other code   
}
function foo() {
  return print("In and out ...\n");
}

if ( foo() ) {

     bar();
}

คุณอาจพบว่ามีการพิมพ์ค่าเฉพาะเมื่อทำการดีบั๊กขณะเดินทางดังตัวอย่างต่อไปนี้แสดงให้เห็นว่า:

<?php
$haystack = 'abcde';
$needle = 'f';
strpos($haystack,$needle) !== FALSE OR print "$needle not in $haystack"; 

// output: f not in abcde

โดยทั่วไปแล้วข้อความข้างต้นไม่ใช่การแสดงออก พวกเขาจะไม่คืนค่า ข้อยกเว้นแน่นอนคือคำสั่งนิพจน์ที่ใช้การพิมพ์และแม้กระทั่งการแสดงออกง่าย ๆ ที่ใช้เป็นคำสั่งเช่น1;ไวยากรณ์ที่ PHP สืบทอดจาก C คำสั่งการแสดงออกอาจดูแปลก แต่มันมีประโยชน์มากทำให้สามารถส่งอาร์กิวเมนต์ไปยัง ฟังก์ชั่น.

คือ printฟังก์ชั่น?

ไม่มันเป็นโครงสร้างภาษา ในขณะที่การเรียกใช้ฟังก์ชั่นทั้งหมดเป็นนิพจน์print (expr)เป็นนิพจน์แม้จะมีภาพที่ปรากฏราวกับว่ากำลังใช้ไวยากรณ์การเรียกใช้ฟังก์ชัน ในความเป็นจริงวงเล็บเหล่านี้เป็นเครื่องหมายวงเล็บ - expr ไวยากรณ์ซึ่งมีประโยชน์สำหรับการประเมินผลการแสดงออก print "Hello, world!"บัญชีที่ความจริงที่ว่าเวลาที่พวกเขาเป็นตัวเลือกถ้าการแสดงออกเป็นเรื่องง่ายที่หนึ่งเช่น ด้วยการแสดงออกที่ซับซ้อนมากขึ้นเช่นprint (5 ** 2 + 6/2); // 28วงเล็บช่วยประเมินการแสดงออก ซึ่งแตกต่างจากชื่อฟังก์ชั่นprintเป็นคำหลักไวยากรณ์และความหมายภาษา "สร้าง"

คำว่า "ภาษาสร้าง" ใน PHP มักจะหมายถึงฟังก์ชั่น "หลอก" ชอบหรือisset emptyแม้ว่า "โครงสร้าง" เหล่านี้จะดูเหมือนฟังก์ชั่น แต่จริงๆแล้วมันเป็นfexprsนั่นคือข้อโต้แย้งจะถูกส่งผ่านไปยังพวกเขาโดยไม่ได้รับการประเมินซึ่งต้องการการดูแลเป็นพิเศษจากคอมไพเลอร์ printเกิดขึ้นเป็น fexpr ที่เลือกที่จะประเมินข้อโต้แย้งในลักษณะเดียวกับฟังก์ชั่น

ความแตกต่างสามารถเห็นได้โดยการพิมพ์get_defined_functions(): ไม่มีprintฟังก์ชั่นที่ระบุไว้ (แม้ว่าprintfและเพื่อนคือ: ไม่เหมือนprintพวกเขาเป็นฟังก์ชั่นที่แท้จริง)

เหตุใดจึงพิมพ์ (foo) แล้ว

ด้วยเหตุผลเดียวกันกับที่ใช้echo(foo)งานได้ วงเล็บเหล่านี้ค่อนข้างแตกต่างจากวงเล็บการเรียกใช้ฟังก์ชันเนื่องจากเกี่ยวข้องกับนิพจน์แทน นั่นคือเหตุผลที่อาจมีรหัสecho ( 5 + 8 )และสามารถคาดหวังผลลัพธ์ 13 แสดง (ดูการอ้างอิง ) วงเล็บเหล่านี้เกี่ยวข้องในการประเมินค่านิพจน์แทนที่จะเรียกใช้ฟังก์ชัน หมายเหตุ: มีการใช้งานอื่น ๆ สำหรับวงเล็บใน PHP เช่นถ้านิพจน์แบบมีเงื่อนไขรายการการกำหนดการประกาศฟังก์ชัน ฯลฯ

ทำไมprint(1,2,3)และecho(1,2,3)ส่งผลให้เกิดข้อผิดพลาดทางไวยากรณ์?

ไวยากรณ์print expr, หรือecho expr echo expr, expr, ..., exprเมื่อพบ PHP (1,2,3)ก็พยายามแยกมันเป็นนิพจน์เดียวและล้มเหลวเพราะต่างจาก C, PHP ไม่ได้มีตัวดำเนินการเครื่องหมายจุลภาคแบบไบนารี เครื่องหมายจุลภาคทำหน้าที่เป็นตัวคั่น (คุณอาจพบเครื่องหมายจุลภาคแบบไบนารีอย่างไรก็ตามใน for-loops ของ PHP, ไวยากรณ์มันสืบทอดมาจาก C. )

อรรถศาสตร์

คำสั่งที่สามารถเข้าใจได้เป็นน้ำตาลประโยคสำหรับecho e1, e2, ..., eN;echo e1; echo e2; ...; echo eN;

ตั้งแต่การแสดงออกทั้งหมดที่มีงบและecho eมักจะมีเหมือนกันผลข้างเคียงที่เป็นprint eและค่าตอบแทนของprint eจะถูกละเว้นเมื่อใช้เป็นคำสั่งที่เราสามารถเข้าใจเป็นน้ำตาลประโยคสำหรับecho eprint e

ทั้งสองข้อสังเกตหมายความว่าสามารถมองเห็นเป็นน้ำตาลประโยคสำหรับecho e1, e2, ..., eN; print e1; print e2; ... print eN;(อย่างไรก็ตามให้สังเกตความแตกต่างของรันไทม์ที่ไม่ใช่ความหมายด้านล่าง)

ดังนั้นเราจึงต้องกำหนดความหมายสำหรับprintเท่านั้น print eเมื่อประเมิน:

  1. ประเมินอาร์กิวเมนต์เดียวeและชนิดปลดเปลื้องsค่าผลลัพธ์ที่จะสตริง (ดังนั้นจึงprint eเทียบเท่ากับprint (string) e)
  2. สตรีมสตริงsไปยังบัฟเฟอร์เอาต์พุต (ซึ่งในที่สุดจะถูกสตรีมไปยังเอาต์พุตมาตรฐาน)
  3. 1ประเมินจำนวนเต็ม

ความแตกต่างในระดับ bytecode

print เกี่ยวข้องกับค่าใช้จ่ายเล็ก ๆ ของการเติมตัวแปรส่งคืน (pseudocode)

print 125;

PRINT  125,$temp     ; print 125 and place 1 in $temp 
UNSET  $temp         ; remove $temp

echoคอมไพล์เดียวกับหนึ่งรหัส:

echo 125;

ECHO 125

echoคอมไพล์หลายค่าเป็นหลาย opcodes

echo 123, 456;

ECHO 123
ECHO 456

โปรดทราบว่าค่าหลายค่าechoไม่เชื่อมโยงอาร์กิวเมนต์ของมันเข้าด้วยกัน แต่จะส่งออกแบบทีละหนึ่ง

เอกสารอ้างอิง: zend_do_print, zend_do_echo.

ความแตกต่างของรันไทม์

ZEND_PRINT มีการดำเนินการดังต่อไปนี้ (pseudocode)

PRINT  var, result:

    result = 1
    ECHO var

ดังนั้นโดยทั่วไปแล้วจะวางไว้1ในตัวแปรผลลัพธ์และมอบหมายงานจริงให้กับZEND_ECHOตัวจัดการ ZEND_ECHOทำดังต่อไปนี้

ECHO var:

    if var is object
        temp = var->toString()
        zend_print_variable(temp)
    else
        zend_print_variable(var)

โดยที่zend_print_variable()ดำเนินการ "การพิมพ์" จริง (ในความเป็นจริงมันเพียงเปลี่ยนเส้นทางไปยังฟังก์ชัน SAPI โดยเฉพาะ)

ความเร็ว: echo xvsprint x

ซึ่งแตกต่างจากเสียงสะท้อน , พิมพ์จัดสรรตัวแปรชั่วคราว อย่างไรก็ตามระยะเวลาที่ใช้ในกิจกรรมนี้คือจิ๋วดังนั้นความแตกต่างระหว่างการสร้างภาษาทั้งสองนี้จึงไม่มีความสำคัญ

ความเร็ว: echo a,b,cvsecho a.b.c

คำสั่งแรกจะรวบรวมคำสั่งแยกกันสามรายการ ครั้งที่สองประเมินการแสดงออกทั้งหมดa.b.c.พิมพ์ผลลัพธ์และจำหน่ายมันทันที เนื่องจากการต่อข้อมูลเกี่ยวข้องกับการจัดสรรหน่วยความจำและการคัดลอกตัวเลือกแรกจะมีประสิทธิภาพมากกว่า

แล้วจะใช้อันไหนดี?

ในเว็บแอปพลิเคชันผลลัพธ์จะเน้นในแม่แบบเป็นส่วนใหญ่ เนื่องจากแม่แบบใช้<?=ซึ่งเป็นนามแฝงของechoมันดูเหมือนว่าตรรกะechoในส่วนอื่น ๆ ของรหัสเช่นกัน echoมีข้อได้เปรียบเพิ่มเติมในการพิมพ์หลายนิพจน์โดยไม่ต้องต่อกันและไม่เกี่ยวข้องกับค่าใช้จ่ายในการเติมตัวแปรส่งคืนชั่วคราว echoดังนั้นการใช้งาน


5
ฉันได้แก้ไขและชี้แจงอย่างกว้างขวางแล้วและได้เพิ่มหัวข้อเกี่ยวกับความหมาย ฉันค่อนข้างมั่นใจในเรื่องนี้ แต่บางคนสามารถตรวจสอบได้อีกครั้ง
jameshfisher

1
นี่หมายถึงว่ามันจะเป็นการดีกว่าถ้าจะใช้echo $a,$b,$cสำหรับการต่อข้อมูลสตริงสตริง? ฉันไม่เคยเห็นสิ่งนี้มาใช้เลย
geoff

1
ส่วน TL; DR อธิบายความแตกต่างในการทำงานอย่างไร ไลค์: printอนุญาตเพียงหนึ่งอาร์กิวเมนต์เท่านั้นechoอาจมีได้หลายอย่าง echoไม่สามารถเป็นส่วนหนึ่งของนิพจน์ในขณะที่printสามารถและส่งคืน ... ; และอาจเป็นข้อมูลสรุปประสิทธิภาพบางประการ และทุกส่วน "ทำไม" และ "ภายใต้ประทุน" หลังจากนั้น
YakovL

0

ฉันรู้ว่าฉันมาช้า แต่สิ่งหนึ่งที่ฉันต้องการเพิ่มคือในรหัสของฉัน

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

ให้ข้อผิดพลาด "ข้อผิดพลาดทางไวยากรณ์ 'echo' ที่ไม่คาดคิด (T_ECHO)"

ในขณะที่

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

ทำงานได้ดี

เอกสารเกี่ยวกับการสะท้อนว่า "ก้อง (ไม่เหมือนบางโครงสร้างภาษาอื่น ๆ ) ไม่ประพฤติเช่นฟังก์ชั่น" ที่นี่ แต่เกี่ยวกับการพิมพ์เอกสารยังกล่าวว่า "พิมพ์ไม่จริงทำงานจริง (มันเป็นภาษาสร้าง)" ที่นี่ ดังนั้นฉันไม่แน่ใจว่าทำไม และยังเกี่ยวกับ echo และ print docs กล่าวว่า "ความแตกต่างที่สำคัญของ echo คือการพิมพ์นั้นยอมรับอาร์กิวเมนต์เพียงครั้งเดียวและส่งคืน 1 เสมอ" ที่นี่

ฉันจะมีความสุขถ้าใครสามารถทำให้กระจ่างเกี่ยวกับพฤติกรรมนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.