Sự khác biệt giữa cấu trúc ngôn ngữ và một hàm “tích hợp sẵn” trong PHP là gì?


92

Tôi biết rằng include, isset, require, print, echo, và một số người khác không phải là chức năng nhưng các cấu trúc ngôn ngữ.

Một số cấu trúc ngôn ngữ này cần dấu ngoặc đơn, những cấu trúc khác thì không.

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

Một số có giá trị trả về, những người khác thì không.

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

Vậy sự khác biệt bên trong giữa cấu trúc ngôn ngữ và chức năng tích hợp là gì?

Câu trả lời:


131

(Điều này lâu hơn tôi dự định; hãy chịu đựng với tôi.)

Hầu hết các ngôn ngữ đều được tạo thành từ một thứ gọi là "cú pháp": ngôn ngữ này bao gồm một số từ khóa được xác định rõ ràng và phạm vi đầy đủ của các biểu thức mà bạn có thể xây dựng trong ngôn ngữ đó được xây dựng từ cú pháp đó.

Ví dụ: giả sử bạn có một "ngôn ngữ" số học bốn hàm đơn giản chỉ lấy các số nguyên có một chữ số làm đầu vào và hoàn toàn bỏ qua thứ tự các phép toán (tôi đã nói với bạn đó là một ngôn ngữ đơn giản). Ngôn ngữ đó có thể được xác định bằng cú pháp:

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

Từ ba quy tắc này, bạn có thể xây dựng bất kỳ số lượng biểu thức số học đầu vào có một chữ số nào. Sau đó bạn có thể viết một phân tích cú pháp cho các cú pháp này mà phá vỡ xuống bất kỳ đầu vào hợp lệ vào loại thành phần của nó ( $expression, $numberhoặc $operator) và những giao dịch với kết quả. Ví dụ, biểu thức 3 + 4 * 5có thể được chia nhỏ như sau:

// 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

Bây giờ chúng ta có một cú pháp được phân tích cú pháp đầy đủ, bằng ngôn ngữ xác định của chúng ta, cho biểu thức gốc. Khi chúng ta có điều này, chúng ta có thể xem qua và viết một trình phân tích cú pháp để tìm kết quả của tất cả các kết hợp của $number $operator $numbervà đưa ra kết quả khi chúng ta chỉ $numbercòn lại một kết hợp .

Lưu ý rằng không có $expressioncấu trúc nào còn lại trong phiên bản được phân tích cú pháp cuối cùng của biểu thức gốc của chúng tôi. Đó là bởi vì $expressionluôn có thể được rút gọn thành sự kết hợp của những thứ khác trong ngôn ngữ của chúng ta.

PHP cũng giống như vậy: các cấu trúc ngôn ngữ được công nhận là tương đương với $numberhoặc của chúng tôi $operator. Chúng không thể được rút gọn thành các cấu trúc ngôn ngữ khác ; thay vào đó, chúng là các đơn vị cơ sở mà từ đó ngôn ngữ được xây dựng. Sự khác biệt chính giữa hàm và cấu trúc ngôn ngữ là: trình phân tích cú pháp xử lý trực tiếp với cấu trúc ngôn ngữ. Nó đơn giản hóa các chức năng thành các cấu trúc ngôn ngữ.

Lý do mà các cấu trúc ngôn ngữ có thể yêu cầu hoặc không cần dấu ngoặc đơn và lý do một số cấu trúc có giá trị trả về trong khi những cấu trúc khác không hoàn toàn phụ thuộc vào các chi tiết kỹ thuật cụ thể của việc triển khai trình phân tích cú pháp PHP. Tôi không rành về cách thức hoạt động của trình phân tích cú pháp, vì vậy tôi không thể giải quyết những câu hỏi này một cách cụ thể, nhưng hãy tưởng tượng trong giây phút một ngôn ngữ bắt đầu bằng điều này:

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

Một cách hiệu quả, ngôn ngữ này có thể tự do sử dụng bất kỳ biểu thức nào mà nó tìm thấy và loại bỏ các dấu ngoặc đơn xung quanh. PHP (và ở đây tôi đang sử dụng phỏng đoán thuần túy) có thể sử dụng một cái gì đó tương tự cho các cấu trúc ngôn ngữ của nó: print("Hello")có thể bị giảm xuống print "Hello"trước khi nó được phân tích cú pháp hoặc ngược lại (các định nghĩa ngôn ngữ có thể thêm dấu ngoặc đơn cũng như loại bỏ chúng).

Đây là căn nguyên của lý do tại sao bạn không thể xác định lại các cấu trúc ngôn ngữ như echohoặc print: chúng được mã hóa cứng một cách hiệu quả vào trình phân tích cú pháp, trong khi các hàm được ánh xạ tới một tập hợp các cấu trúc ngôn ngữ và trình phân tích cú pháp cho phép bạn thay đổi ánh xạ đó trong thời gian biên dịch- hoặc thời gian chạy thành thay thế tập hợp các cấu trúc hoặc biểu thức ngôn ngữ của riêng bạn.

Vào cuối ngày, sự khác biệt bên trong giữa cấu trúc và biểu thức là thế này: cấu trúc ngôn ngữ được trình phân tích cú pháp hiểu và xử lý. Các hàm tích hợp, mặc dù được cung cấp bởi ngôn ngữ, được ánh xạ và đơn giản hóa thành một tập hợp các cấu trúc ngôn ngữ trước khi phân tích cú pháp.

Thêm thông tin:

  • Biểu mẫu Backus-Naur , cú pháp được sử dụng để xác định các ngôn ngữ chính thức (yacc sử dụng biểu mẫu này)

Edit: Đọc qua một số đáp án khác, mọi người cho điểm hay. Trong số đó:

  • Nội trang ngôn ngữ gọi nhanh hơn một hàm. Điều này đúng, nếu chỉ là một chút, bởi vì trình thông dịch PHP không cần phải ánh xạ hàm đó tới các chức năng tương đương với ngôn ngữ nội trang của nó trước khi phân tích cú pháp. Tuy nhiên, trên một máy hiện đại, sự khác biệt là khá không đáng kể.
  • Nội trang ngôn ngữ bỏ qua kiểm tra lỗi. Điều này có thể đúng hoặc không, tùy thuộc vào việc triển khai nội bộ PHP cho từng nội trang. Chắc chắn đúng là thường xuyên hơn không, các chức năng sẽ có tính năng kiểm tra lỗi nâng cao hơn và các chức năng khác mà các nội dung không có.
  • Cấu trúc ngôn ngữ không thể được sử dụng làm hàm gọi lại. Điều này đúng, bởi vì một cấu trúc không phải là một hàm . Chúng là những thực thể riêng biệt. Khi bạn viết mã nội trang, bạn không mã hóa một hàm nhận đối số - cú pháp của nội trang được trình phân tích cú pháp xử lý trực tiếp và được công nhận là nội trang chứ không phải là một hàm. (Điều này có thể dễ hiểu hơn nếu bạn coi các ngôn ngữ có hàm hạng nhất: một cách hiệu quả, bạn có thể chuyển các hàm xung quanh dưới dạng đối tượng. Bạn không thể làm điều đó với nội trang.)

2
Câu trả lời tuyệt vời đủ mở để áp dụng cho nhiều ngôn ngữ, không chỉ PHP. Cảm ơn bạn!
Levi Botelho

15

Các cấu trúc ngôn ngữ được cung cấp bởi chính ngôn ngữ đó (như các hướng dẫn như "if", "while", ...); do đó tên của họ.

Một hệ quả của điều đó là chúng nhanh hơn được gọi ra so với các hàm được xác định trước hoặc do người dùng xác định (hoặc vì vậy tôi đã nghe / đọc vài lần)

Tôi không biết nó được thực hiện như thế nào, nhưng một điều họ có thể làm (vì được tích hợp trực tiếp vào ngôn ngữ) là "bỏ qua" một số loại cơ chế xử lý lỗi. Ví dụ, Isset () có thể được sử dụng với các biến không tồn tại mà không gây ra bất kỳ thông báo, cảnh báo hoặc lỗi nào.

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

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

* Lưu ý rằng nó không đúng với các cấu trúc của tất cả các ngôn ngữ.

Một sự khác biệt khác giữa các hàm và cấu trúc ngôn ngữ là một số trong số chúng có thể được gọi mà không có dấu ngoặc đơn, như một từ khóa.

Ví dụ :

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

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

Ở đây cũng vậy, nó không phải là trường hợp cho tất cả các cấu trúc ngôn ngữ.

Tôi cho rằng hoàn toàn không có cách nào để "vô hiệu hóa" một cấu trúc ngôn ngữ bởi vì bản thân nó là một phần của ngôn ngữ. Mặt khác, rất nhiều hàm PHP "tích hợp sẵn" không thực sự được tích hợp sẵn vì chúng được cung cấp bởi các phần mở rộng để chúng luôn hoạt động (nhưng không phải tất cả chúng)

Một sự khác biệt khác là các cấu trúc ngôn ngữ không thể được sử dụng làm "con trỏ hàm" (ý tôi là, ví dụ: gọi lại):

$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

Tôi không có bất kỳ ý tưởng nào khác trong đầu tôi ngay bây giờ ... và tôi không biết nhiều về nội bộ của PHP ... Vì vậy, đó sẽ là nó ngay bây giờ ^^

Nếu bạn không nhận được nhiều câu trả lời ở đây, có thể bạn có thể hỏi điều này với nội bộ danh sách gửi thư (xem http://www.php.net/mailing-lists.php ), nơi có nhiều nhà phát triển lõi PHP; họ là những người có thể biết về những thứ đó ^^

(Và tôi thực sự quan tâm đến các câu trả lời khác, btw ^^)

Để tham khảo: danh sách các từ khóa và cấu trúc ngôn ngữ trong PHP


Bạn có thể có một hàm chấp nhận một biến chưa được đặt mà không cần tạo thông báo bằng cách lấy biến theo tham chiếu. Điều này không giới hạn ở các cấu trúc ngôn ngữ như Isset ().
Tom Haigh

Ồ, không nghĩ về điều đó :-( Cảm ơn!
Pascal MARTIN

4

Sau khi lướt qua mã, tôi thấy rằng php phân tích cú pháp một số câu lệnh trong tệp yacc. Vì vậy, chúng là những trường hợp đặc biệt.

(xem Zend / zend_language_parser.y)

Ngoài điều đó ra, tôi không nghĩ rằng có những khác biệt khác.


1

Bạn có thể ghi đè các chức năng tích hợp sẵn . Từ khóa là mãi mãi.


Đó không phải là một chức năng được tích hợp sẵn. Được định nghĩa trong phần mở rộng APD (Advanced PHP Debugger).
Ionuț G. Stan

về ghi đè các chức năng, bạn có thể có một chiến lợi phẩm tại phần mở rộng runkit (nó cũng không phải cốt lõi, nó là một phần mở rộng, vì vậy không trả lời OP mà chỉ trả lời câu trả lời này); nó thực sự mạnh mẽ, và nhiều hơn nữa gần đây hơn APD (và tôi tin rằng tôi đã nghe một số thời gian trước đây rằng một số người vẫn làm việc trên nó, ngay cả khi nó không được hiển thị trên pecl.php.net)
Pascal MARTIN
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.