Exhibit 1a
("false" == 0) returns true
No fucking joke. What a pile of turd.
It gets worse though.
- ("false" == 0) returns true.
- (false == 0) returns true.
- ("false" == false) returns false.
Identity in PHP is non-transitive. A == B, B == C, but A != C. Utter logic fail.
It's just not the case that false and "false" are the same. One is a bool and the other is a string. Technically, '==' in PHP means "equals to" - to check identity, you need to use the triple equals "identical to". This is so silly. Python and Ruby don't require this kind of thing. Another way around this is to explicitly cast all the operands in a PHP 'equal to'.
This can lead to confusion for newbies. ("foobar" == 0) and ("1" == 0) return different values. One has to explicitly remember that "0", "1", "true" and "false" have special values.
There's no reason why '==' should mean anything other than identity. In Ruby, there's a nice principle applied - the principle of least surprise. That '==' has a strange and fuzzy concept type-specific equality rather than a strict identity comparison is surprising. Read PHP types comparsion and tell me that loose comparison isn't totally surprising and baffling.
Why is this important? Some people see this and tell me "oh, well, it's just one of PHP's many lovable quirks. Just use triple-equals". That'd be fine if PHP weren't the language picked so often by beginners. PHP has taught a wide swathe of people that "==" means something other than it means in all the other languages they might use in the future.
"It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration." (Dijkstra)
s/BASIC/PHP/g; Christ on a fucking wheel, I hate PHP likeness operator.
Exhibit 1b: Likeness Harder, With a Typecast Vengeance
If you think the likeness operator sucks - if you don't, go compete in the Darwin Awards - you just gotta see the version with explicit type casting:
((string) "false" == (int) 0)
What does that return? TRUE. I'm not fucking kidding. You'd think explicitly casting the literals might tip off the interpreter that I care about the types. Yes, yes, it's doing what it should be doing according to the likeness documentation. It's rigidly following a moronic rule.
Exhibit 2
Goto. Apparently, it's for low-level programming. Programming that is low-level enough to require a goto (hint: you don't need it for your blog. I promise.) but not quite low level enough to write it as a C extension. That makes sense.
Do note that the PHP guys added a new language literal for this - "procedurename: { /* code */ }". Remember that when closures or $yourFavouriteLanguageFeature gets rejected because it would clutter up the syntax.
Exhibit 3
The functions of the standard library are a mess. They manage to include both camelCase, underscore_separation and C-style function names like strpos. Instead of proper namespacing and OO, they simply use prefixing as a pseudo-namespace, which is pretty horrible because it means every time you type a function name you have to type prefix_function_name(whatever).
PHP 5 should really have deprecated the array handling which is a crazy mess of badly named functions.
Here is the list of functions for handling arrays in PHP:
- array_change_key_case
- array_chunk
- array_combine
- array_count_values
- array_diff_assoc
- array_diff_key
- array_diff_uassoc
- array_diff_ukey
- array_diff
- array_fill_ keys
- array_fill
- array_filter
- array_flip
- array_intersect_assoc
- array_intersect_key
- array_intersect_uassoc
- array_intersect_ukey
- array_intersect
- array_key_exists
- array_keys
- array_map
- array_merge_recursive
- array_merge
- array_multisort
- array_pad
- array_pop
- array_product
- array_push
- array_rand
- array_reduce
- array_replace_recursive
- array_replace
- array_reverse
- array_search
- array_shift
- array_slice
- array_splice
- array_sum
- array_udiff_assoc
- array_udiff_uassoc
- array_udiff
- array_uintersect_assoc
- array_uintersect_uassoc
- array_uintersect
- array_unique
- array_unshift
- array_values
- array_walk_recursive
- array_walk
- array
- arsort
- asort
- compact
- count
- current
- each
- end
- extract
- in_array
- key
- krsort
- ksort
- list
- natcasesort
- natsort
- next
- pos
- prev
- range
- reset
- rsort
- shuffle
- sizeof
- sort
- uasort
- uksort
- usort
Compare PHP:
$array = array(1, 2, 3, 4, 5);$array = array_reverse($array);echo implode(" ", $array);
Ruby:
array = [1, 2, 3, 4, 5]array.reverse!puts array.join(' ')
That seems okay, but if you boil that down to one-liners, the crapness of PHP shines through:
echo implode(" ", array_reverse(array(1, 2, 3, 4, 5)));vs.puts [1, 2, 3, 4, 5].reverse.join(' ')
For the human reading this, the latter (Ruby) code, except for the puts at the beginning (I'm tempted to monkeypatch .puts in as a method on Object that would just run puts on .to_s), is readable left-to-right. First, you get the array, then you get the reversal, then you get the join. With PHP, it's the other way around. You get the implode function (which is a stupid name btw: you aren't making the array implode, you are joining the members of the array into one object, a string), then you get the reversal of the array, the finally you get the array. If you are coding in Ruby and you decide you don't want to reverse the array, you simply delete ".reverse" from the code, while in PHP, you have to remove the "array_reverse" and the two brackets. If you've got 500 lines of this crap, you can easily write a regex that removes every ".reverse" method in the code, but doing similarly for removing nested function calls is a pain in the arse.
Here is a radical idea: in PHP6, get rid of all this shit. Replace it with proper classes and objects. Maybe not everything - at the most, leave numbers and basic maths operators (use class methods for the rest, as per Java). Whatever. There is no reason arrays, strings or whatever needn't be objects. Just do it and tell everyone to get fucked if they don't like it.
Exhibit 4
PHP's array is both a list and a hash at the same time. Let's say you are writing some library code. If you were doing it in Java, you'd be explicit about your methods and say whether or not they were expecting, say, a String[] or a HashMap or a List or whatever. In PHP, you put in your docs that the method or function takes an array. But if an array can be both a list and a hash, what should you process?
Let's say you are expecting a list and you pass it a hash - sorry, an associative array - then you might just iterate on value. But flip it around: if you are expecting both a key and a value, and someone passes you an array containing only values, then what?
The whole thing is such a mess. It's much simpler just to say a list is a list and a hash is a hash and if you need anything more complicated than that, define your own class or struct for it to go into. Christ, perhaps I should write a proposal to the JCP to replace all the java.util.List subclasses with just java.util.Blob (and then we'd need AbstractArbitraryBlobFactory and a few interfaces). It'll make everyone's life so much easier.