String? No. This looks like a number. It must be one.
codepad.org/NMc1idYXPosted Apr 17, 2014, 11:47 AM UTC
Posted Apr 17, 2014, 11:47 AM UTC
This pisses me off endlessly as silent conversions of strings into number is working against any use of the === operator.
In this particular case, it's impossible to have a string that looks like a number as an array key. If it looks like a number, it gets converted and thus breaks any uses of the === operator later on.
Take a string, add it to an array as a key, take that key, compare with the original string. Won't pass ===
well...... there is a way...
php -r '$x = new stdclass; $x->{"123"} = 2; $x = (array)$x; list($x) = array_keys($x); var_dump($x);'
Why would you kill kittens like that?
Oh man, this behaviour leads to an entire class of bugs that you will never see in development, but will only appear in production.
PHP's insistence on coercing strings to integers without telling you particularly gets you when you're writing code that deals with hashes (as in MD5s or SHAs). Strings that are almost always hexadecimal, but occasionally are decimal.
Use those strings as array keys and your keys will almost always be strings, but occasionally (when you have the rare hash that has no letters) you will end up with an integer key. Causing some comparison somewhere in your code to fail when it shouldn't.
Even more maddening is that this behaviour depends not only on the length of the number but also on the architecture of the machine it's running on. For real.
Take a look. Here's some code on codepad (presumably running a 32-bit version of PHP): http://codepad.org/pwEC68w8
Here's the result:
Starting with the string 11111111
Are they equal using ==: yes
Are they equal using ===: no
Starting with the string 11111111111111
Are they equal using ==: yes
Are they equal using ===: yes
Starting with the string 111111111111111111111111111111111111
Are they equal using ==: yes
Are they equal using ===: yes
Running the same code on php repl (which is running a 64-bit version) results in this:
Starting with the string 11111111
Are they equal using ==: yes
Are they equal using ===: no
Starting with the string 11111111111111
Are they equal using ==: yes
Are they equal using ===: no
Starting with the string 111111111111111111111111111111111111
Are they equal using ==: yes
Are they equal using ===: yes
Interesting. I knew in_array used loose comparison but I wasn't aware arrays had an inherent string->int key conversion built in.
some day they'll make hash() "a language construct" and all the Perl people will look at them and say "You're all fucking retards."
all the Perl people will look at them and say "You're all fucking retards."
implying that isn't already happening
Well, as a Perl dude working PHP full-time, it's more the part where they make the array syntax "array()". And then give in and make it "[]" while doing silly things like $hash = ["foo"=>"bar"]; because "reasons".
It's really not. That's like a pentathlete kicking a quadriplegic.
Oh, it's even sadder than that. Try to use a float as a key? Silently cast to integer and truncated. That might make sense if arrays were purely numerically indexed, but they're really hash maps. If you can hash a string why not hash a float? Ruby, Perl, and Python have no problem with this.
And similarly null gets silently converted to the empty string when used as a key, and booleans get silently converted to integers. There's no consistency at all.
JavaScript converts every key of every object automatically into strings. But at least it does it consistently.
es6 or es7 will special hash types that will allow keys to be objects I believe. Or it may be in draft mode.
You should never compare a float for equality directly, without epsilon.
Therefore it's sensible to forbid float keys in a hash.
But PHP doesn't forbid float keys in a hash, it silently corrupts their values instead.
It's perfectly fine to compare floating point values for equality when you're working with exactly representable values.
And as I said, none of the other major dynamic scripting languages seem to have found it necessary to forbid floating point values as hash keys.
Edit: also, hashes are necessarily always used for lookups. You might want to use them for things like removing duplicates or other set-like operations.
And when you are working just with exactly representable values? One example might be computing powers of 2, but in practice, there are no such applications.
You have "exact" values every time you can guarantee that the values underwent the same set of computations in the same order, on the same hardware and with the same optimizations. Three of these points are static within the life time of a single program and the optimization only changes for some forms of just in time compiled code.
As a result you can use the values produced by floating point math with equality checks with a lot more than just power of 2 and using these values in a hash map is a perfectly fine thing to do.
Just off the top of my head:
...and so on. All of the above are exactly representable in binary floating point, and there is no possibility of roundoff error.