Mengapa PHP menganggap 0 sama dengan string?

111

Saya memiliki potongan kode berikut:

$item['price'] = 0;
/* Code to get item information goes in here */
if($item['price'] == 'e') {
    $item['price'] = -1;
}

Ini dimaksudkan untuk menginisialisasi harga item ke 0 dan kemudian mendapatkan informasi tentangnya. Jika harga diinformasikan sebagai 'e' itu berarti pertukaran, bukan penjualan, yang disimpan dalam database sebagai angka negatif.

Ada juga kemungkinan untuk meninggalkan harga sebagai 0, baik karena item tersebut adalah bonus atau karena harga akan ditetapkan nanti.

Namun, setiap kali harga tidak disetel, yang meninggalkannya dengan nilai awal 0, ifloop yang ditunjukkan di atas mengevaluasi sebagai benar dan harga disetel ke -1. Artinya, ia menganggap 0 sama dengan 'e'.

Bagaimana ini bisa dijelaskan?

Jika harga diberikan sebagai 0 (setelah inisialisasi), perilakunya tidak menentu: terkadang if dievaluasi sebagai benar, terkadang dievaluasi sebagai salah. *

Sérgio Domingues
sumber
1
Saya menemukan bahwa menggunakan triple === daripada double == memberikan perilaku yang diharapkan. Tapi tetap saja aneh.
Sérgio Domingues
2
(referensi) cukup dijelaskan dalam Manual PHP di bab Jenis Juggling dan diilustrasikan dalam Tabel Perbandingan Jenis
Gordon
1
Jika satu-satunya jenis string yang mungkin adalah 'e', ​​tidak bisakah Anda menggunakan pemeriksaan is_string ($ item ["price"])? Itu akan sedikit lebih efisien daripada ===. [Rujukan?]
Jimmie Lin
dalam perbandingan yang lemah antara string dan integer , string diubah menjadi integer (bukan integer yang "dipromosikan" menjadi string). if((string)$item['price'] == 'e')memperbaiki perilaku aneh. Lihat stackoverflow.com/a/48912540/1579327 untuk lebih jelasnya
Paolo
Harap perhatikan kasus lain dalam komentar di bawah ini oleh @Paolo di mana 0 (integer) sama dengan string lain saat menggunakan operator sama dengan ganda.
Haitham Sweilem

Jawaban:

113

Anda melakukan ==yang memilah jenis untuk Anda.

0adalah int, jadi dalam hal ini akan dilemparkan 'e'ke int. Yang tidak dapat diuraikan menjadi satu dan akan menjadi 0. Sebuah string '0e'akan menjadi 0dan akan cocok!

Menggunakan ===

Nanne
sumber
14
Kerugian lain dari perbandingan yang longgar.
MC Emperor
5
Yang rumit. Baru saja bertemu dengan yang ini dan kagum mengapa string == 0. Harus diingat itu.
Grzegorz
2
Saya menggaruk-garuk kepala saya yang satu ini juga ketika saya mengulang pada kunci string, tetapi array memiliki item indeks 'nol' awal yang terus menghasilkan true pada perbandingan kunci string pertama. Saya seperti apa Bagaimana di ... jadi, cukup yakin, jawaban ini menjelaskannya! Saya terkejut bahwa seluruh pertanyaan ini belum SATU jawaban yang diterima. Hanya pergi untuk menunjukkan beberapa penanya pertanyaan brengsek.
IncredibleHat
48

Ini karena bagaimana PHP melakukan operasi ==perbandingan yang ditunjukkan oleh operator pembanding :

Jika membandingkan angka dengan string atau perbandingan melibatkan string numerik, maka setiap string diubah menjadi angka dan perbandingan dilakukan secara numerik. […] Konversi jenis tidak terjadi bila perbandingannya ===atau !==karena ini melibatkan perbandingan jenis serta nilainya.

Karena operan pertama adalah angka ( 0) dan yang kedua adalah string ( 'e'), string juga diubah menjadi angka (lihat juga Tabel Perbandingan dengan Berbagai Jenis ). Halaman manual pada tipe data string mendefinisikan bagaimana konversi string ke angka dilakukan:

Saat string dievaluasi dalam konteks numerik, nilai dan jenis yang dihasilkan ditentukan sebagai berikut.

Jika string tidak berisi salah satu karakter ' .', ' e', atau ' E' dan nilai numerik cocok dengan batas jenis integer (seperti yang ditentukan oleh PHP_INT_MAX), string akan dievaluasi sebagai integer. Dalam semua kasus lain itu akan dievaluasi sebagai pelampung.

Dalam hal ini string adalah 'e'dan karenanya akan dievaluasi sebagai pelampung:

Nilai diberikan oleh bagian awal string. Jika string dimulai dengan data numerik yang valid, ini akan menjadi nilai yang digunakan. Jika tidak, nilainya akan menjadi 0(nol). Data numerik yang valid adalah tanda opsional, diikuti dengan satu atau beberapa digit (opsional berisi titik desimal), diikuti dengan eksponen opsional. Eksponennya adalah ' e' atau ' E' yang diikuti dengan satu atau lebih digit.

Karena 'e'tidak dimulai dengan data numerik yang valid, ia mengevaluasi ke float 0.

Gumbo
sumber
3
php mendesain hampir semuanya agar mudah dibandingkan dan kemudian melemparkan beberapa gotcha hanya untuk merusak hari kita. Ini tidak sesuai dengan filosofi desain PHP lainnya. Kecuali penipuan apakah ada filosofi ???
pengguna3338098
1
terutama karena "e" menghasilkan true dan "" menghasilkan false
pengguna3338098
20
"ABC" == 0

mengevaluasi truekarena pertama "ABC" diubah menjadi bilangan bulat dan menjadi 0 kemudian dibandingkan dengan 0.

Ini adalah perilaku aneh dari bahasa PHP: biasanya seseorang diharapkan 0untuk dipromosikan menjadi string "0"dan kemudian dibandingkan "ABC"dengan hasil false. Mungkin itulah yang terjadi dalam bahasa lain seperti JavaScript di mana "ABC" == 0evaluasi perbandingan lemah false.

Melakukan perbandingan yang ketat menyelesaikan masalah:

"ABC" === 0

mengevaluasi false.

Tetapi bagaimana jika saya perlu membandingkan angka sebagai string dengan angka?

"123" === 123

mengevaluasi falsekarena suku kiri dan kanan memiliki tipe yang berbeda.

Apa yang sebenarnya dibutuhkan adalah perbandingan yang lemah tanpa perangkap juggling tipe PHP.

Solusinya adalah dengan mempromosikan istilah secara eksplisit menjadi string dan kemudian melakukan perbandingan (ketat atau lemah tidak masalah lagi).

(string)"123" === (string)123

adalah

true

sementara

(string)"123" === (string)0

adalah

false


Diterapkan ke kode asli:

$item['price'] = 0;
/*code to get item information goes in here*/
if((string)$item['price'] == 'e') {
    $item['price'] = -1;
}
Paolo
sumber
9

Operator == akan mencoba mencocokkan nilai meskipun nilainya berbeda. Misalnya:

'0' == 0 will be true

Jika Anda juga membutuhkan perbandingan jenis, gunakan operator ===:

'0' === 0 will be false
Vincent Mimoun-Prat
sumber
9

Masalah Anda adalah operator yang sama ganda, yang akan mengetikkan anggota kanan ke jenis kiri. Gunakan yang ketat jika Anda mau.

if($item['price'] == 'e') {
    $item['price'] = -1;
}

Mari kembali ke kode Anda (disalin di atas). Dalam kasus ini, dalam banyak kasus, $ item ['price'] adalah integer (kecuali jika sama dengan e, tentunya). Dengan demikian, berdasarkan hukum PHP, PHP akan melakukan typecast "e"ke integer, yang menghasilkan int(0). (Jangan percaya padaku? <?php $i="e"; echo (int)$i; ?>).

Untuk dengan mudah lolos dari ini, gunakan operator triple equal (perbandingan tepat), yang akan memeriksa jenis dan tidak akan typecast secara implisit.

PS: fakta menyenangkan PHP: a == btidak menyiratkan itu b == a. Ambil contoh Anda dan balikkan: if ("e" == $item['price'])tidak akan pernah benar-benar terpenuhi asalkan $ item ['price'] selalu berupa bilangan bulat.

Sébastien Renauld
sumber
7

Ada metode yang agak berguna dalam PHP untuk memvalidasi campuran "0", "false", "off" as == false dan "1", "on", "true" as == true yang sering diabaikan. Ini sangat berguna untuk mengurai argumen GET / POST:

filter_var( $item['price'], FILTER_VALIDATE_BOOLEAN );

Ini tidak sepenuhnya relevan dengan kasus penggunaan ini, tetapi mengingat kesamaan dan fakta ini adalah hasil pencarian yang cenderung ditemukan ketika mengajukan pertanyaan untuk memvalidasi (string) "0" sebagai salah, saya pikir itu akan membantu orang lain.

http://www.php.net/manual/en/filter.filters.validate.php

Jim Morrison
sumber
6

Anda harus menggunakan ===bukan ==, karena operator biasa tidak membandingkan tipe. Sebaliknya itu akan mencoba untuk mengetikan item.

Sementara itu ===mempertimbangkan jenis barang.

  • === berarti "sama dengan",
  • == artinya "eeeeh .. agak mirip"
tereško
sumber
1
Saya melihat. Ini sekarang bekerja (dengan typecast):if((string)$item['price']=='e'){ $item['price'] = -1; }
Sérgio Domingues
tapi sebaiknya jangan lakukan itu. cukup gunakan ===operator
tereško
3

Saya pikir yang terbaik adalah menunjukkan dengan contoh yang saya lakukan, sambil mengalami perilaku aneh yang sama. Lihat kasus pengujian saya dan semoga ini akan membantu Anda memahami perilakunya dengan lebih baik:

// Normal comparison using the == Operator
echo (0 == "0"); // true
echo (0 == "a"); // true
echo (0 == "safta!"); // true
echo (1000 == "bla"); // false. It appears that PHP has a weird behavior only with the number / string 0 / "0" according to the past 3 examples.
echo (23 == "23"); // true. So as we said, PHP has a problem (not a problem but weird behavior) only when the number / string 0 (or "0") is present
echo (23 == "24"); // false. values aren't equal (unlike last example). The type is less relevant with the == operator as we can see.

// Now using the === and !== Operators
echo ("0" === 0); // false, since === requires both value and type to be the same. Here, type is different (int vs string)
echo ("0" !== 0); // true because they aren't the same in terms of === comparison (type is different and that's why it's true)
echo ("bla" === "blaa"); // false because the values are not the same. The type is the same, but === checks for both equal type and equal value.

//Now using casting and === Operator:
echo ((string)123 === "123"); // true. The casting of the int 123 to string changed it to "123" and now both variables have same value and are of same type
echo ((int)"123" === 123); // true. The casting of the string 123 to int, changed it to int, and now both variables are of same value and type (which is exactly what the === operator is looking for)

// Now using casting and == Operator. Basically, as we've seen above, the == care less for the
// type of var, but more to the value. So the casting is less relevant here, because even
// without casting, like we saw earlier, we can still compare string to int with the == operator
// and if their value is same, we'll get true. Either way, we will show that:
echo ((string)123 == "123"); // true. The casting of the int 123 to string changed it to "123" and now both vars have same value and are of same type
echo ((int)"123" == 123); // true. The casting of the string 123 to int, changed it to int, and now both vars are of same value and type (which is exactly what the === operator is looking for)
Kosem
sumber
Tes yang bagus, saya melakukan hal yang sama tetapi saya membuat meja yang bagus. lihat jawaban saya
IAMTHEBEST
0

Pada dasarnya, selalu gunakan ===operator, untuk menjamin keamanan tipe.

Masukkan deskripsi gambar di sini

Masukkan deskripsi gambar di sini

SAYA YANG TERBAIK
sumber