อะไรคือความแตกต่างระหว่างโครงสร้างภาษาและฟังก์ชัน“ built-in” ใน PHP?


92

ฉันรู้ว่าinclude, isset, require, print, echoและบางคนอื่นไม่ได้ฟังก์ชั่น แต่โครงสร้างภาษา

โครงสร้างภาษาเหล่านี้บางส่วนจำเป็นต้องมีวงเล็บส่วนภาษาอื่น ๆ ไม่ต้องการ

require 'file.php';
isset($x);

บางอย่างมีมูลค่าตอบแทนบางส่วนไม่มี

print 'foo'; //1
echo  'foo'; //no return value

แล้วความแตกต่างภายในระหว่างโครงสร้างภาษาและฟังก์ชันในตัวคืออะไร?

คำตอบ:


132

(นานกว่าที่ตั้งใจไว้โปรดอดทนรอด้วย)

ภาษาส่วนใหญ่ประกอบด้วยสิ่งที่เรียกว่า "ไวยากรณ์": ภาษาประกอบด้วยคำหลักหลายคำที่กำหนดไว้อย่างดีและช่วงของนิพจน์ทั้งหมดที่คุณสามารถสร้างในภาษานั้นสร้างขึ้นจากไวยากรณ์นั้น

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

// The | means "or" and the := represents definition
$expression := $number | $expression $operator $expression
$number := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
$operator := + | - | * | /

จากกฎทั้งสามนี้คุณสามารถสร้างนิพจน์เลขคณิตอินพุตหลักเดียวจำนวนเท่าใดก็ได้ จากนั้นคุณสามารถเขียน parser สำหรับรูปแบบนี้ที่หยุดพักลงการป้อนข้อมูลที่ถูกต้องเป็นประเภทที่เป็นส่วนประกอบของ ( $expression, $numberหรือ$operator) และข้อตกลงที่มีผล ตัวอย่างเช่นนิพจน์3 + 4 * 5สามารถแบ่งออกได้ดังนี้:

// Parentheses used for ease of explanation; they have no true syntactical meaning
$expression = 3 + 4 * 5
            = $expression $operator (4 * 5) // Expand into $exp $op $exp
            = $number $operator $expression // Rewrite: $exp -> $num
            = $number $operator $expression $operator $expression // Expand again
            = $number $operator $number $operator $number // Rewrite again

ตอนนี้เรามีไวยากรณ์ที่แยกวิเคราะห์อย่างสมบูรณ์ในภาษาที่เรากำหนดสำหรับนิพจน์ดั้งเดิม เมื่อเรามีนี้เราสามารถไปถึงและเขียนแยกวิเคราะห์เพื่อหาผลของการรวมกันทั้งหมดของ$number $operator $numberและถ่มน้ำลายออกผลเมื่อเรามีเพียงหนึ่ง$numberซ้าย

โปรดทราบว่าไม่มี$expressionโครงสร้างเหลืออยู่ในเวอร์ชันที่แยกวิเคราะห์ขั้นสุดท้ายของนิพจน์ดั้งเดิมของเรา นั่นเป็นเพราะ$expressionสามารถลดลงเป็นการรวมกันของสิ่งอื่นในภาษาของเรา

PHP นั้นเหมือนกันมาก: โครงสร้างภาษาได้รับการยอมรับว่าเทียบเท่ากับ$numberหรือ$operator. พวกเขาไม่สามารถลดลงในโครงสร้างภาษาอื่น ; แต่เป็นหน่วยฐานที่สร้างภาษาขึ้นมา ความแตกต่างที่สำคัญระหว่างฟังก์ชันและโครงสร้างภาษาคือสิ่งนี้ตัวแยกวิเคราะห์เกี่ยวข้องโดยตรงกับโครงสร้างภาษา ช่วยลดความซับซ้อนของฟังก์ชันลงในโครงสร้างภาษา

เหตุผลที่โครงสร้างภาษาอาจต้องใช้วงเล็บหรือไม่และเหตุผลบางอย่างมีค่าส่งคืนในขณะที่บางส่วนไม่ได้ขึ้นอยู่กับรายละเอียดทางเทคนิคเฉพาะของการใช้งานตัวแยกวิเคราะห์ PHP ฉันไม่ค่อยเชี่ยวชาญในการทำงานของโปรแกรมแยกวิเคราะห์ดังนั้นฉันจึงไม่สามารถตอบคำถามเหล่านี้โดยเฉพาะได้ แต่ลองนึกภาพภาษาที่สองที่ขึ้นต้นด้วยสิ่งนี้:

$expression := ($expression) | ...

อย่างมีประสิทธิภาพภาษานี้มีอิสระที่จะใช้นิพจน์ใด ๆ ที่พบและกำจัดวงเล็บโดยรอบ PHP (และที่นี่ฉันใช้การคาดเดาล้วนๆ) อาจใช้สิ่งที่คล้ายกันสำหรับโครงสร้างภาษา: print("Hello")อาจลดลงprint "Hello"ก่อนที่จะแยกวิเคราะห์หรือในทางกลับกัน (คำจำกัดความภาษาสามารถเพิ่มวงเล็บและกำจัดได้)

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

ในตอนท้ายของวันความแตกต่างภายในระหว่างโครงสร้างและนิพจน์คือสิ่งนี้: โครงสร้างภาษาจะเข้าใจและจัดการกับตัววิเคราะห์ ฟังก์ชั่นในตัวในขณะที่จัดเตรียมโดยภาษาจะถูกแมปและทำให้ง่ายขึ้นกับชุดของโครงสร้างภาษาก่อนที่จะแยกวิเคราะห์

ข้อมูลเพิ่มเติม:

  • รูปแบบ Backus-Naurไวยากรณ์ที่ใช้กำหนดภาษาที่เป็นทางการ (yacc ใช้แบบฟอร์มนี้)

แก้ไข: การอ่านคำตอบอื่น ๆ บางส่วนผู้คนให้คะแนนที่ดี ในหมู่พวกเขา:

  • ภาษาในตัวเรียกได้เร็วกว่าฟังก์ชัน นี่เป็นความจริงหากเพียงเล็กน้อยเนื่องจากตัวแปล PHP ไม่จำเป็นต้องแมปฟังก์ชันนั้นกับฟังก์ชันที่สร้างขึ้นในภาษาของตนก่อนที่จะแยกวิเคราะห์ อย่างไรก็ตามสำหรับเครื่องจักรที่ทันสมัยความแตกต่างนั้นค่อนข้างเล็กน้อย
  • ภาษาในตัวจะข้ามการตรวจสอบข้อผิดพลาด สิ่งนี้อาจเป็นจริงหรือไม่ก็ได้ขึ้นอยู่กับการใช้งานภายใน PHP สำหรับบิวด์อินแต่ละตัว แน่นอนว่าบ่อยกว่านั้นฟังก์ชันจะมีการตรวจสอบข้อผิดพลาดขั้นสูงและฟังก์ชันอื่น ๆ ที่ในตัวไม่มี
  • ไม่สามารถใช้โครงสร้างภาษาเป็นการเรียกกลับของฟังก์ชันได้ นี่คือความจริงเพราะสร้างเป็นไม่ได้ฟังก์ชั่น พวกเขาเป็นหน่วยงานที่แยกจากกัน เมื่อคุณเขียนโค้ดในตัวคุณจะไม่ได้เข้ารหัสฟังก์ชันที่รับอาร์กิวเมนต์ - ไวยากรณ์ของบิวอินจะถูกจัดการโดยตรงโดยตัววิเคราะห์และได้รับการยอมรับว่าเป็นบิวอินแทนที่จะเป็นฟังก์ชัน (สิ่งนี้อาจเข้าใจง่ายกว่าถ้าคุณพิจารณาภาษาที่มีฟังก์ชันชั้นหนึ่ง: อย่างมีประสิทธิภาพคุณสามารถส่งผ่านฟังก์ชันไปรอบ ๆ เป็นวัตถุได้คุณไม่สามารถทำได้ด้วยบิวด์อิน

2
คำตอบที่ยอดเยี่ยมคือปลายเปิดเพียงพอที่จะใช้กับหลายภาษาไม่ใช่แค่ PHP ขอขอบคุณ!
Levi Botelho

15

โครงสร้างภาษามีให้โดยตัวภาษาเอง (เช่นคำสั่งเช่น "if", "while", ... ); ดังนั้นชื่อของพวกเขา

ผลที่ตามมาประการหนึ่งคือเรียกใช้งานได้เร็วกว่าฟังก์ชันที่กำหนดไว้ล่วงหน้าหรือฟังก์ชันที่ผู้ใช้กำหนด(หรือฉันเคยได้ยิน / อ่านมาหลายครั้ง)

ฉันไม่รู้ว่ามันทำได้อย่างไร แต่สิ่งหนึ่งที่พวกเขาทำได้ (เนื่องจากการรวมเข้ากับภาษาโดยตรง) คือ "เลี่ยง" กลไกการจัดการข้อผิดพลาดบางประเภท ตัวอย่างเช่น isset () สามารถใช้กับตัวแปรที่ไม่มีอยู่ได้โดยไม่ต้องแจ้งให้ทราบคำเตือนหรือข้อผิดพลาด

function test($param) {}
if (test($a)) {
    // Notice: Undefined variable: a
}

if (isset($b)) {
    // No notice
}

* โปรดทราบว่าไม่ใช่กรณีของโครงสร้างของภาษาทั้งหมด

ความแตกต่างอีกอย่างระหว่างฟังก์ชันและโครงสร้างภาษาคือบางส่วนสามารถเรียกได้โดยไม่ต้องมีวงเล็บเช่นคีย์เวิร์ด

ตัวอย่างเช่น:

echo 'test'; // language construct => OK

function my_function($param) {}
my_function 'test'; // function => Parse error: syntax error, unexpected T_CONSTANT_ENCAPSED_STRING

เช่นกันไม่ใช่กรณีของโครงสร้างภาษาทั้งหมด

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

ความแตกต่างอีกประการหนึ่งคือไม่สามารถใช้โครงสร้างภาษาเป็น "ฟังก์ชันพอยน์เตอร์" ได้ (ฉันหมายถึงเรียกกลับเป็นต้น):

$a = array(10, 20);

function test($param) {echo $param . '<br />';}
array_map('test', $a);  // OK (function)

array_map('echo', $a);  // Warning: array_map() expects parameter 1 to be a valid callback, function 'echo' not found or invalid function name

ตอนนี้ฉันไม่มีความคิดอื่นใดในใจเลย ... และฉันก็ไม่รู้อะไรมากเกี่ยวกับภายในของ PHP ... ก็จะถึงตอนนี้ ^^

หากคุณไม่ได้รับคำตอบที่นี่มากนักคุณอาจถามสิ่งนี้กับภายในรายชื่ออีเมล (ดูhttp://www.php.net/mailing-lists.php ) ซึ่งมีผู้พัฒนาหลัก PHP จำนวนมาก พวกเขาเป็นคนที่น่าจะรู้เกี่ยวกับเรื่องนั้น ๆ ^^

(และฉันก็สนใจคำตอบอื่น ๆ จริงๆนะคะ ^^)

เป็นข้อมูลอ้างอิง: รายการคำหลักและโครงสร้างภาษาใน PHP


คุณสามารถมีฟังก์ชันที่ยอมรับตัวแปรที่ไม่ได้ตั้งค่าโดยไม่ต้องแจ้งให้ทราบล่วงหน้าโดยการอ้างอิงตัวแปร สิ่งนี้ไม่ จำกัด เฉพาะโครงสร้างภาษาเช่น isset ()
Tom Haigh

โอ้ไม่ได้คิดเรื่องนั้น :-( ขอบคุณ!
Pascal MARTIN

4

หลังจากลุยโค้ดฉันพบว่า php แยกวิเคราะห์คำสั่งบางส่วนในไฟล์ yacc ดังนั้นพวกเขาจึงเป็นกรณีพิเศษ

(ดู Zend / zend_language_parser.y)

นอกเหนือจากนั้นฉันไม่คิดว่ามีความแตกต่างอื่น ๆ


1

คุณสามารถแทนที่ฟังก์ชันในตัวได้ คีย์เวิร์ดอยู่ตลอดไป


นั่นไม่ใช่ฟังก์ชันในตัว ถูกกำหนดไว้ในส่วนขยาย APD (Advanced PHP Debugger)
Ionuț G. Stan

เกี่ยวกับฟังก์ชั่นการลบล้างคุณสามารถมีของขวัญได้ที่ส่วนขยาย runkit (ไม่ใช่แกนหลักเช่นกันมันเป็นส่วนขยายดังนั้นจึงไม่ตอบโจทย์ OP แต่ตอบสนองต่อคำตอบนี้เท่านั้น) มันทรงพลังจริงๆและล่าสุดกว่า APD (และฉันเชื่อว่าฉันเคยได้ยินมาบ้างแล้วว่ามีบางคนยังคงทำงานอยู่แม้ว่าจะไม่ได้แสดงบน pecl.php.net ก็ตาม)
Pascal MARTIN
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.