ทำไมสองคนสร้าง?
ความจริงเกี่ยวกับ พิมพ์และเสียงก้องคือแม้ว่าพวกเขาจะปรากฏต่อผู้ใช้เป็นสองโครงสร้างที่แตกต่างกันพวกเขาทั้งคู่เป็นเงาของเสียงก้องถ้าคุณลงไปถึงพื้นฐานเช่นดูที่รหัสที่มาภายใน ซอร์สโค้ดนั้นเกี่ยวข้องกับตัวแยกวิเคราะห์รวมถึงตัวจัดการ 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เมื่อประเมิน:
- ประเมินอาร์กิวเมนต์เดียว
eและชนิดปลดเปลื้องsค่าผลลัพธ์ที่จะสตริง (ดังนั้นจึงprint eเทียบเท่ากับprint (string) e)
- สตรีมสตริง
sไปยังบัฟเฟอร์เอาต์พุต (ซึ่งในที่สุดจะถูกสตรีมไปยังเอาต์พุตมาตรฐาน)
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ดังนั้นการใช้งาน