Home | History
== 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 [http://www.artima.com/intv/ruby4.html principle of least surprise]. That '==' has a strange and fuzzy concept type-specific equality rather than a strict identity comparison is surprising. Read [http://uk.php.net/manual/en/types.comparisons.php 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 == [http://php.net/goto 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.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: [http://debuggable.com/posts/my-new-best-friend-phps-create_function:480f4dd6-b9b0-47e3-b3e5-4c57cbdd56cb My new best friend: PHP's create_function]. Someone really likes their create_function... You want to see something even more painful? [http://zaemis.blogspot.com/2009/06/currying-in-php.html Currying in PHP]. Compare with [http://www.codecommit.com/blog/scala/function-currying-in-scala 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 [http://www.sitepoint.com/blogs/2007/12/23/lexical-scope-to-appear-in-php/ 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.
((string) "false" == (int) 0)
$array = array(1, 2, 3, 4, 5);
$array = array_reverse($array);
echo implode(" ", $array);
array = [1, 2, 3, 4, 5]
array.reverse!
puts array.join(' ')
echo implode(" ", array_reverse(array(1, 2, 3, 4, 5)));
puts [1, 2, 3, 4, 5].reverse.join(' ')
$foo = create_function('$x', 'echo($x);'); $foo(5);
function($x) { ... }(5);
[proc {|i| i + 5 }, proc {|i| i + 6 }]
array(array("name" => "plusFive", "args" => "$x", "impl" => "return $x + 5;"));
foreach(array('php', 'pisses', 'me', 'off') as $i) { echo $i; }
echo $i;
NameError: undefined local variable or method `i' for main:Object
NameError: name 'i' is not defined
error: not found: value i
def whatever(arg1, arg2) { if arg1.whatever { return ((arg3) => arg2 * arg3) } else { return ((arg3) => arg2 * 2 * arg3) }}
Powered by WiGit