Writing Solid PHP
I borrowed Eric's Writing Solid Code by Steve Maguire, because I've always wanted to read it. It's one of those software development classics, etc. At a high level, it describes Microsoft's approach to writing bug-free code by using a set of techniques that catch bugs as soon as possible -- before they reach the source repository, Testing, or, heaven forbid, the customer.
At first, I was surprised at how old it was -- 1993! Before Win95 even! Then, it seemed quite out of date. It mostly talks about programming in C on some old, anemic platforms without the niceties that we take for granted, such as hardware faults when dereferencing invalid pointers. So, of course, I'm trying to take higher-level lessons from it, such as the approach he's taken to develop these bug-eliminating techniques, and then apply that approach to the environment in which I work. Unfortunately, a lot of the techniques have to do with low-level C, where you don't have a powerful and safe type system or GC to eliminate whole classes of errors. Most of the specific bugs he's mentioned wouldn't even arise in Haskell, Python, Java, or C#. Also, this book was written before unit testing and test-driven development were really commonplace, and I think that these supplant some of the techniques Steve mentions. (Not all; there is some overlap between test-driven development and writing bug-free code, but neither TDD nor after-the-fact comprehensive tests give you license to write sloppy code.)
And as I'm thinking about how spoiled we are with our strong typing, garbage collection, and hardware exceptions, I realized that I program every day in an unsafe language that tries as hard as it can to hide errors from you, just like the microcomputers of yore. PHP. I don't think there's been a day yet where learning about a PHP design decision hasn't made me think "WTF". I mean, why do you need a rich collection of container types such as in STL or java.util? PHP's array does it all! If you pass too few arguments to a PHP function, that's okay, the missed ones will just be null. If you pass too many, no problem! They'll be ignored. Try to append something to an array that _doesn't even exist_, no problem! It'll magically create the array! I could go on and on. Anyway, the language actively prevents you from finding bugs.
So, from here on out, I'm going to read the book, trying to apply every technique to PHP. I think there is a lot we can learn. Here's the first technique that would catch bugs in our code that popped into my head:
function foo($a, $b) { tep_assert(count(func_get_args()) == 2); ...
Another one: null, true, and false are considered 'magic numbers' without immediately visible context. What does...
updateProgress(true);
...mean? Compare that to...
updateProgress(showNewMessage=true);
It's well-known that true and false make bad function parameters, but I'd never thought about 'null' being a magic number, however. And it is, for the same reasons as true and false above. When you see code like 'new DialogBox(null)', what does that null represent?
I'm sure more will come...
Not to be too off topic... but you really should finish up the honeymoon account and post it. I'm sure people want to hear about our time there!
So what you're trying to say is that you're essentially going to double the size of your code to do bug prevention that PHP ought to do anyway. And hey, if you're going that far, you might as well make a whole framework for it! :)
Exactly. But it's cheaper (for now) to add these bug-prevention techniques ad hoc to our code rather than encoding them directly within the system, as much as I'd like that. :)
Something I've found myself doing rather frequently lately is extracting a method to take a single associative array instead of a set of arguments. I blame Python for giving us **kw, and PHP for taking it away. :(
Whoa! I've been doing that as well, and I hadn't even been thinking about it! I can see how doing it natively would go against the spirit of all the silent and dynamic declarations and typing.
Speaking of php silently munging types, and functions passed as an array... The pecl extension libcurl in latish (>5.1) versions of php can "sort of" (you need to call two or three functions, but you get there) take an arbitrary number of pseudo-parameters in an array through the function curlsetoptarray() (I think it's called) .. Long story short though... I haven't figured out which constants I can actually put through there without it issuing a warning (that's right, not a notice.. A WARNING) ... making it an irritating function. I haven't tried giving it integer values yet, maybe that will work.... lol....
And ANOTHER thing! In PHP5 (tested under 5.2.x) I found interesting behaviour from filegetcontents() .. I think it will be fixed in PHP6 and is acknowledged as a bug (maybe even php 5.3) but try giving the function an error (say, a file doesn't exist or the redirection limit is exceeded if over http) and attempt to try/catch it. You can't. With the great new private, public, protected, extended and so on and so forth classy power of recent php versions - and with the object oriented nature of a lot of common extensions and built in functions now such as things to do with dates - well, some things throw exceptions and some just throw errors.
How do you deal with this? It was a only a 200-line toy script where this actually stung me, but what I had to do was to implement a custom error handler (for that!?)... So if you want to use try/catch then in some specific situations you need to have an error handler AND an exception handler. Small thing to care about but a gotcha -- this is why people test what they write, I guess.
Regardless, on that script I just die for most issues, or at least complain very loudly and let it happen five times and then die.
Wow, I've written so many words and yet I haven't actually said anything. Meh, maybe someone can salvage something from it; I'll submit it anyway :P