Apa perbedaan antara konstruksi bahasa dan fungsi "built-in" di PHP?

92

Saya tahu bahwa include, isset, require, print, echo, dan beberapa yang lain tidak fungsi tetapi bahasa konstruksi.

Beberapa konstruksi bahasa ini membutuhkan tanda kurung, yang lainnya tidak.

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

Beberapa memiliki nilai balik, yang lainnya tidak.

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

Jadi apa perbedaan internal antara konstruksi bahasa dan fungsi bawaan?

Philippe Gerber
sumber

Jawaban:

131

(Ini lebih lama dari yang saya inginkan; mohon bersabarlah.)

Sebagian besar bahasa terdiri dari sesuatu yang disebut "sintaks": bahasa terdiri dari beberapa kata kunci yang ditentukan dengan baik, dan rangkaian lengkap ekspresi yang dapat Anda buat dalam bahasa tersebut dibangun dari sintaks tersebut.

Sebagai contoh, katakanlah Anda memiliki "bahasa" aritmatika empat fungsi sederhana yang hanya menggunakan bilangan bulat satu digit sebagai masukan dan sepenuhnya mengabaikan urutan operasi (saya katakan itu adalah bahasa sederhana). Bahasa itu dapat didefinisikan dengan sintaks:

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

Dari ketiga aturan ini, Anda dapat membuat sejumlah ekspresi aritmatika dengan input satu digit. Anda kemudian dapat menulis parser untuk sintaks ini yang memecah setiap input yang valid ke dalam jenis komponen ( $expression, $number, atau $operator) dan penawaran dengan hasilnya. Misalnya, ekspresi 3 + 4 * 5dapat dipecah sebagai berikut:

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

Sekarang kami memiliki sintaks yang diurai sepenuhnya, dalam bahasa yang kami tentukan, untuk ekspresi asli. Setelah kita memiliki ini, kita dapat menelusuri dan menulis parser untuk menemukan hasil dari semua kombinasi $number $operator $number, dan mengeluarkan hasil saat kita hanya memiliki satu yang $numbertersisa.

Perhatikan bahwa tidak ada $expressionkonstruksi yang tersisa dalam versi parsing terakhir dari ekspresi asli kita. Itu karena $expressionselalu dapat direduksi menjadi kombinasi hal-hal lain dalam bahasa kita.

PHP kurang lebih sama: konstruksi bahasa diakui sebagai padanan dari kami $numberatau $operator. Mereka tidak dapat direduksi menjadi konstruksi bahasa lain ; sebaliknya, mereka adalah unit dasar dari mana bahasa itu dibangun. Perbedaan utama antara fungsi dan konstruksi bahasa adalah ini: parser berhubungan langsung dengan konstruksi bahasa. Ini menyederhanakan fungsi menjadi konstruksi bahasa.

Alasan konstruksi bahasa mungkin atau mungkin tidak memerlukan tanda kurung dan alasan beberapa memiliki nilai kembalian sementara yang lain tidak bergantung sepenuhnya pada detail teknis spesifik dari implementasi parser PHP. Saya tidak begitu paham tentang cara kerja parser, jadi saya tidak bisa menjawab pertanyaan-pertanyaan ini secara spesifik, tapi bayangkan sejenak bahasa yang dimulai dengan ini:

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

Secara efektif, bahasa ini bebas mengambil ekspresi apa pun yang ditemukannya dan menghilangkan tanda kurung di sekitarnya. PHP (dan di sini saya menggunakan tebakan murni) dapat menggunakan sesuatu yang serupa untuk konstruksi bahasanya: print("Hello")mungkin dikurangi menjadi print "Hello"sebelum diurai, atau sebaliknya (definisi bahasa dapat menambahkan tanda kurung dan juga menghilangkannya).

Inilah akar dari mengapa Anda tidak dapat mendefinisikan ulang konstruksi bahasa seperti echoatau print: mereka secara efektif dikodekan ke dalam parser, sedangkan fungsi dipetakan ke sekumpulan konstruksi bahasa dan parser memungkinkan Anda untuk mengubah pemetaan itu pada kompilasi- atau runtime ke gantilah kumpulan konstruksi atau ekspresi bahasa Anda sendiri.

Pada akhirnya, perbedaan internal antara konstruksi dan ekspresi adalah ini: konstruksi bahasa dipahami dan ditangani oleh parser. Fungsi bawaan, sementara disediakan oleh bahasa, dipetakan dan disederhanakan menjadi sekumpulan konstruksi bahasa sebelum parsing.

Info lebih lanjut:

  • Bentuk Backus-Naur , sintaks yang digunakan untuk mendefinisikan bahasa formal (yacc menggunakan formulir ini)

Sunting: Membaca beberapa jawaban lain, orang membuat poin bagus. Diantara mereka:

  • Bahasa bawaan lebih cepat untuk dipanggil daripada fungsi. Ini benar, jika hanya sedikit, karena interpreter PHP tidak perlu memetakan fungsi itu ke bahasa bawaannya yang setara sebelum parsing. Namun, pada mesin modern, perbedaannya bisa diabaikan.
  • Sebuah bahasa bawaan melewati pemeriksaan kesalahan. Ini mungkin atau mungkin tidak benar, tergantung pada implementasi internal PHP untuk setiap builtin. Memang benar bahwa lebih sering daripada tidak, fungsi akan memiliki pemeriksaan kesalahan yang lebih canggih dan fungsionalitas lain yang tidak ada di dalamnya.
  • Konstruksi bahasa tidak dapat digunakan sebagai callback fungsi. Ini benar, karena konstruk bukanlah fungsi . Mereka adalah entitas yang terpisah. Saat Anda membuat kode bawaan, Anda tidak mengkodekan fungsi yang membutuhkan argumen - sintaks dari bawaan ditangani langsung oleh parser, dan dikenali sebagai bawaan, bukan sebagai fungsi. (Ini mungkin lebih mudah dipahami jika Anda mempertimbangkan bahasa dengan fungsi kelas satu: efektif, Anda dapat meneruskan fungsi sebagai objek. Anda tidak dapat melakukannya dengan bawaan.)
Tim
sumber
2
Jawaban bagus yang cukup terbuka untuk diterapkan ke banyak bahasa, tidak hanya PHP. Terima kasih!
Levi Botelho
15

Konstruksi bahasa disediakan oleh bahasa itu sendiri (seperti instruksi seperti "if", "while", ...); karena itulah nama mereka.

Salah satu konsekuensi dari itu adalah mereka lebih cepat dipanggil daripada fungsi yang ditentukan sebelumnya atau yang ditentukan pengguna (atau begitulah yang saya dengar / baca beberapa kali)

Saya tidak tahu bagaimana melakukannya, tetapi satu hal yang dapat mereka lakukan (karena terintegrasi langsung ke dalam bahasa) adalah "melewati" semacam mekanisme penanganan kesalahan. Misalnya, isset () dapat digunakan dengan variabel yang tidak ada tanpa menyebabkan pemberitahuan, peringatan, atau kesalahan.

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

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

* Perhatikan bahwa ini bukan kasus konstruksi semua bahasa.

Perbedaan lain antara fungsi dan konstruksi bahasa adalah beberapa di antaranya dapat dipanggil tanpa tanda kurung, seperti kata kunci.

Misalnya :

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

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

Di sini juga, tidak demikian halnya untuk semua konstruksi bahasa.

Saya kira sama sekali tidak ada cara untuk "menonaktifkan" konstruksi bahasa karena itu adalah bagian dari bahasa itu sendiri. Di sisi lain, banyak fungsi PHP "built-in" tidak benar-benar built-in karena disediakan oleh ekstensi sedemikian rupa sehingga selalu aktif (tetapi tidak semuanya)

Perbedaan lainnya adalah bahwa konstruksi bahasa tidak dapat digunakan sebagai "penunjuk fungsi" (maksud saya, callback, misalnya):

$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

Saya tidak memiliki ide lain yang muncul di benak saya saat ini ... dan saya tidak tahu banyak tentang internal PHP ... Jadi itu saja sekarang ^^

Jika Anda tidak mendapatkan banyak jawaban di sini, mungkin Anda bisa menanyakan hal ini ke milis internal (lihat http://www.php.net/mailing-lists.php ), di mana terdapat banyak pengembang inti PHP; mereka adalah orang-orang yang mungkin tahu tentang hal itu ^^

(Dan saya sangat tertarik dengan jawaban lainnya, btw ^^)

Sebagai referensi: daftar kata kunci dan konstruksi bahasa di PHP

Pascal MARTIN
sumber
Anda dapat memiliki fungsi yang menerima variabel tidak ditetapkan tanpa membuat pemberitahuan dengan mengambil variabel dengan referensi. Ini tidak terbatas pada konstruksi bahasa seperti isset ().
Tom Haigh
Oh, tidak berpikir tentang itu :-( Terima kasih!
Pascal MARTIN
4

Setelah mengarungi kode, saya telah menemukan bahwa php mem-parsing beberapa pernyataan dalam file yacc. Jadi itu kasus khusus.

(lihat Zend / zend_language_parser.y)

Selain itu saya rasa tidak ada perbedaan lainnya.

ujung
sumber
1

Anda dapat mengganti fungsi bawaan . Kata kunci selamanya.

Jason S
sumber
Itu bukan fungsi bawaan. Didefinisikan dalam ekstensi APD (Advanced PHP Debugger).
Ionuț G. Stan
tentang fungsi utama, Anda bisa mendapatkan jarahan di ekstensi runkit (itu juga bukan inti, ini ekstensi, jadi tidak menjawab OP, tetapi hanya untuk jawaban ini); ini benar-benar kuat, dan lebih baru daripada APD (dan saya yakin saya mendengar beberapa waktu lalu bahwa beberapa orang masih mengerjakannya, meskipun tidak ditampilkan di pecl.php.net)
Pascal MARTIN