ทำไมสองคนสร้าง?
ความจริงเกี่ยวกับ พิมพ์และเสียงก้องคือแม้ว่าพวกเขาจะปรากฏต่อผู้ใช้เป็นสองโครงสร้างที่แตกต่างกันพวกเขาทั้งคู่เป็นเงาของเสียงก้องถ้าคุณลงไปถึงพื้นฐานเช่นดูที่รหัสที่มาภายใน ซอร์สโค้ดนั้นเกี่ยวข้องกับตัวแยกวิเคราะห์รวมถึงตัวจัดการ 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 e
print 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 x
vsprint x
ซึ่งแตกต่างจากเสียงสะท้อน , พิมพ์จัดสรรตัวแปรชั่วคราว อย่างไรก็ตามระยะเวลาที่ใช้ในกิจกรรมนี้คือจิ๋วดังนั้นความแตกต่างระหว่างการสร้างภาษาทั้งสองนี้จึงไม่มีความสำคัญ
ความเร็ว: echo a,b,c
vsecho a.b.c
คำสั่งแรกจะรวบรวมคำสั่งแยกกันสามรายการ ครั้งที่สองประเมินการแสดงออกทั้งหมดa.b.c.
พิมพ์ผลลัพธ์และจำหน่ายมันทันที เนื่องจากการต่อข้อมูลเกี่ยวข้องกับการจัดสรรหน่วยความจำและการคัดลอกตัวเลือกแรกจะมีประสิทธิภาพมากกว่า
แล้วจะใช้อันไหนดี?
ในเว็บแอปพลิเคชันผลลัพธ์จะเน้นในแม่แบบเป็นส่วนใหญ่ เนื่องจากแม่แบบใช้<?=
ซึ่งเป็นนามแฝงของecho
มันดูเหมือนว่าตรรกะecho
ในส่วนอื่น ๆ ของรหัสเช่นกัน echo
มีข้อได้เปรียบเพิ่มเติมในการพิมพ์หลายนิพจน์โดยไม่ต้องต่อกันและไม่เกี่ยวข้องกับค่าใช้จ่ายในการเติมตัวแปรส่งคืนชั่วคราว echo
ดังนั้นการใช้งาน