php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #39579 Comparing zero & string values in boolean comparison has unexpected behaviour
Submitted: 2006-11-22 07:31 UTC Modified: 2021-04-15 01:52 UTC
From: iain at workingsoftware dot com dot au Assigned:
Status: Not a bug Package: Variables related
PHP Version: 5.2.0 OS: FreeBSD 6.1
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: iain at workingsoftware dot com dot au
New email:
PHP Version: OS:

 

 [2006-11-22 07:31 UTC] iain at workingsoftware dot com dot au
Description:
------------
if you have a variable with the value 0 assigned to it and do a comparison with a non-integer then the non-integer value is cast to an int with unexpected results. it behaves differently when comparing a non-zero integer.

Reproduce code:
---------------
put this in test.php and run php -f test.php:

<?php
$zero = 0;
$one = 1;

if($zero == 'SOME STRING')
    echo("0 does equal 'SOME STRING'\n");
else
    echo("0 does not equal 'SOME STRING'\n");

if($one == 'SOME STRING')
    echo("1 does equal 'SOME STRING'\n");
else
    echo("1 does not equal 'SOME STRING'\n");
?>

Expected result:
----------------
0 does not equal 'SOME STRING'
1 does not equal 'SOME STRING'

Actual result:
--------------
0 does equal 'SOME STRING'
1 does not equal 'SOME STRING'

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2006-11-22 08:17 UTC] derick@php.net
Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

.
 [2006-11-22 08:35 UTC] iain at workingsoftware dot com dot au
well it seems to me that at least a warning should be given when comparing an integer 0 to a non-integer value that evaluates to 0 when cast as an int unless an explicit type cast is used without using strict equals.

i can see why this behaviour is not a bug, because (int)'SOME STRING' == 0, but it's also has the potential to cause unexpected results/bugs unless you use strict equals everywhere.

in this case, i fixed the problem by using === instead of ==, but it took a bit of time to track down the error because there was no warning or anything. i think that it would be better to require someone to do:

if($value == (int)'SOME STRING')

OR

if($value === 'SOME STRING')

in order to avoid a warning being emmited if $value == 0.
 [2006-11-22 09:39 UTC] johannes@php.net
Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

That's how PHP works and it won't change.
 [2006-11-22 11:06 UTC] mgf@php.net
And, besides, this behaviour is documented at
http://www.php.net/manual/en/language.types.string.php#language.types.string.conversion
 [2006-11-22 11:14 UTC] iain at workingsoftware dot com dot au
it's not the behaviour of how a string is cast to an integer that i'm talking about, but if i have a comparison:

if($value == Class::CONSTANT)

and the class constant is a string, it's not immediately apparent that if $value == 0 then this will evaluate to true. maybe warning is too strong, but a notice might be good in the event that a non-strict == operation returns true because one of the operands is 0 and the other operand is a value that evaluates to 0 when cast as an int.

anyway, i guess if php has been around for this long without anyone mentioning it yet ... i mean, this is the first time i've come across the problem. if a notice had been emitted it would have been a time saver.
 [2006-11-22 11:36 UTC] mgf@php.net
It's not a problem -- it's a feature, and it's documented at the address I've just quoted, which describes evaluation of a string in any numeric context.
 [2012-08-01 10:31 UTC] v dot picture at free dot fr
Hi, I'm wondering why this comparison should be evaluated in a numeric context and not a string context, after all 
there is no loss with string casting whereas there is a huge risk of doing mistakes with numeric casting:
(string) 0 => "0"
(string) 42.5 => "42.5"
(int) "test" => 0
But ok, let's say it's a normal behavior.

"If you compare a number with a string or the comparison involves numerical strings, then each string is converted to 
a number"

Then why would PHP decide to do that in a string context ? I mean, when I compare two strings I don't expect PHP to 
convert everything to numbers !
"10" == "1e1" => true
Sorry folks, this really seems like a string context to me.
 [2013-02-14 17:14 UTC] radamanf at gmail dot com
This is a epic FAIL in pure logic of comparing formats not having explicitly defining them, whole beauty of PHP disappear! This is BROKEN LOGIC.

Connected BUGS
Bug #44990
Bug #39579

Different people are coming across this BUG and thinking the same as me, so please CHANGE your documentation, it's WRONG! Who is this "GENIES" to make possible converting String to Integer NOT INT TO STRING!

Guys, I'm very disappointed! :)
 [2013-02-14 17:24 UTC] nikic@php.net
@radamanf: Stop shouting. Shouting does not help.

I agree that this behavior ('foo' == 0) is counter-productive. I think most people would agree on that. But even if everyone agrees that it's the wrong behavior, changing it isn't so easy. Changing this behavior will probably break existing software.

If you really want to change this, then there is only one way: Make the change and then test a shitload of code against it. See how many tests will fail in major PHP projects and how easy things are to fix.

If you can provide sufficient data that this change (which goes rather deep into the core semantics of the language) won't affect existing projects heavily, then I see no problem with doing it.
 [2013-02-14 17:26 UTC] radamanf at gmail dot com
I've seen a topic in the web: http://josephscott.org/archives/2012/03/why-php-strings-equal-zero/ related to this bug, and I can see that sometimes string need to be compared with integer as integer BUT this is a huge pool of potential bugs across all the worldwide! :) Can you imagine how many people usign PHP without realizing this BUG ! :)_) I'm sure this is a loved BUG by most hackers out there !
 [2013-02-14 22:50 UTC] iain at workingsoftware dot com dot au
Hi, I agree this shouldn't be changed. It's pretty fundamental to how PHP works 
and since reporting this bug 6 
years ago I've learned a lot more about PHP :)

I still think my suggestion of emitting a Notice whenever a string is converted 
to 0 as a result of being 
evaluated in a numeric context isn't too outlandish though.

*Changing* the way that strings are evaluated would break a buttload of code and 
isn't really productive.

Emitting a Notice would help people unfamiliar with the implicit typecasting 
behaviour save some time and 
avoid bugs when developing. For those that like to write "clean code" with no 
Notices there is a very simple 
way of suppressing it (ie. use === or (int)).

Also, emitting a notice wouldn't actually break any code, although it might 
cause a number of scripts out in 
the wild to become more verbose in their logging - is that not an acceptable 
risk?

The notice could even say something like:

Notice: String evaluated to 0 (zero) when used in numeric context on line 
WHATEVER. Use === or (int) to 
prevent this notice.

This would enable people to quickly understand the behaviour (which is kind of 
unintuitive but fair enough 
when you look at PHPs type system - just a foible of the language everyone 
should learn) and encourage people 
to learn the importance of === earlier on.
 [2013-02-14 22:52 UTC] iain at workingsoftware dot com dot au
-Type: Bug +Type: Feature/Change Request
 [2013-02-14 22:52 UTC] iain at workingsoftware dot com dot au
Sorry I meant to submit this as "Feature/Change Request" but submitted the form 
without a password and it changed back to "bug".
 [2013-02-15 10:08 UTC] radamanf at gmail dot com
Hi Nick, Thanks for your answers on both related bugs.
I apologize for my expressions.
But I'm still thinking this bug should be fixed one day, maybe on later day when a few backwards compatible bugs will be found, so it makes more sense to fix it as a bunch. I hear that PHP 5.3 was breaking release and some old code was not working since, it was the right time to fix this bug.

The general logic should be: Convert less complicated variable type into more complicated type, so convert Int up to String not bringing String down to Int. This need to be universal and according to KISS logic. If geeks need to use strings for octal or hexadecimal then they know what they are doing and need to cast such string as Int! To bring string down to Int.

v dot picture at free dot fr ->
"
Then why would PHP decide to do that in a string context ? I mean, when I compare two strings I don't expect PHP to 
convert everything to numbers !
"10" == "1e1" => true
Sorry folks, this really seems like a string context to me.
"

Someone need to write a great exploit to hack into eCommerce shops and produce unseen damage across the Globe :) then this will not be funny at all.

I wish this patch could happen one day! Temporarily it could be at least a Warning, Iain suggested to give a notice, but I believe this bug got a great hidden potential.
 [2013-07-02 21:31 UTC] xtalviper at yahoo dot com
Here's the basic problem of logic that doesn't follow for me:

If I do

  if(0) {
    //...is false, this will never run
  }

and I do

  if("string") {
    //...is true, will always run
  }

Then how the hell can true == false
 [2013-07-03 00:36 UTC] iain at workingsoftware dot com dot au
Hey xtalviper, the reason it does this is because in the case of:

if($string)

then the string is cast to a boolean. If you do:

if($int == $string)

then the string is cast to an int.

You can see this more clearly by doing this:

<?php
    $boolstring = (boolean)"string";
    $intstring  = (int)"string";
    var_dump($boolstring);
    var_dump($intstring);
 [2013-10-22 18:42 UTC] mirroredfate at gmail dot com
> It's not a problem -- it's a feature, and it's documented at the address I've just quoted

And I could document a webserver sometimes crashing when you click a button and call it a "feature", but that doesn't make it so.

This is clearly counter-intuitive behavior and should be beaten to death with an iron rod after being water-boarded. It is terrible. If you are defending this, you should probably take a long, hard look at your life and your principles.

Making zero equal any string is just wrong. I completely goes against how PHP operates in every other instance, and no doubt causes developers no end of distress and frustration. It's a feature like how getting beaten up in a dark alley is a feature of that alley.

This needs to be fixed, people.
 [2013-10-22 20:45 UTC] iain at workingsoftware dot com dot au
Hi mirroredfate at gmail dot com, I really don't think this should be changed. Take a look at my note above about why this happens, it's because in one case the string is cast as boolean, and in another case cast as an int.

Changing this would require either:

a) Changing the type that you cast a variable to when you do if($var) to something other than boolean or changing the way that types are cast when comparing a string and an int to each other OR;

b) Changing the value that a string is cast to when cast as either a boolean or an int, to be consistent

Both of these changes would wreak unspeakable havoc on existing applications, and probably wouldn't make any tremendous difference overall to the uninitiated.

HOWEVER, I do think that it would be acceptable to emit a NOTICE when a string is implicitly cast to (int) during a comparison using == and suggesting they use an explicit type cast or strict equals to suppress the notice (see my comments above).
 [2013-10-22 21:33 UTC] v dot picture at free dot fr
*Sighh* Do I have to open a separate bug report ?
Everybody seems to ignore the comment I made about a year ago about the fact that:

((string) "10" == (string) "1e1") => true

So okay, ("test" == 0) => true, let's pretend it's a feature (and insult the intelligence of about every PHP developer) but there is no way that a comparison between two strings could end up being evaluated as numbers !
There is clearly something very wrong with that !
 [2013-10-22 21:42 UTC] mirroredfate at gmail dot com
There is another option: when a string casting to an int fails inside a conditional statement, the conditional evaluates to false.

>Both of these changes would wreak unspeakable havoc on existing applications, and probably wouldn't make any tremendous difference overall to the uninitiated.

Really? I am genuinely curious as to why anyone would actually use '(0 == 'string') to determine if a string could not be cast to an int. This seems kind of like a "rip the band-aid off" situation to me. Let people know a few patches ahead of time, then go ahead  with the change. After all, PHP had no problem removing JSON.

The fact that it could cause problems is one of the biggest reasons why one should not design stupid, counter-intuitive stuff the first place.
 [2013-11-07 23:18 UTC] duerra at yahoo dot com
I'm sorry, but this is just silly.  This has to be one of PHP's most glaring weaknesses.  While it can be understood, this does NOT make it logical.  In fact, unless you have a very firm understanding of both how PHP casts strings to integers, and always explicitly know or cast your types (which PHP's design largely tries to avoid having users worry about), this becomes an issue that can hit somebody out of nowhere, unexpectedly, and leave them wracking their brains trying to sort out.

root@host:/bright/Bright# php -r "echo (0 == 'string') ? 'TRUE' : 'FALSE';"
TRUE
root@host:/bright/Bright# php -r "echo (0 == '1string') ? 'TRUE' : 'FALSE';"
FALSE 
root@host:/bright/Bright# php -r "echo ('0' == 'string1') ? 'TRUE' : 'FALSE';"
FALSE
root@host:/bright/Bright# php -r "echo (1 == 'string') ? 'TRUE' : 'FALSE';"
FALSE  
root@host:/bright/Bright# php -r "echo (1 == '1string') ? 'TRUE' : 'FALSE';"
TRUE   
root@host:/bright/Bright# php -r "echo (1 == 'string1') ? 'TRUE' : 'FALSE';"
FALSE  

I realize and understand that this may never change, but that doesn't make it logical or correct, and in the meantime you're probably allowing more bugs in the wild than you would cause by actually fixing the issue or using one of the other suggestions from this thread (such as throwing a Notice).
 [2014-05-27 15:41 UTC] tom at r dot je
I have to agree with the above, this is illogical and confusing. What just caught me out was an internal conversion from string to int which triggered the bug (and yes I definitely consider this a bug.) Try this:

<?php
$arr = array('0' => 'Abc');

$keys = array_keys($arr);

if ($keys[0] == 'foo') {
	echo 'String\'s equal';
}
?>
 [2014-05-28 13:25 UTC] tom at r dot je
To expand on my point above:

0 == '0' //TRUE

Here, 0 and '0' are equivalent. However:

 0 == 'a'; //TRUE
'0' == 'a'; //FALSE

Here, 0 and '0' are not equivalent which is very inconsistent. 

If 0 is equivalent to '0' and 0 is equivalent to 'a' than it logically follows that '0' is equivalent to 'a'; which is clearly not the case, and nor should it be to fix this logical contradiction the only option is to make 0 and 'a' not equivalent. 


As for the problems presented above about actually implementing a change, why not do what's been done before and add an INI setting (settable via ini_set). Any scripts which do rely on this broken functionality can just call ini_set for compatibility with the old operator.
 [2014-05-28 13:53 UTC] rasmus@php.net
The transitive property of equality doesn't apply to a type-coercing equality check because the types matter in each comparison. So you can't say that since
0 == '0' and 0 == 'a' then '0' must be equivalent to 'a'. In the first two comparisons you are comparing integers to strings and in the last one you are comparing two strings. These are completely different situations for a type-coercing equality operator like ==
If you don't want to juggle types, simply use ===
 [2020-06-19 16:24 UTC] amfriedman at gmail dot com
This "feature" just burned an hour of my time as I wracked my brain trying figure out why a continue; statement in my foreach loop was being triggered. I had an value of (int) 0 in one of the array items, and it passed an if-test that compared it to a particular string. Super frustrating.

I think PHP should generate a Notice to give the developer a head's up about this "gotcha".
 [2021-04-14 20:48 UTC] ehsan at chavoshi dot no--spam dot com
This behavior has the potential to cause many logical errors.At the same time, it is not easy to change it, and changing it also causes logical errors in existing programs. 
My strong suggestion is to throw a E_NOTICE when the result of the comparison would have been different if the typecasting had not been done. (or anytime typecasting occurred when comparing two values) .
showing a safe NOTICE attracts the programmer's attention and at the same time does not cause any problems in running programs.
I wonder why this problem has not been solved so far.
 [2021-04-14 20:53 UTC] nikic@php.net
> I wonder why this problem has not been solved so far.

It so happens that this problem *has* been solved, in PHP 8.0. Please see https://wiki.php.net/rfc/string_to_number_comparison for more information.
 [2021-04-15 01:52 UTC] iain at workingsoftware dot com dot au
When I posted this as a bug initially I was about to turn 26.

5 months ago I turned 40.

Thanks for the follow up Niki, long live PHP!
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 23:01:29 2024 UTC