Mencoba memberikan ikhtisar dari berbagai diskusi dan jawaban:
Tidak ada jawaban tunggal untuk pertanyaan yang dapat menggantikan semua cara yang isset
dapat digunakan. Beberapa kasus penggunaan ditangani oleh fungsi lain, sementara yang lain tidak tahan terhadap pengawasan, atau memiliki nilai yang meragukan di luar kode golf. Jauh dari menjadi "rusak" atau "tidak konsisten", kasus penggunaan lain menunjukkan mengapa isset
reaksi terhadap null
perilaku logis.
Kasus penggunaan nyata (dengan solusi)
1. Array keys
Array dapat diperlakukan seperti kumpulan variabel, dengan unset
dan isset
memperlakukan mereka seolah-olah mereka. Namun, karena mereka dapat diulang, dihitung, dll, nilai yang hilang tidak sama dengan nilai yang nilainya null
.
Jawabannya dalam hal ini, adalah menggunakan array_key_exists()
alih-alihisset()
.
Karena ini membutuhkan array untuk memeriksa sebagai argumen fungsi, PHP masih akan memunculkan "pemberitahuan" jika array itu sendiri tidak ada. Dalam beberapa kasus, secara sah dapat dikatakan bahwa setiap dimensi seharusnya diinisialisasi terlebih dahulu, sehingga pemberitahuan melakukan tugasnya. Untuk kasus lain, fungsi "rekursif" array_key_exists
, yang memeriksa setiap dimensi array pada gilirannya, akan menghindari ini, tetapi pada dasarnya akan sama dengan @array_key_exists
. Ini juga agak tangensial untuk penanganan null
nilai.
2. Properti objek
Dalam teori tradisional "Pemrograman Berorientasi Objek", enkapsulasi dan polimorfisme adalah sifat-sifat kunci dari objek; dalam implementasi OOP berbasis kelas seperti PHP, sifat dikemas dinyatakan sebagai bagian dari definisi kelas, dan diberikan tingkat akses ( public
, protected
, atau private
).
Namun, PHP juga memungkinkan Anda untuk secara dinamis menambahkan properti ke objek, seperti Anda akan kunci ke array, dan beberapa orang menggunakan objek kelas-kurang (secara teknis, contoh built in stdClass
, yang tidak memiliki metode atau fungsi pribadi) dalam cara yang sama cara untuk array asosiatif. Ini mengarah ke situasi di mana fungsi mungkin ingin tahu apakah properti tertentu telah ditambahkan ke objek yang diberikan padanya.
Seperti dengan kunci array, solusi untuk memeriksa properti objek termasuk dalam bahasa, cukup, disebutproperty_exists
,.
Kasus penggunaan yang tidak dapat dibenarkan, dengan diskusi
3. register_globals
, dan polusi lain dari namespace global
The register_globals
fitur ditambahkan variabel ke lingkup global yang namanya ditentukan oleh aspek permintaan HTTP (GET dan POST parameter, dan cookie). Ini dapat menyebabkan kode buggy dan tidak aman, yang karenanya telah dinonaktifkan secara default sejak PHP 4.2, dirilis Agustus 2000 dan dihapus sepenuhnya dalam PHP 5.4, dirilis Mar 2012 . Namun, ada kemungkinan bahwa beberapa sistem masih berjalan dengan fitur ini diaktifkan atau ditiru. Dimungkinkan juga untuk "mencemari" namespace global dengan cara lain, menggunakan global
kata kunci, atau $GLOBALS
array.
Pertama, register_globals
itu sendiri tidak mungkin menghasilkan null
variabel, karena nilai GET, POST, dan cookie akan selalu berupa string (dengan ''
masih kembali true
dari isset
), dan variabel dalam sesi harus sepenuhnya di bawah kendali programmer.
Kedua, pencemaran suatu variabel dengan nilai null
hanya masalah jika ini berlebihan menulis beberapa inisialisasi sebelumnya. "Over-writing" suatu variabel yang tidak diinisialisasi dengan null
hanya akan menjadi masalah jika kode di tempat lain membedakan antara kedua negara, jadi dengan sendirinya kemungkinan ini adalah argumen untuk tidak membuat perbedaan seperti itu.
4. get_defined_vars
dancompact
Beberapa fungsi yang jarang digunakan dalam PHP, seperti get_defined_vars
dan compact
, memungkinkan Anda untuk memperlakukan nama variabel seolah-olah mereka adalah kunci dalam array. Untuk variabel global, array super-global$GLOBALS
memungkinkan akses yang sama, dan lebih umum. Metode akses ini akan berperilaku berbeda jika variabel tidak didefinisikan dalam ruang lingkup yang relevan.
Setelah Anda memutuskan untuk memperlakukan serangkaian variabel sebagai array menggunakan salah satu dari mekanisme ini, Anda dapat melakukan semua operasi yang sama di atasnya seperti pada array normal apa pun. Akibatnya, lihat 1.
Fungsionalitas yang ada hanya untuk memprediksi bagaimana fungsi-fungsi ini akan berperilaku (misalnya "apakah akan ada 'foo' kunci dalam array yang dikembalikan oleh get_defined_vars
?") Adalah berlebihan, karena Anda dapat menjalankan fungsi dan mencari tahu tanpa efek buruk.
4a. Variabel variabel ( $$foo
)
Meskipun tidak persis sama dengan fungsi yang mengubah set variabel menjadi array asosiatif, kebanyakan kasus menggunakan "variabel variabel" ("tetapkan ke variabel bernama berdasarkan variabel lain ini") dapat dan harus diubah untuk menggunakan array asosiatif sebagai gantinya .
Nama variabel, pada dasarnya, adalah label yang diberikan pada nilai oleh pemrogram; jika Anda menentukannya pada saat run-time, itu bukan benar-benar label tetapi kunci di beberapa toko nilai kunci. Lebih praktisnya, dengan tidak menggunakan array, Anda kehilangan kemampuan untuk menghitung, beralih, dll; itu juga bisa menjadi tidak mungkin untuk memiliki variabel "di luar" toko kunci-nilai, karena mungkin ditulis berlebihan oleh $$foo
.
Setelah diubah untuk menggunakan array asosiatif, kode akan setuju untuk solusi 1. Akses properti objek tidak langsung (misalnya $foo->$property_name
) dapat diatasi dengan solusi 2.
5. isset
jauh lebih mudah untuk mengetik daripadaarray_key_exists
Saya tidak yakin ini benar-benar relevan, tapi ya, nama-nama fungsi PHP bisa sangat bertele-tele dan kadang-kadang tidak konsisten. Rupanya, versi PHP pra-sejarah menggunakan panjang nama fungsi sebagai kunci hash, jadi Rasmus sengaja membuat nama fungsi seperti htmlspecialchars
sehingga mereka akan memiliki jumlah karakter yang tidak biasa ...
Tetap saja, setidaknya kita tidak menulis Java, kan? ;)
6. Variabel yang tidak diinisialisasi memiliki tipe
The halaman manual pada dasar-dasar variabel meliputi pernyataan ini:
Variabel yang tidak diinisialisasi memiliki nilai default tipenya tergantung pada konteks di mana mereka digunakan
Saya tidak yakin apakah ada beberapa gagasan di Zend Engine tentang "tipe belum diinisialisasi tetapi diketahui" atau apakah ini terlalu banyak membaca pernyataan itu.
Yang jelas adalah bahwa tidak ada perbedaan praktis untuk perilaku mereka, karena perilaku yang dijelaskan pada halaman itu untuk variabel tidak diinisialisasi identik dengan perilaku variabel yang nilainya null
. Untuk memilih satu contoh, keduanya $a
dan $b
dalam kode ini akan berakhir sebagai integer 42
:
unset($a);
$a += 42;
$b = null;
$b += 42;
(Yang pertama akan memunculkan pemberitahuan tentang variabel yang tidak dideklarasikan, dalam upaya membuat Anda menulis kode yang lebih baik, tetapi tidak akan ada bedanya dengan bagaimana kode itu benar-benar berjalan.)
99. Mendeteksi jika suatu fungsi telah berjalan
(Menjaga yang terakhir ini, karena jauh lebih lama daripada yang lain. Mungkin aku akan mengeditnya nanti ...)
Pertimbangkan kode berikut:
$test_value = 'hello';
foreach ( $list_of_things as $thing ) {
if ( some_test($thing, $test_value) ) {
$result = some_function($thing);
}
}
if ( isset($result) ) {
echo 'The test passed at least once!';
}
Jika some_function
bisa kembali null
, ada kemungkinan echo
tidak akan tercapai walaupun some_test
dikembalikan true
. Tujuan programmer adalah untuk mendeteksi kapan $result
tidak pernah diatur, tetapi PHP tidak mengizinkan mereka untuk melakukannya.
Namun, ada masalah lain dengan pendekatan ini, yang menjadi jelas jika Anda menambahkan loop luar:
foreach ( $list_of_tests as $test_value ) {
// something's missing here...
foreach ( $list_of_things as $thing ) {
if ( some_test($thing, $test_value) ) {
$result = some_function($thing);
}
}
if ( isset($result) ) {
echo 'The test passed at least once!';
}
}
Karena $result
tidak pernah diinisialisasi secara eksplisit, itu akan mengambil nilai ketika tes pertama berlalu, sehingga tidak mungkin untuk mengetahui apakah tes berikutnya lulus atau tidak. Ini sebenarnya adalah bug yang sangat umum ketika variabel tidak diinisialisasi dengan benar.
Untuk memperbaikinya, kita perlu melakukan sesuatu di saluran tempat saya berkomentar bahwa ada sesuatu yang hilang. Solusi yang paling jelas adalah mengatur $result
ke "nilai terminal" yang some_function
tidak pernah bisa kembali; jika ini null
, maka sisa kode akan berfungsi dengan baik. Jika tidak ada kandidat alami untuk nilai terminal karena some_function
memiliki tipe pengembalian yang sangat tidak dapat diprediksi (yang mungkin merupakan pertanda buruk pada dirinya sendiri), maka nilai boolean tambahan, misalnya $found
, dapat digunakan sebagai gantinya.
Percobaan satu: very_null
konstanta
PHP secara teoritis dapat memberikan konstanta khusus - dan juga null
- untuk digunakan sebagai nilai terminal di sini; mungkin, akan ilegal untuk mengembalikan ini dari suatu fungsi, atau itu akan dipaksa null
, dan hal yang sama mungkin berlaku untuk meneruskannya sebagai argumen fungsi. Itu akan membuat kasus yang sangat spesifik ini sedikit lebih sederhana, tetapi segera setelah Anda memutuskan untuk memfaktorkan ulang kode - misalnya, untuk meletakkan loop dalam ke dalam fungsi yang terpisah - itu akan menjadi tidak berguna. Jika konstanta dapat dilewati antar fungsi, Anda tidak dapat menjamin bahwa some_function
tidak akan mengembalikannya, sehingga tidak lagi berguna sebagai nilai terminal universal.
Argumen untuk mendeteksi variabel yang tidak diinisialisasi dalam kasus ini bermuara pada argumen untuk konstanta khusus itu: jika Anda mengganti komentar dengan unset($result)
, dan memperlakukannya secara berbeda dari $result = null
, Anda memperkenalkan "nilai" untuk $result
yang tidak dapat diteruskan, dan hanya dapat terdeteksi oleh fungsi bawaan tertentu.
Percobaan dua pemikiran: penugasan tugas
Cara lain untuk berpikir tentang apa yang terakhir if
ditanyakan adalah "apakah ada yang membuat tugas $result
?" Daripada menganggapnya sebagai nilai khusus $result
, Anda mungkin bisa menganggap ini sebagai "metadata" tentang variabel, sedikit mirip dengan "variabel tainting" Perl. Jadi, daripada isset
Anda mungkin menyebutnya has_been_assigned_to
, dan bukan unset
, reset_assignment_state
.
Tetapi jika demikian, mengapa berhenti di boolean? Bagaimana jika Anda ingin tahu berapa kali tes lulus; Anda cukup memperluas metadata ke bilangan bulat dan memiliki get_assignment_count
dan reset_assignment_count
...
Tentunya, menambahkan fitur seperti itu akan memiliki kompromi dalam kompleksitas dan kinerja bahasa, sehingga perlu dipertimbangkan dengan hati-hati terhadap manfaat yang diharapkan. Seperti very_null
konstanta, ini akan berguna hanya dalam keadaan yang sangat sempit, dan akan sama-sama tahan terhadap re-factoring.
Pertanyaan mudah-mudahan-jelas adalah mengapa mesin runtime PHP harus mengasumsikan di muka bahwa Anda ingin melacak hal-hal seperti itu, daripada membiarkan Anda melakukannya secara eksplisit, menggunakan kode normal.