40

I'm really confused as to why this operation works. Can someone explain it?

$test1 = "d85d1d81b25614a3504a3d5601a9cb2e";
$test2 = "3581169b064f71be1630b321d3ca318f";

if ($test1 == 0)
  echo "Test 1 is Equal!?";
if ($test2 == 0)
  echo "Test 2 is Equal!?";

// Returns: Test 1 is Equal!?

For clarification, I am trying to compare the string "0" to the $test variables. I already know to fix the code I can just enclose (as I should have) the 0 in ""s

I'm wondering if this is a PHP bug, a server bug, or somehow a valid operation. According to http://us3.php.net/types.comparisons this should not have worked.

Edit: Scratch that, apparently it does mention that Loose comparisons between string and 0 is true. But I still don't know why.

Edit 2: I've revised my question, why does the $test2 value of "3581169b064f71be1630b321d3ca318f" not work?

user2864740
  • 60,010
  • 15
  • 145
  • 220
St. John Johnson
  • 6,590
  • 7
  • 35
  • 56
  • which $test value doesn't work? – Assaf Lavie Mar 23 '09 at 02:27
  • If you run the code, it only outputs `Test 1 is Equal!?` – St. John Johnson Mar 23 '09 at 02:31
  • 2
    Edit 2 : because comparison statement automatically casts "3581169b064f71be1630b321d3ca318f" and as it starts with numbers its casted to int and gets the first part of string till a letter as value. and that comparison (3581169 == 0) to false – Erdinç Çorbacı Oct 27 '19 at 19:29
  • FWIW, *printing* the values of `$test1 + 0` and `$test2 + 0` would have given a clue as to what was happening. Or alternatively, the values of `+$test1` and `+$test2` (using the identity operator). – ToolmakerSteve Aug 13 '20 at 22:30

5 Answers5

44

From the PHP manual:

String conversion to numbers

When a string is evaluated in a numeric context, the resulting value and type are determined as follows.

The string will be evaluated as a float if it contains any of the characters '.', 'e', or 'E'. Otherwise, it will be evaluated as an integer.

The value is given by the initial portion of the string. If the string starts with valid numeric data, this will be the value used. Otherwise, the value will be 0 (zero). Valid numeric data is an optional sign, followed by one or more digits (optionally containing a decimal point), followed by an optional exponent. The exponent is an 'e' or 'E' followed by one or more digits.

Assaf Lavie
  • 73,079
  • 34
  • 148
  • 203
  • Still don't think it should convert to a 0, or at least if it starts with a number not to convert to a decimal if there are erroneous characters in the string. – St. John Johnson Mar 23 '09 at 03:16
  • 2
    PHP has no "NaN" type like Javascript (another loose-typed language) so it pretty much has to convert to 0 as there would be no other way (converting to NULL wouldn't work cos NULL also converts to 0). I think the answer is not to use a loose comparison (==), or to validate the string first. – thomasrutter Mar 23 '09 at 03:24
  • 1
    **In addition to this answer**, when comparing 2 elements with `==` operator , it is converted to common type. it is mentioned here. http://www.php.net/manual/en/language.operators.comparison.php – Nis Mar 25 '14 at 23:23
  • 1
    The best way to do the test is to use the "empty" method: `empty($test1)` – Pascal_dher Jan 21 '15 at 12:46
  • @Pascal_dher - ugh, no. Reason: *say what you mean*. Here, the intent is to ask the question *"Is this value equal to 0"*. An expression that looks similar to the intent is *obvious*, whereas an expression that relies on a quirk of the language, is *non-obvious*. IMHO. [If the only computer language that existed was PHP, then your suggestion would be reasonable. However, that still would not make it *the best* way; it would just make it an *equivalently good alternative*.] – ToolmakerSteve Aug 13 '20 at 22:33
  • Note that this has changed in PHP 8. It will now throw a `TypeError` if a non-numeric string is used in a numeric context. https://www.php.net/manual/en/language.types.numeric-strings.php – Barmar May 27 '21 at 23:52
12

Type conversion using the == operator

The == operator is a loosely-typed comparison. It will convert both to a common type and compare them. The way strings are converted to integers is explained here.

Note that the page you linked to doesn't contradict this. Check the second table, where it says that comparing the integer 0 to a string "php" using == shall be true.

What happens is that the string is converted to integer, and non-numeric strings (strings that do not contain or begin with a number) convert to 0.

Numeric vs non-numeric strings

A string that consists of a number, or begins with a number, is considered a numeric string. If the string has other characters after that number, these are ignored.

If a string starts with a character that cannot be interpreted as part of a number, then it is a non-numeric string and will convert to 0. This doesn't mean that a numeric string has to start with a digit (0-9) - for example "-1" is a numeric string because the minus sign is part of a number in that case.

So for example, your string "d85d1d81b25614a3504a3d5601a9cb2e" does not start with a number, so it would convert to 0. But your second string "3581169b064f71be1630b321d3ca318f" would be converted to integer 3581169. So that's why your second test does not work the same way.

What you should do

You probably want:

if ($test1 === "0")

Notice the use of triple equals instead of a double equals. This ensures that what you are comparing is a string that contains the digit zero only, and prevents any type conversion.

thomasrutter
  • 114,488
  • 30
  • 148
  • 167
8

After some investigation, it turns out aidan from the PHP manual mentioned that any strings that do not start with a number will be converted to 0 when casted as an integer.

This means that:

("php" == 0) === true
("1php" == 0) === false

Very annoying and not well documented. It was at the bottom of the comments on the type comparison page.

St. John Johnson
  • 6,590
  • 7
  • 35
  • 56
  • 1
    Yeah, figured that was it. Poor documentation indeed. – Paolo Bergantino Mar 23 '09 at 02:23
  • I don't think it's poor documentation at all. Implicit type conversion is a fundamental of PHP which is explained very well in the manual in the section on Types, particularly the Type Juggling section. == is a loosely typed comparison; using === instead can help prevent confusing situations. – thomasrutter Mar 23 '09 at 02:32
  • See also "String Conversion to Numbers" at http://us3.php.net/manual/en/language.types.string.php#language.types.string.conversion – thomasrutter Mar 23 '09 at 02:33
2
$test1 = "d85d1d81b25614a3504a3d5601a9cb2e";

this string starts with a "d", which is not valid number, the var will resolve to 0 and your test#1 will pass.

$test2 = "3581169b064f71be1630b321d3ca318f";

this string starts with 3581169 which is a valid number, so the var will resolve to that value which is not equal to 0. So your test#2 will not pass.

HBR
  • 901
  • 1
  • 8
  • 15
1

I've been using some conversions and comparisons to test if a numeric string is a number:

$test1="19de6a91d2ca9d91721d82f1bd8102b6";
   echo (float)$test1==$test1; //TRUE
   echo is_float($test1); //FALSE

   //Converting the string to float and then converting it to string and compare will do the trick 
   echo (string)((float)$test1)==(string)$test1; //FALSE


$test2="5.66";
   echo (float)$test2==$test2; //TRUE

   //Testing the numeric string using `is_float` wont give the expected result
   echo is_float($test2); //FALSE

   echo (string)((float)$test2)==(string)$test2; //TRUE
Eiad Samman
  • 397
  • 4
  • 15