เหตุใด PHP จึงพิจารณาให้ 0 เท่ากับสตริง


112

ฉันมีรหัสต่อไปนี้:

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

มีวัตถุประสงค์เพื่อเริ่มต้นราคาสินค้าเป็น 0 จากนั้นรับข้อมูลเกี่ยวกับราคานั้น หากราคาถูกแจ้งเป็น 'e' หมายถึงการแลกเปลี่ยนแทนการขายซึ่งถูกเก็บไว้ในฐานข้อมูลเป็นจำนวนลบ

นอกจากนี้ยังมีความเป็นไปได้ที่จะปล่อยให้ราคาเป็น 0 เนื่องจากไอเท็มนั้นเป็นโบนัสหรือเนื่องจากราคาจะถูกกำหนดในอีกสักครู่

แต่เมื่อใดก็ตามที่ไม่ได้กำหนดราคาซึ่งปล่อยให้ราคาเริ่มต้นเป็น 0 ifลูปที่ระบุด้านบนจะประเมินว่าเป็นจริงและราคาจะถูกกำหนดเป็น -1 นั่นคือมันถือว่า 0 เท่ากับ 'e'

สิ่งนี้สามารถอธิบายได้อย่างไร?

เมื่อระบุราคาเป็น 0 (หลังการเริ่มต้น) พฤติกรรมจะไม่แน่นอน: บางครั้งถ้าประเมินว่าเป็นจริงบางครั้งก็ประเมินว่าเป็นเท็จ *


1
ฉันพบว่าการใช้ triple === แทน double == ให้พฤติกรรมที่คาดหวัง แต่มันก็ยังแปลก
Sérgio Domingues

2
(อ้างอิง)อธิบายไว้อย่างเพียงพอในคู่มือ PHP ที่บทType Jugglingและแสดงไว้ในตารางเปรียบเทียบประเภท
Gordon

1
ถ้าสตริงประเภทเดียวที่เป็นไปได้คือ 'e' คุณไม่สามารถตรวจสอบ is_string ($ item ["price"]) ได้หรือไม่? นั่นจะมีประสิทธิภาพมากกว่า === เล็กน้อย [ต้องการอ้างอิง]
Jimmie Lin

ในการเปรียบเทียบที่อ่อนแอระหว่างสตริงและจำนวนเต็มสตริงจะถูกแปลงเป็นจำนวนเต็ม (แทนที่จะเป็นจำนวนเต็มที่ "เลื่อนระดับ" เป็นสตริง) if((string)$item['price'] == 'e')แก้ไขพฤติกรรมแปลก ๆ ดูstackoverflow.com/a/48912540/1579327สำหรับรายละเอียดเพิ่มเติม
เปาโล

โปรดสังเกตอีกกรณีหนึ่งในความคิดเห็นด้านล่างโดย @Paolo โดยที่ 0 (จำนวนเต็ม) เท่ากับสตริงอื่น ๆ เมื่อใช้ตัวดำเนินการเท่ากับคู่
Haitham Sweilem

คำตอบ:


114

คุณกำลังทำ==สิ่งที่จัดประเภทให้คุณ

0เป็น int ดังนั้นในกรณีนี้มันจะส่ง'e'ไปยัง int ซึ่งไม่ได้เป็น parsable 0เป็นหนึ่งและจะกลายเป็น สตริง'0e'จะกลายเป็น0และตรงกัน!

ใช้ ===


14
ข้อเสียอีกประการหนึ่งของการเปรียบเทียบแบบหลวม ๆ
MC Emperor

5
ยุ่งยากอย่างหนึ่ง แค่ชนอันนี้ก็แปลกใจว่าทำไม string == 0 ต้องจำไว้
Grzegorz

2
เกาหัวของฉันกับอันนี้ด้วยเช่นกันเมื่อฉันวนลูปบนคีย์สตริง แต่อาร์เรย์มีรายการดัชนี 'ศูนย์' เริ่มต้นซึ่งทำให้ผลลัพธ์เป็นจริงในการเปรียบเทียบคีย์สตริงแรก ฉันชอบอะไร? ดังนั้นใน ... ดังนั้นคำตอบนี้ก็ชัดเจนขึ้นนั่นเอง! ฉันแปลกใจที่คำถามทั้งหมดนี้ไม่มีคำตอบใดที่ยอมรับ เพียงแค่ไปแสดงคำถามบางคำถามก็กระตุก
IncredibleHat

48

เนื่องจาก PHP ทำการเปรียบเทียบที่ตัวดำเนินการ==เปรียบเทียบหมายถึง:

หากคุณเปรียบเทียบตัวเลขกับสตริงหรือการเปรียบเทียบเกี่ยวข้องกับสตริงตัวเลขแต่ละสตริงจะถูกแปลงเป็นตัวเลขและการเปรียบเทียบจะดำเนินการเป็นตัวเลข […] การแปลงประเภทจะไม่เกิดขึ้นเมื่อมีการเปรียบเทียบ===หรือ!==เกี่ยวข้องกับการเปรียบเทียบประเภทและมูลค่า

เนื่องจากตัวถูกดำเนินการตัวแรกเป็นตัวเลข ( 0) และตัวที่สองคือสตริง ( 'e') สตริงจะถูกแปลงเป็นตัวเลขด้วย (ดูตารางการเปรียบเทียบกับประเภทต่างๆ ) หน้าคู่มือในประเภทข้อมูลสตริงกำหนดวิธีการแปลงสตริงเป็นตัวเลข :

เมื่อสตริงถูกประเมินในบริบทตัวเลขค่าและประเภทผลลัพธ์จะถูกกำหนดดังนี้

หากสตริงไม่มีอักขระ ' .' ' e' หรือ ' E' และค่าตัวเลขพอดีกับขีด จำกัด ประเภทจำนวนเต็ม (ตามที่กำหนดโดยPHP_INT_MAX) สตริงจะถูกประเมินเป็นจำนวนเต็ม ในกรณีอื่น ๆ ทั้งหมดจะถูกประเมินเป็นลอย

ในกรณีนี้สตริง'e'จะถูกประเมินเป็นค่าลอย:

ค่านี้กำหนดโดยส่วนเริ่มต้นของสตริง หากสตริงเริ่มต้นด้วยข้อมูลตัวเลขที่ถูกต้องนี่จะเป็นค่าที่ใช้ มิฉะนั้นค่าจะเป็น0(ศูนย์) ข้อมูลตัวเลขที่ถูกต้องคือเครื่องหมายที่เป็นทางเลือกตามด้วยตัวเลขตั้งแต่หนึ่งหลักขึ้นไป (มีจุดทศนิยมเป็นทางเลือก) ตามด้วยเลขชี้กำลัง เลขชี้กำลังคือ ' e' หรือ ' E' ตามด้วยหนึ่งหรือหลายหลัก

ในฐานะที่ไม่ได้เริ่มต้นด้วยข้อมูลที่เป็นตัวเลขที่ถูกต้องก็ประเมินลอย'e'0


3
php ออกแบบทุกอย่างให้เทียบเคียงได้ง่ายแล้วพ่น gotchas เพียงไม่กี่อย่างเพื่อทำลายวันของเรา สิ่งนี้ไม่สอดคล้องกับปรัชญาการออกแบบที่เหลือของ PHP เว้นแต่การหลอกลวงมีปรัชญา ???
user3338098

1
โดยเฉพาะอย่างยิ่งเมื่อ "e" ร่ายเป็นจริงและ "" เป็นเท็จ
user3338098

21
"ABC" == 0

ประเมินtrueเพราะครั้งแรกที่ "ABC"จะถูกแปลงเป็นจำนวนเต็มและกลายเป็น0 แล้ว0มันจะถูกเปรียบเทียบกับ

นี่คือแปลกพฤติกรรมของภาษา PHP: ปกติหนึ่งจะคาดหวัง0ที่จะได้รับการเลื่อนตำแหน่งให้สตริง"0"และเมื่อเทียบกับแล้วมีผล"ABC" falseบางทีนั่นอาจจะเป็นสิ่งที่เกิดขึ้นในภาษาอื่น ๆ เช่น JavaScript ที่เปรียบเทียบอ่อนแอประเมิน"ABC" == 0false

การเปรียบเทียบอย่างเข้มงวดจะช่วยแก้ปัญหาได้:

"ABC" === 0

falseประเมิน

แต่ถ้าฉันต้องเปรียบเทียบตัวเลขเป็นสตริงกับตัวเลขล่ะ?

"123" === 123

ประเมินfalseเนื่องจากคำศัพท์ซ้ายและขวาเป็นประเภทที่แตกต่างกัน

สิ่งที่จำเป็นจริงๆคือการเปรียบเทียบที่อ่อนแอโดยไม่มีข้อผิดพลาดของการเล่นกลประเภท PHP

วิธีแก้ปัญหาคือการโปรโมตเงื่อนไขเป็นสตริงอย่างชัดเจนจากนั้นทำการเปรียบเทียบ (เข้มงวดหรืออ่อนแอไม่สำคัญอีกต่อไป)

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

คือ

true

ในขณะที่

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

คือ

false


ใช้กับรหัสเดิม:

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

วิธีนี้ดีกว่ามาก! ขอบคุณ
Pooya Behravesh

9

ตัวดำเนินการ == จะพยายามจับคู่ค่าแม้ว่าจะเป็นประเภทต่างๆกันก็ตาม ตัวอย่างเช่น:

'0' == 0 will be true

หากคุณต้องการเปรียบเทียบประเภทด้วยให้ใช้ตัวดำเนินการ ===:

'0' === 0 will be false

9

ปัญหาของคุณคือตัวดำเนินการเท่ากับสองเท่าซึ่งจะพิมพ์สมาชิกทางขวาไปยังประเภททางซ้าย ใช้อย่างเข้มงวดหากคุณต้องการ

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

กลับไปที่รหัสของคุณ (คัดลอกด้านบน) ในกรณีนี้ในกรณีส่วนใหญ่ $ item ['price'] เป็นจำนวนเต็ม (ยกเว้นเมื่อมันเท่ากับ e ชัด ๆ ) เช่นตามกฎหมายของ PHP, PHP จะ typecast เป็นจำนวนเต็มซึ่งอัตราผลตอบแทน"e" int(0)(ไม่เชื่อฉัน? <?php $i="e"; echo (int)$i; ?>).

เพื่อหลีกเลี่ยงสิ่งนี้ให้ใช้ตัวดำเนินการสามเท่า (การเปรียบเทียบที่แน่นอน) ซึ่งจะตรวจสอบประเภทและจะไม่พิมพ์คาสต์โดยปริยาย

PS: สนุก PHP จริง: ไม่ได้หมายความว่าa == b b == aลองใช้ตัวอย่างของคุณแล้วย้อนกลับ: if ("e" == $item['price'])จะไม่มีทางเติมเต็มได้หาก $ item ['price'] เป็นจำนวนเต็มเสมอ


7

PHP มีวิธีการที่ค่อนข้างสะดวกในการตรวจสอบความถูกต้องของส่วนผสมของ "0", "false", "off" เป็น == false และ "1", "on", "true" as == true ซึ่งมักถูกมองข้าม มีประโยชน์อย่างยิ่งสำหรับการแยกวิเคราะห์อาร์กิวเมนต์ GET / POST:

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

ไม่เกี่ยวข้องทั้งหมดกับกรณีการใช้งานนี้ แต่ให้ความคล้ายคลึงกันและความจริงแล้วนี่คือผลการค้นหามีแนวโน้มที่จะพบเมื่อถามคำถามเกี่ยวกับการตรวจสอบความถูกต้อง (สตริง) "0" ว่าเป็นเท็จฉันคิดว่ามันจะช่วยคนอื่นได้

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


6

คุณควรใช้===แทน==เนื่องจากตัวดำเนินการธรรมดาไม่ได้เปรียบเทียบประเภท แต่จะพยายามพิมพ์แคสต์รายการแทน

ในขณะเดียวกัน===ต้องคำนึงถึงประเภทของรายการ

  • === หมายถึง "เท่ากับ"
  • == แปลว่า "eeeeh .. ดูเหมือนว่า"

1
ฉันเห็น. ตอนนี้ใช้งานได้แล้ว (ด้วยตัวพิมพ์):if((string)$item['price']=='e'){ $item['price'] = -1; }
Sérgio Domingues

แต่คุณไม่ควรทำเช่นนั้น เพียงแค่ใช้ตัว===ดำเนินการ
tereško

3

ฉันคิดว่าเป็นการดีที่สุดที่จะแสดงโดยตัวอย่างที่ฉันทำในขณะที่พบพฤติกรรมแปลก ๆ แบบเดียวกัน ดูกรณีทดสอบของฉันและหวังว่าจะช่วยให้คุณเข้าใจพฤติกรรมได้ดีขึ้น:

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

การทดสอบที่ยอดเยี่ยมฉันทำเหมือนกัน แต่ฉันสร้างตารางที่ดีจาก ดูคำตอบของฉัน
IAMTHEBEST

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.