Bagaimana menghindari isset () dan empty ()

98

Saya memiliki beberapa aplikasi lama yang membuang banyak pesan "xyz tidak terdefinisi" dan "offset tak terdefinisi" saat berjalan pada tingkat kesalahan E_NOTICE, karena keberadaan variabel tidak secara eksplisit diperiksa menggunakan isset()dan selir.

Saya sedang mempertimbangkan untuk mengatasinya agar kompatibel dengan E_NOTICE, karena pemberitahuan tentang variabel yang hilang atau offset dapat menjadi penyelamat, mungkin ada beberapa peningkatan kinerja kecil yang bisa diperoleh, dan secara keseluruhan cara yang lebih bersih.

Namun, saya tidak suka apa yang menyebabkan ratusan isset() empty()dan array_key_exists()dilakukan pada kode saya. Itu membengkak, menjadi kurang dapat dibaca, tanpa mendapatkan apa pun dalam hal nilai atau makna.

Bagaimana saya dapat menyusun kode saya tanpa pemeriksaan variabel yang berlebihan, sekaligus juga kompatibel dengan E_NOTICE?

Pekka
sumber
6
Aku sangat setuju. Itulah mengapa saya sangat menyukai Zend Framework, modul permintaannya sangat bagus di sana. Jika saya mengerjakan beberapa aplikasi kecil, saya biasanya membuat kode beberapa kelas permintaan sederhana dengan metode ajaib __set dan __get yang berfungsi mirip dengan permintaan ZF. Dengan cara itu saya menghindari semua kejadian isset dan kosong dalam kode saya. Dengan cara itu semua yang perlu Anda gunakan adalah if (count ($ arr)> 0) pada array sebelum mengulanginya dan if (null! == $ variable) di beberapa tempat kritis. Ini terlihat jauh lebih bersih.
Richard Knop

Jawaban:

128

Bagi mereka yang tertarik, saya telah memperluas topik ini menjadi artikel kecil, yang memberikan informasi di bawah ini dalam bentuk terstruktur yang agak lebih baik: Panduan Definitif Untuk isset PHP Dan kosong


IMHO Anda harus berpikir tentang tidak hanya membuat aplikasi "E_NOTICE kompatibel", tetapi juga merestrukturisasi semuanya. Memiliki ratusan poin dalam kode Anda yang secara teratur mencoba menggunakan variabel yang tidak ada terdengar seperti program yang terstruktur dengan buruk. Mencoba mengakses variabel yang tidak ada seharusnya tidak pernah terjadi, bahasa lain menolak ini pada waktu kompilasi. Fakta bahwa PHP memungkinkan Anda untuk melakukannya tidak berarti Anda harus melakukannya.

Peringatan ini ada untuk membantu Anda, bukan untuk mengganggu Anda. Jika Anda mendapat peringatan, "Anda mencoba bekerja dengan sesuatu yang tidak ada!" , reaksi Anda seharusnya "Ups, saya buruk, biarkan saya memperbaikinya secepatnya." Bagaimana lagi Anda akan membedakan antara "variabel yang bekerja dengan baik tanpa definisi" dan kode yang benar - benar salah yang dapat menyebabkan kesalahan serius ? Ini juga alasan mengapa Anda selalu, selalu , mengembangkan dengan pelaporan kesalahan berubah menjadi 11 dan terus memasukkan kode Anda sampai tidak ada satu punNOTICE yang dikeluarkan. Menonaktifkan pelaporan kesalahan hanya untuk lingkungan produksi, untuk menghindari kebocoran informasi dan memberikan pengalaman pengguna yang lebih baik bahkan saat menghadapi kode buggy.


Untuk menguraikan:

Anda akan selalu membutuhkan issetatau emptydi suatu tempat dalam kode Anda, satu-satunya cara untuk mengurangi kemunculannya adalah dengan menginisialisasi variabel Anda dengan benar. Bergantung pada situasinya, ada berbagai cara untuk melakukannya:

Argumen fungsi:

function foo ($bar, $baz = null) { ... }

Tidak perlu untuk memeriksa apakah $baratau $bazditetapkan dalam fungsi karena Anda hanya mengatur mereka, semua yang perlu Anda khawatir tentang adalah jika nilai mengevaluasi mereka untuk trueatau false(atau apapun yang lain).

Variabel biasa di mana saja:

$foo = null;
$bar = $baz = 'default value';

Inisialisasi variabel Anda di bagian atas blok kode tempat Anda akan menggunakannya. Ini memecahkan !issetmasalah, memastikan bahwa variabel Anda selalu memiliki nilai default yang diketahui, memberi pembaca gambaran tentang apa yang akan dikerjakan kode berikut dan dengan demikian juga berfungsi sebagai semacam dokumentasi mandiri.

Array:

$defaults = array('foo' => false, 'bar' => true, 'baz' => 'default value');
$values = array_merge($defaults, $incoming_array);

Hal yang sama seperti di atas, Anda menginisialisasi array dengan nilai default dan menimpanya dengan nilai aktual.

Dalam kasus yang tersisa, katakanlah template tempat Anda mengeluarkan nilai yang mungkin atau mungkin tidak disetel oleh pengontrol, Anda hanya perlu memeriksa:

<table>
    <?php if (!empty($foo) && is_array($foo)) : ?>
        <?php foreach ($foo as $bar) : ?>
            <tr>...</tr>
        <?php endforeach; ?>
    <?php else : ?>
        <tr><td>No Foo!</td></tr>
    <?php endif; ?>
</table>

Jika Anda sering menggunakannya array_key_exists, Anda harus mengevaluasi untuk apa Anda menggunakannya. Satu-satunya saat hal itu membuat perbedaan adalah di sini:

$array = array('key' => null);
isset($array['key']); // false
array_key_exists('key', $array); // true

Seperti yang dinyatakan di atas, jika Anda menginisialisasi variabel Anda dengan benar, Anda tidak perlu memeriksa apakah kuncinya ada atau tidak, karena Anda tahu itu ada. Jika Anda mendapatkan array dari sumber eksternal, nilai akan kemungkinan besar tidak akan nulltetapi '', 0, '0', falseatau sesuatu seperti itu, yaitu nilai yang Anda dapat mengevaluasi dengan issetatau empty, tergantung pada niat Anda. Jika Anda secara teratur menyetel kunci array ke nulldan ingin artinya tidak berarti apa pun false, yaitu jika dalam contoh di atas hasil yang berbeda issetdan array_key_existsmembuat perbedaan pada logika program Anda, Anda harus bertanya pada diri sendiri mengapa. Keberadaan variabel seharusnya tidak penting, hanya nilainya yang harus menjadi konsekuensinya. Jika kuncinya adalah true/ falseflag, maka gunakantrueatau . Satu-satunya pengecualian untuk ini adalah perpustakaan pihak ke-3 yang ingin memiliki arti, tetapi karena sangat sulit untuk dideteksi di PHP, saya belum menemukan perpustakaan apa pun yang melakukan ini.false , bukannullnullnull

menipu
sumber
4
Benar, tetapi sebagian besar upaya akses yang gagal berada di sepanjang garis if ($array["xyz"])alih - alih isset()atau array_key_exists()yang menurut saya agak sah, tentu saja bukan masalah struktural (perbaiki saya jika saya salah). Menambahkan array_key_exists()hanya terlihat seperti pemborosan yang mengerikan bagi saya.
Pekka
9
Saya tidak bisa memikirkan kasus apa pun di mana saya akan menggunakan array_key_existsalih-alih yang sederhana isset($array['key'])atau !empty($array['key']). Tentu, keduanya menambahkan 7 atau 8 karakter ke kode Anda, tapi saya tidak akan menyebut itu masalah. Ini juga membantu untuk memperjelas kode Anda: if (isset($array['key']))berarti variabel ini memang opsional dan mungkin tidak ada, sedangkan if ($array['key'])hanya berarti "jika benar". Jika Anda mendapat pemberitahuan untuk yang terakhir, Anda tahu logika Anda kacau di suatu tempat.
menipu
6
Saya percaya perbedaan antara isset () dan array_key_exists () adalah bahwa yang terakhir akan mengembalikan true jika nilainya adalah NULL. isset () tidak akan.
Htbaa
1
Benar, tapi saya tidak bisa memikirkan kasus penggunaan yang waras di mana saya perlu membedakan antara variabel yang tidak ada dan kunci set yang nilainya null. Jika nilai dievaluasi ke SALAH, perbedaannya harus tanpa perbedaan. :)
menipu
1
Kunci array tentu lebih mengganggu daripada variabel yang tidak ditentukan. Tapi jika Anda tidak yakin apakah array berisi kunci atau tidak, itu berarti baik Anda tidak mendefinisikan array sendiri atau Anda menariknya dari sumber yang Anda tidak mengontrol. Tidak ada skenario yang harus sering terjadi; dan jika itu terjadi, Anda punya banyak alasan untuk memeriksa apakah array berisi apa yang menurut Anda dilakukannya. Ini adalah ukuran keamanan IMO.
kijin
37

Cukup tulis fungsi untuk itu. Sesuatu seperti:

function get_string($array, $index, $default = null) {
    if (isset($array[$index]) && strlen($value = trim($array[$index])) > 0) {
        return get_magic_quotes_gpc() ? stripslashes($value) : $value;
    } else {
        return $default;
    }
}

yang dapat Anda gunakan sebagai

$username = get_string($_POST, 'username');

Lakukan hal yang sama untuk hal-hal sepele seperti get_number(), get_boolean(), get_array()dan sebagainya.

BalusC
sumber
5
Ini terlihat bagus, dan melakukan pengecekan magic_quotes juga. Bagus!
Pekka
Fungsi yang bagus! Terima kasih banyak telah berbagi.
Mike Moore
3
Perhatikan bahwa $ _POST ['sesuatu'] dapat mengembalikan array, misalnya input dengan <input name="something[]" />. Ini akan menyebabkan kesalahan (karena trim tidak dapat diterapkan ke array) menggunakan kode di atas, dalam hal ini harus digunakan is_stringdan mungkin strval. Ini bukan hanya kasus di mana seseorang harus menggunakan get_arraybaik karena input pengguna (jahat) mungkin apa saja dan parser input pengguna tidak boleh membuang kesalahan.
Ciantic
1
Saya menggunakan jenis fungsi yang sama tetapi didefinisikan seperti itu: function get_value (& $ item, $ default = NULL) {return isset ($ item)? $ item: $ default; } Keuntungan dari fungsi ini adalah Anda bisa memanggilnya dengan array, variabel dan objek. Kekurangannya adalah bahwa $ item akan diinisialisasi (menjadi null) setelahnya jika tidak.
Mat
Anda harus mematikan kutipan ajaib secara global, daripada menanganinya dalam 1 fungsi. Ada banyak sumber di internet yang menjelaskan kutipan ajaib.
Kayla
13

Saya percaya salah satu cara terbaik untuk mengatasi masalah ini adalah dengan mengakses nilai array GET dan POST (COOKIE, SESSION, dll.) Melalui kelas.

Buat kelas untuk setiap array tersebut dan deklarasikan __getserta __setmetode ( overloading ). __getmenerima satu argumen yang akan menjadi nama nilai. Metode ini harus memeriksa nilai ini dalam larik global terkait, baik menggunakan isset()atau empty()dan mengembalikan nilai jika ada ataunull (atau nilai default lainnya) sebaliknya.

Setelah itu Anda dapat mengakses nilai array dengan percaya diri dengan cara ini: $POST->usernamedan melakukan validasi apa pun jika diperlukan tanpa menggunakan isset()s atau empty()s. Jika usernametidak ada dalam larik global terkait maka nullakan dikembalikan, jadi tidak ada peringatan atau pemberitahuan yang akan dibuat.

Jamol
sumber
1
Ini adalah ide yang bagus, dan sesuatu yang saya siap untuk menyusun ulang kodenya. +1
Pekka
Sayangnya, Anda tidak akan dapat membuat instance tersebut menjadi superglobal kecuali Anda menetapkannya ke $ _GET atau $ _POST yang akan sangat jelek. Tapi tentu saja Anda bisa menggunakan kelas statis ...
ThiefMaster
1
Anda tidak dapat menggunakan getter dan setter pada "kelas statis". dan menulis satu kelas per variabel adalah praktik yang buruk karena menyiratkan duplikasi kode, yang juga buruk. Saya rasa solusi ini bukan yang paling memadai.
Mat
Anggota statis publik dari suatu kelas bertindak seperti superglobal, yaitu: HTTP :: $ POST-> nama pengguna, di mana Anda membuat contoh HTTP :: $ POST di beberapa titik sebelum digunakan, yaitu. Kelas HTTP {public static $ POST = array (); ...}; HTTP :: $ POST = new someClass ($ _ POST); ...
velcrow
6

Saya tidak keberatan menggunakan array_key_exists()fungsi tersebut. Sebenarnya, saya lebih suka menggunakan fungsi khusus ini daripada mengandalkan fungsi hack yang dapat mengubah perilaku mereka di masa depan seperti emptydanisset (menyerang untuk menghindari kerentanan ).


Bagaimanapun, saya menggunakan fungsi sederhana yang berguna dalam hal ini, dan beberapa situasi lain dalam menangani indeks array :

function Value($array, $key, $default = false)
{
    if (is_array($array) === true)
    {
        settype($key, 'array');

        foreach ($key as $value)
        {
            if (array_key_exists($value, $array) === false)
            {
                return $default;
            }

            $array = $array[$value];
        }

        return $array;
    }

    return $default;
}

Katakanlah Anda memiliki array berikut:

$arr1 = array
(
    'xyz' => 'value'
);

$arr2 = array
(
    'x' => array
    (
        'y' => array
        (
            'z' => 'value',
        ),
    ),
);

Bagaimana Anda mendapatkan "nilai" dari array? Sederhana:

Value($arr1, 'xyz', 'returns this if the index does not exist');
Value($arr2, array('x', 'y', 'z'), 'returns this if the index does not exist');

Kami sudah memiliki array uni dan multi-dimensi yang tercakup, apa lagi yang bisa kami lakukan?


Ambil potongan kode berikut ini misalnya:

$url = '/programming/1960509';
$domain = parse_url($url);

if (is_array($domain) === true)
{
    if (array_key_exists('host', $domain) === true)
    {
        $domain = $domain['host'];
    }

    else
    {
        $domain = 'N/A';
    }
}
else
{
    $domain = 'N/A';
}

Cukup membosankan bukan? Berikut adalah pendekatan lain menggunakan Value()fungsi tersebut:

$url = '/programming/1960509';
$domain = Value(parse_url($url), 'host', 'N/A');

Sebagai contoh tambahan, ambil RealIP()fungsi untuk tes:

$ip = Value($_SERVER, 'HTTP_CLIENT_IP', Value($_SERVER, 'HTTP_X_FORWARDED_FOR', Value($_SERVER, 'REMOTE_ADDR')));

Rapi, ya? ;)

Alix Axel
sumber
6
"Mengandalkan fungsi hack yang dapat mengubah perilaku mereka di masa depan" ?! Maaf, tapi itu tentang hal paling konyol yang pernah saya dengar sepanjang minggu. Pertama-tama, issetdan emptymerupakan konstruksi bahasa , bukan fungsi. Kedua, jika ada fungsi pustaka inti / konstruksi bahasa mengubah perilakunya, Anda mungkin akan kacau atau tidak. Bagaimana jika array_key_existsperilakunya berubah? Jawabannya tidak akan, selama Anda menggunakannya sebagai dokumen. Dan issetdidokumentasikan untuk digunakan dengan tepat. Fungsi kasus terburuk tidak digunakan lagi pada satu atau dua versi rilis utama. Sindrom NIH buruk!
menipu
Saya deceze maaf, tapi pertama-tama hack dalam huruf miring dalam kasus Anda tidak menyadari. =) Kedua, maksud Anda bahwa seseorang tidak boleh mengandalkan array_key_exists()untuk memeriksa apakah ada kunci dalam array ?! array_key_exists()itu dibuat persis untuk itu , saya lebih mengandalkan untuk tujuan ini dari isset()dan khusus empty()yang deskripsi resmi adalah: "menentukan apakah suatu variabel kosong", tidak menyebutkan apa-apa jika itu benar-benar ada. Komentar dan suara negatif Anda adalah salah satu yang paling konyol yang pernah saya saksikan sepanjang bulan .
Alix Axel
3
Saya katakan issetdan emptytidak lebih atau kurang dapat diandalkan daripada array_key_existsdan dapat melakukan pekerjaan yang persis sama. Contoh Anda yang kedua dan bertele-tele dapat ditulis $domain = isset($domain['host']) ? $domain['host'] : 'N/A';hanya dengan fitur bahasa inti, tidak perlu panggilan atau deklarasi fungsi tambahan (perhatikan bahwa saya tidak selalu menganjurkan penggunaan operator terner; o)). Untuk variabel skalar biasa, Anda masih perlu menggunakan issetatau empty, dan Anda dapat menggunakannya untuk array dengan cara yang persis sama. "Keandalan" adalah alasan buruk untuk tidak melakukannya.
menipu
1
Anda membuat poin Anda, meskipun saya tidak setuju dengan sebagian besar hal yang Anda katakan. Saya pikir Anda salah pada 90% + kasus, misalnya saya menggunakan nilai "0" di bidang tersembunyi di formulir sepanjang waktu. Namun saya yakin solusi yang saya berikan tidak pantas untuk ditolak dan mungkin berguna bagi Pekka.
Alix Axel
2
Sementara @deceze ada hubungannya dengan fungsi kustom - saya biasanya mengambil sikap yang sama - pendekatan value () terlihat cukup menarik sehingga saya akan melihatnya. Saya pikir jawaban dan tindak lanjutnya akan memungkinkan semua orang yang menemukannya nanti untuk mengambil keputusan sendiri. +1.
Pekka
3

Aku disini bersama mu. Tapi desainer PHP telah membuat kesalahan yang jauh lebih buruk dari itu. Singkatnya, mendefinisikan fungsi kustom untuk pembacaan nilai apa pun, tidak ada jalan lain.

vava
sumber
1
isset () barang. Membuat semuanya menjadi nol secara default akan menghemat banyak masalah.
vava
2
Dan apakah 'segalanya' ini? Tampaknya sia-sia jika PHP harus membayangkan setiap nama variabel yang dapat dibayangkan dan menetapkannya ke NULL agar pengembang yang malas dapat menghindari mengetik 5 karakter.
Lotus Notes
5
@Byron, lihat, ini sangat sederhana, banyak bahasa lain melakukannya, Ruby dan Perl sebagai beberapa contohnya. VM tahu apakah variabel digunakan sebelumnya atau tidak, bukan? Itu selalu dapat mengembalikan null alih-alih gagal dengan atau tanpa pesan kesalahan. Dan ini bukan tentang 5 karakter yang buruk, ini tentang menulis params["width"] = params["width"] || 5untuk menyetel default alih-alih semua omong kosong dengan isset()panggilan.
vava
3
Maaf telah menghidupkan kembali utas lama. Dua kesalahan terburuk PHP adalah register_globalsdan magic_quotes. Masalah yang dikembangkan ini membuat variabel yang tidak diinisialisasi terlihat hampir tidak berbahaya jika dibandingkan.
statika
3

Saya menggunakan fungsi ini

function load(&$var) { return isset($var) ? $var : null; }
function POST($var) { return isset($_POST[$var]) ? $_POST[$var] : null; }

Contoh

$y = load($x); // null, no notice

// this attitude is both readable and comfortable
if($login=POST("login") and $pass=POST("pass")) { // really =, not ==
  // executes only if both login and pass were in POST
  // stored in $login and $pass variables
  $authorized = $login=="root" && md5($pass)=="f65b2a087755c68586568531ad8288b4";
}
Jan Turoň
sumber
2
Saya menggunakan ini juga, tetapi ingat bahwa dalam beberapa kasus, variabel Anda akan diinisialisasi secara otomatis: mis. Load ($ array ['FOO']) akan membuat kunci FOO dalam $ array.
Mat
2

Selamat datang di operator penggabungan nol (PHP> = 7.0.1):

$field = $_GET['field'] ?? null;

PHP mengatakan:

Operator penggabungan nol (??) telah ditambahkan sebagai gula sintaksis untuk kasus umum yang memerlukan penggunaan terner dalam hubungannya dengan isset (). Ia mengembalikan operan pertamanya jika ada dan bukan NULL; jika tidak, ia mengembalikan operan keduanya.

Alexandre Thebaldi
sumber
1

Buat fungsi yang mengembalikan falsejika tidak disetel, dan, jika ditentukan, falsejika kosong. Jika valid itu mengembalikan variabel. Anda dapat menambahkan lebih banyak opsi seperti yang terlihat pada kode di bawah ini:

<?php
function isset_globals($method, $name, $option = "") {
    if (isset($method[$name])) {    // Check if such a variable
        if ($option === "empty" && empty($method[$name])) { return false; } // Check if empty 
        if ($option === "stringLength" && strlen($method[$name])) { return strlen($method[$name]); }    // Check length of string -- used when checking length of textareas
        return ($method[$name]);
    } else { return false; }
}

if (!isset_globals("$_post", "input_name", "empty")) {
    echo "invalid";
} else {
    /* You are safe to access the variable without worrying about errors! */
    echo "you uploaded: " . $_POST["input_name"];
}
?>
Naga api
sumber
0

Perangkat lunak tidak secara ajaib dijalankan oleh rahmat Tuhan. Jika Anda mengharapkan sesuatu yang hilang, Anda harus menanganinya dengan benar.

Jika Anda mengabaikannya, Anda mungkin membuat lubang keamanan di aplikasi Anda. Dalam bahasa statis, mengakses variabel yang tidak ditentukan itu tidak mungkin. Itu tidak akan hanya mengkompilasi atau merusak aplikasi Anda jika null.

Selain itu, itu membuat aplikasi Anda tidak dapat dikelola, dan Anda akan menjadi gila ketika hal-hal yang tidak terduga terjadi. Ketegasan bahasa adalah suatu keharusan dan PHP, secara desain, salah dalam banyak aspek. Ini akan membuat Anda menjadi programmer yang buruk jika Anda tidak menyadarinya.

knoopx.dll
sumber
Saya sangat menyadari kekurangan PHP. Seperti yang saya tunjukkan dalam pertanyaan, saya berbicara tentang perombakan proyek lama.
Pekka
Sepakat. Menjadi pengembang PHP ling-waktu cukup sulit bagi saya untuk menjelajah ke bahasa baru seperti Java di mana Anda perlu mendeklarasikan semuanya.
Dzhuneyt
0

Saya tidak yakin apa definisi Anda tentang keterbacaan, tetapi penggunaan yang tepat dari blok empty (), isset () dan try / throw / catch, cukup penting untuk keseluruhan proses.

Jika E_NOTICE Anda berasal dari $ _GET atau $ _POST, maka E_NOTICE Anda harus diperiksa dengan empty () bersama dengan semua pemeriksaan keamanan lain yang harus dilalui data tersebut.

Jika berasal dari feed atau pustaka eksternal, itu harus dibungkus dengan try / catch.

Jika berasal dari database, $ db_num_rows () atau yang setara harus diperiksa.

Jika berasal dari variabel internal, mereka harus diinisialisasi dengan benar. Seringkali, jenis pemberitahuan ini berasal dari menetapkan variabel baru untuk mengembalikan fungsi yang mengembalikan FALSE saat gagal. Mereka harus digabungkan dalam pengujian yang, jika terjadi kegagalan, dapat menetapkan variabel nilai default yang dapat diterima yang dapat ditangani kode, atau melontarkan pengecualian yang dapat ditangani kode.

Hal-hal ini membuat kode lebih panjang, menambahkan blok ekstra, dan menambahkan tes ekstra, tetapi saya tidak setuju dengan Anda karena saya pikir mereka pasti menambah nilai ekstra.

Mlutz
sumber
-2

Bagaimana dengan menggunakan @ operator?

Sebagai contoh:

if(@$foo) { /* Do something */ }

Anda mungkin mengatakan ini buruk karena Anda tidak memiliki kendali atas apa yang terjadi "di dalam" $ foo (jika itu adalah panggilan fungsi yang berisi kesalahan PHP misalnya), tetapi jika Anda hanya menggunakan teknik ini untuk variabel, ini setara dengan:

if(isset($foo) && $foo) { /* ... */ }
Tikar
sumber
if(isset($foo))sebenarnya sudah cukup. Ini akan kembali TRUEjika ekspresi mengevaluasi ke TRUE.
Dzhuneyt
2
@ ColorWP.com juga akan mengembalikan nilai true jika ekspresi bernilai false.
Jon Hulka
Anda hanya boleh menggunakan parameter @ (untuk mengabaikan pemberitahuan) pada kode yang tidak benar-benar dalam pengembangan lebih lanjut, atau pada kode satu kali atau perbaikan cepat pada proyek yang ada, yang tidak ingin Anda tunjukkan kepada orang lain. Tetapi ini adalah solusi umum untuk peretasan cepat.
rubo77