PHP neden 0'ın bir dizgeye eşit olduğunu düşünür?


111

Aşağıdaki kod parçasına sahibim:

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

Ürün fiyatını 0 olarak başlatmak ve ardından bununla ilgili bilgi almak amaçlanmıştır. Fiyat 'e' olarak bildirilirse, bir veri tabanında negatif bir sayı olarak saklanan satış yerine değişim anlamına gelir.

Ürün bir bonus olduğu için veya fiyat daha sonra belirleneceği için fiyatı 0 olarak bırakma imkanı da vardır.

Ancak, fiyat ayarlanmadığında, bu da onu başlangıç ​​değeri 0 ile bırakır if, yukarıda belirtilen döngü doğru olarak değerlendirilir ve fiyat -1 olarak ayarlanır. Yani 0'ı 'e'ye eşit olarak kabul eder.

Bu nasıl açıklanabilir?

Fiyat 0 olarak sağlandığında (başlatmadan sonra), davranış düzensizdir: bazen doğru, bazen yanlış olarak değerlendirilir. *


1
Double == yerine triple === kullanmanın beklenen davranışı verdiğini buldum. Ama yine de tuhaf.
Sérgio Domingues

2
(referans) PHP Kılavuzunda Tip Hokkabazlık bölümünde yeterince açıklanmış ve Tip Karşılaştırma Tablosu'nda gösterilmiştir
Gordon

1
Olası tek dize türü 'e' ise, bir is_string ($ öğe ["fiyat"]) kontrolüne gidemez misiniz? Bu, ==='den biraz daha verimli olacaktır. [kaynak belirtilmeli]
Jimmie Lin

içinde zayıf karşılaştırmalar dize arasında ve tamsayı dize tamsayıya dönüştürülür (yerine tamsayı dizeye "terfi" olma). if((string)$item['price'] == 'e')garip davranışı düzeltir. Daha fazla ayrıntı için stackoverflow.com/a/48912540/1579327 sayfasına bakın
Paolo

Lütfen aşağıdaki yorumlarda @Paolo tarafından yazılan başka bir duruma dikkat edin; burada 0 (tamsayı), çift eşittir operatörünü kullanırken diğer herhangi bir dizeye eşittir.
Haitham Sweilem

Yanıtlar:


113

==Türleri sizin için sıralayan yapıyorsunuz .

0bir int, dolayısıyla bu durumda 'e'bir int'e dönüştürülecektir. Hangisi bir olarak ayrıştırılamaz ve olacak 0. Bir dizge '0e'olur 0ve eşleşir!

kullanım ===


14
Gevşek karşılaştırmanın başka bir dezavantajı.
MC İmparator

5
Zor biri. Sadece buna rastladım ve neden string == 0'a şaşırdım. Bunu hatırlamalıyım.
Grzegorz

2
Dize anahtarları üzerinde döngü yaparken kafamı da bu konuya kaşıyordum, ancak dizinin ilk dize anahtar karşılaştırmasında doğru sonuç veren bir başlangıç ​​'sıfır' indeks öğesi vardı. Ben ne gibiydim Nasıl ... yani, elbette, bu cevap bunu açıklığa kavuşturdu! Tüm bu sorunun TEK bir cevabı kabul etmemesine şaşırdım. Sadece bazı soru soranların pislik olduğunu göstermeye gider.
IncredibleHat

48

Bunun nedeni, PHP'nin ==karşılaştırma operatörünün belirttiği karşılaştırma işlemini nasıl yaptığıdır:

Bir sayıyı bir dizeyle karşılaştırırsanız veya karşılaştırma sayısal dizeler içeriyorsa, her dize bir sayıya dönüştürülür ve karşılaştırma sayısal olarak gerçekleştirilir. […] Tür dönüşümü, karşılaştırma yapıldığında ===veya !==bu değerin yanı sıra türün karşılaştırılmasını içerdiğinden gerçekleşmez.

İlk işlenen bir sayı ( 0) ve ikincisi bir dize ( 'e') olduğundan, dizi de bir sayıya dönüştürülür (ayrıca bkz . Çeşitli Türlerle Karşılaştırma tablosu ). Dize veri türündeki kılavuz sayfası, dizeden sayıya dönüştürme işleminin nasıl yapıldığını tanımlar :

Bir dizge sayısal bir bağlamda değerlendirildiğinde, ortaya çıkan değer ve tür aşağıdaki gibi belirlenir.

Dize ' .', ' e' veya ' E' karakterlerinden herhangi birini içermiyorsa ve sayısal değer tamsayı türü sınırlarına uyuyorsa (ile tanımlandığı gibi PHP_INT_MAX), dize bir tamsayı olarak değerlendirilecektir. Diğer tüm durumlarda şamandıra olarak değerlendirilecektir.

Bu durumda dize, 'e'bu nedenle bir kayan nokta olarak değerlendirilecektir:

Değer, dizenin ilk kısmı tarafından verilir. Dize geçerli sayısal verilerle başlıyorsa, bu kullanılan değer olacaktır. Aksi takdirde, değer 0(sıfır) olacaktır. Geçerli sayısal veriler, isteğe bağlı bir işarettir, ardından bir veya daha fazla rakam (isteğe bağlı olarak bir ondalık nokta içerir) ve ardından isteğe bağlı bir üs gelir. Üs, bir ' e' veya ' E' ve ardından bir veya daha fazla rakamdır.

Gibi 'e'geçerli bir sayısal veri ile başlamaz, bu yüzer değerlendirir 0.


3
php çoğu şeyi kolayca karşılaştırılabilir olacak şekilde tasarlar ve sonra sadece günümüzü mahvetmek için birkaç sorun çıkarır. Bu, PHP'nin tasarım felsefesinin geri kalanına uymuyor. Aldatma yoksa felsefe var mı ???
user3338098

1
özellikle "e"
doğruya

20
"ABC" == 0

değerlendirir trueçünkü önce "ABC" tam sayıya dönüştürülür ve 0 sonra ile karşılaştırılır 0.

Bu, PHP dilinin tuhaf bir davranışıdır: Normalde bir kişi 0dizgeye yükseltilir "0"ve sonra "ABC"bir sonuçla karşılaştırılır false. Belki de zayıf karşılaştırmanın "ABC" == 0değerlendirildiği JavaScript gibi diğer dillerde olan budur false.

Kesin bir karşılaştırma yapmak sorunu çözer:

"ABC" === 0

değerlendirir false.

Peki ya sayıları dizeler olarak sayılarla karşılaştırmam gerekirse?

"123" === 123

falsesol ve sağ terim farklı türde olduğu için değerlendirir .

Aslında ihtiyaç duyulan şey, PHP tipi hokkabazlık tuzakları olmadan zayıf bir karşılaştırmadır.

Çözüm, terimleri açık bir şekilde dizeye yükseltmek ve ardından bir karşılaştırma yapmaktır (artık katı veya zayıf önemli değil).

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

dır-dir

true

süre

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

dır-dir

false


Orijinal koda uygulandı:

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

9

== operatörü, farklı türde olsalar bile değerleri eşleştirmeye çalışır. Örneğin:

'0' == 0 will be true

Tür karşılaştırmasına da ihtiyacınız varsa, === operatörünü kullanın:

'0' === 0 will be false

9

Senin sorunun, sağ üyeyi sol tipine yazacak olan çift eşit operatördür. Tercih ederseniz katı kullanın.

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

Kodunuza geri dönelim (yukarıda kopyalanmıştır). Bu durumda, çoğu durumda, $ öğe ['fiyat'] bir tamsayıdır (tabii ki e'ye eşit olduğu durumlar dışında). Bu nedenle, PHP yasalarına göre, PHP "e"tam sayıya yazılır ve bu da sonuç verir int(0). (Bana inanma? <?php $i="e"; echo (int)$i; ?>)

Bundan kolayca kurtulmak için, türü kontrol edecek ve dolaylı olarak yazım yapmayacak olan üçlü eşit (tam karşılaştırma) operatörünü kullanın.

Not: PHP'nin eğlenceli bir gerçeği: a == bbunu ima etmez b == a. Örneğinizi alın ve tersine çevirin: if ("e" == $item['price'])$ item ['fiyat'] her zaman bir tamsayı olduğu sürece asla yerine getirilmeyecektir.


7

PHP'de "0", "false", "off" as == false ve "1", "on", "true" as == true karışımlarını doğrulamak için oldukça kullanışlı bir yöntem vardır ve bu genellikle göz ardı edilir. GET / POST bağımsız değişkenlerini ayrıştırmak için özellikle yararlıdır:

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

Bu kullanım durumuyla tamamen alakalı değil, ancak benzerlik ve gerçek göz önüne alındığında, bu sonuç aramanın, "0" 'ı yanlış olarak doğrulama sorusunu sorarken bulma eğiliminde olduğu başkalarına yardımcı olacağını düşündüm.

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


6

Bunun ===yerine kullanmalısınız ==, çünkü sıradan operatör türleri karşılaştırmaz. Bunun yerine, öğeleri yazmayı deneyecektir.

Bu arada, ===tür öğeleri dikkate alır.

  • === "eşittir" anlamına gelir,
  • == anlamı "eeeeh .. biraz benziyor"

1
Anlıyorum. Bu artık çalışıyor (bir typecast ile):if((string)$item['price']=='e'){ $item['price'] = -1; }
Sérgio Domingues

ama bunu yapmamalısın. sadece ===operatörü kullanın
tereško

3

Aynı tuhaf davranışa rastlarken yaptığım örneklerle göstermenin en iyisi olduğunu düşünüyorum. Test durumuma bakın ve umarım davranışı daha iyi anlamanıza yardımcı olur:

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

Harika bir test, ben de aynısını yaptım ama güzel bir masa yaptım. cevabımı gör
IAMTHEBEST

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.