PHP Sadness

Type-coercing comparison operators will convert numeric strings to numbers

This issue has a PHP bug database entry and is even cited there!

According to php language.operators.comparison, the type-coercing comparison operators will coerce both operands to floats if they both look like numbers, even if they are both already strings:

If you compare a number with a string or the comparison involves numerical strings, then each string is converted to a number and the comparison performed numerically.

Of course, "numerical strings" can mean a lot of different things.

$ php -r 'var_dump("1e3" == "1000");'
bool(true)

As of PHP 5.4.4, a change was made which causes two numeric strings which both overflow and appear equal numerically to be compared as strings instead. Before this change, even these such strings were compared numerically.

This can become especially important in situations where the developer chooses to use == to compare two values which will always be strings. For example, consider a simple password checker:

if (md5($password) == $hash) {
  print "Allowed!\n";
}

Assume that the $hash is loaded from a known safe string value from a database and contains a real MD5 hash. Now, suppose the $password is "ximaz", which has an all-numeric hex-encoded MD5 hash of "61529519452809720693702583126814". When PHP does the comparison, it will print "Allowed!" for any password which matches even the first half of the hash:

$ php -r 'var_dump("61529519452809720693702583126814" == "61529519452809720000000000000000");'
bool(true)

The solution, of course, is "never use type-coercing comparison operators" - but this remains an easily-overlooked bug factory for beginning and even intermediate developers. Some languages solve this situation by having two separate sets of comparison operators for numeric or string comparisons so that the developer can be explicit in their intent without needing to manually cast their arguments.