Exhibit 1a

("false" == 0) returns true

No fucking joke. What a pile of turd.

It gets worse though.

  1. ("false" == 0) returns true.
  2. (false == 0) returns true.
  3. ("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:

  1. array_change_key_case
  2. array_chunk
  3. array_combine
  4. array_count_values
  5. array_diff_assoc
  6. array_diff_key
  7. array_diff_uassoc
  8. array_diff_ukey
  9. array_diff
  10. array_fill_ keys
  11. array_fill
  12. array_filter
  13. array_flip
  14. array_intersect_assoc
  15. array_intersect_key
  16. array_intersect_uassoc
  17. array_intersect_ukey
  18. array_intersect
  19. array_key_exists
  20. array_keys
  21. array_map
  22. array_merge_recursive
  23. array_merge
  24. array_multisort
  25. array_pad
  26. array_pop
  27. array_product
  28. array_push
  29. array_rand
  30. array_reduce
  31. array_replace_recursive
  32. array_replace
  33. array_reverse
  34. array_search
  35. array_shift
  36. array_slice
  37. array_splice
  38. array_sum
  39. array_udiff_assoc
  40. array_udiff_uassoc
  41. array_udiff
  42. array_uintersect_assoc
  43. array_uintersect_uassoc
  44. array_uintersect
  45. array_unique
  46. array_unshift
  47. array_values
  48. array_walk_recursive
  49. array_walk
  50. array
  51. arsort
  52. asort
  53. compact
  54. count
  55. current
  56. each
  57. end
  58. extract
  59. in_array
  60. key
  61. krsort
  62. ksort
  63. list
  64. natcasesort
  65. natsort
  66. next
  67. pos
  68. prev
  69. range
  70. reset
  71. rsort
  72. shuffle
  73. sizeof
  74. sort
  75. uasort
  76. uksort
  77. 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.ArbitraryBlob (and then we'd need AbstractArbitraryBlobFactory and a few dozen interfaces). It'll make everyone's life so much easier.

Exhibit 5

create_function. Seriously. It is quite possibly the most horrible attempt to recreate anonymous functions. Firstly, they weren't anonymous - you had to assign them to a variable to use them. Like so:

$foo = create_function('$x', 'echo($x);'); $foo(5);

Rather than:

function($x) { ... }(5);

And here it gets even better - they don't get garbage collected after use. If you are using a nice language, they'll get properly cleared up after use, right? An anonymous function probably ought to be cleared up after use - once it has, say, mapped all the members of the array or reduced it or whatever it is doing. No such luck in the old PHP. create_function lets people "do FP" without hurting their brain too much and while creating memory leaks in the process. Win win.

If there were a PHP sanity checker, it would check first for create_function calls. Then it'd move on to use of the likeness operator.

What is PHP actually doing when you call create_function? It is basically eval'ing a function definition. Classy, eh? So, what if you want to define a function but not actually do anything with it? You know, just keep it lying around in an array or something for safe-keeping. In Ruby, that's pretty easy:

[proc {|i| i + 5 }, proc {|i| i + 6 }]

In PHP, you have to basically store a struct:

array(array("name" => "plusFive", "args" => "$x", "impl" => "return $x + 5;"));

And then iterate through the array pulling out the relevant bits and running create_function (basically eval) on them. This isn't really an improvement over just storing the string "function whatever($x) { return $x + 5; }" in an array, and then eval-ing that.

Specal bonus - Stockholm syndrome link: My new best friend: PHP's create_function. Someone really likes their create_function...

You want to see something even more painful? Currying in PHP. Compare with Function Currying in Scala. It's really painfully awful.

Exhibit 6

The scoping rules are painful.

Good christ, I've got PHP 5.3.1 on my machine. This is supposed to be up-to-date and sexy and free of all the ugly sins of the past. Enjoy, then:

foreach(array('php', 'pisses', 'me', 'off') as $i) { echo $i; }

Iterates through the array. That's fine, right? Lack of lexical scoping means that when I call:

echo $i;

It'll re-print the last value $i was assigned to. Damnit, that's a foreach loop. It shouldn't do that.

What does a real programming language do?

Ruby:

NameError: undefined local variable or method `i' for main:Object

Python:

NameError: name 'i' is not defined

Scala:

error: not found: value i

Now, where might a little bit of flexibility in scoping rules be useful? I know: defining anonymous functions. That way you can do something the functional crowd do quite often. I've written code like this before (in a kind of weird half-Ruby, half-Scala pseudocode):

def whatever(arg1, arg2) { if arg1.whatever { return ((arg3) => arg2 * arg3) } else { return ((arg3) => arg2 * 2 * arg3) }}

You call it and it'll return one of the two anonymous functions. Now, for this to work, the inner anonymous functions need to be able to access arg2 of the whatever function. Does that work in PHP? Of course not. About the only way to do it is, well, too disgusting to think about. It's --BLEEP--. No, that's not allowed. It'll damage the ears of sensitive children.

What's the plan then? Oh, they are going to add a new keyword called lexical to PHP. Or at least that was the plan in 2007. You can use 'lexical' like 'global' - global pulls in the variable from the global context while lexical pulls it from the parent context. Or something like that. Just reading the idea made me want to gouge my own eyes out in despair.

PHPs scoping rules: protecting you from actually doing anything useful with the FP functionality while leaking out of loops.

Exhibit 7

Namespaces use the backslash character. Yeah. I know. As someone who uses Ruby, I'm not really in a position to comment - what with Ruby's complete lack of namespaces. But still. Backslashes.

The 1980s is calling and wants its DOS install floppies back.

Powered by WiGit