Versions matter

I ran into an interesting PHP bug at work the other day. Actually, it was a bug in PDO in a particular version of PHP.

Here's the situation. I needed to write a script to do some fairly extensive data massaging for a database integration. Since we have PHP installed on pretty much all of the production and development servers, I wrote it in PHP. It worked perfectly on the test server, so I figured it was safe to use it in production. And I was right, to a certain extent: it was safe in the sense that it didn't break anything - it's just that the script didn't work.

I won't get too much into the details of the script, as it's kind of long and not particularly interesting, but here's some sample code with the key points: while ($rec = $data_stmt->fetch()) { $db->beginTransaction(); $get_id_statement->execute(array($rec['something'])); $id = $get_id_statement->fetchColumn(); $update_statement->execute(array($somevar, $id)); $db->commit(); } The upshot here is that I was looping through the result set from a query and performing other select and update queries based on the current record. Simple enough, right?

The error message I received when I tried to run the script on our production database server was this:

PDO::prepare(): SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.

The wierd thing about this is that, as I said, the script worked perfectly on the development server. And the development servers are supposed to be configured the same as the production servers. Of course, that obviously wasn't the case, so we ended up spending the next hour or so messing around with various code configurations with regard to transactions, prepared statements, and all that good stuff.

To make a long story short, it turned out that the explanation was simply that the development database server was running PHP 5.2.x and the production database server was running 5.1.x. The version on the production server still has an experimental version of PDO which, as it turns out, didn't work quite right. If I had just adjusted the IP address in the PDO connection string and run the script from our production staging server, which has PHP 5.2, it would have worked perfectly the first time. Unfortunately, I didn't discover that until I had already wasted an hour bug-hunting and messing with the script until I got it to work on PHP 5.1 (by moving all the fetch and fetchColumn method calls to fetchAll). Live and learn.

Insane library design

My current project at work is to integrate a web application that runs one aspect of our site with the rest of the site. Part of this involves converting the database code from a custom DB abstraction library to use the standard PHP Data Objects (PDO) library that comes with PHP 5.1. I had never used it before, but it turns out PDO isn't a bad library. It has an object-oriented API, supports multiple databases, "prepared" (i.e. DBMS parametrized) queries, transactions, and so forth. Pretty much the basic features you need in a database abstraction layer. The only problem is that the error handling API is somewhere between "weird" and "completely frickin' insane."

You see, PDO has "configurable" error handling. That is, when a method reaches an error condition, it will either return an error code or throw an exception. Which one it does is determined by an attribute setting on the PDO connection object. (Technically, it may actually return the error code and throw an exception, but the return value is irrelevant once an exception is thrown, so it doesn't really make any difference.)

Let me repeat that, in case you missed it: PDO will either throw an exception or return an error code, depending on a setting of the PDO connection object. So it's possible to change one single constant and break error handling for an entire script.

Upon learning this, my initial reaction was: "That's the stupidest thing I've ever heard in my life!" I mean, honestly, who ever heard of configurable error handling? As if programmers don't have enough to worry about with database code, now we have to pick an error handling mode as well?!? And the worst part is that, when you read the error handling documentation, the default mode not only doesn't throw an exception, it doesn't even print a warning. It just leaves you to see the bad return value and manually inspect the error code on the object. You have to manually set it to turn on warning messages. Why?!? Isn't that why we have the error_reporting variable in php.ini? What the heck were they thinking?!?

What really kills me is that exceptions are optional, but object-oriented programming is mandatory. Unlike, say, the MySQL extension, PDO does not have a procedural version of the API, just the object-oriented one. And yet exceptions, which I think I can safely say are the object-oriented way of doing error handling, aren't even used by default. Shouldn't that be the only way of dealing with errors? Why is it better to make them optional? What's the rationale behind that?

So far, the only rationale I can think of is that the PHP community has what seems to be a disproportionate number of morons in it. That may be a little harsh, but it seems to me like there are a lot of third-rate PHP programmers and that the people who run the PHP project tend to go a little too far to cater to them. I can only assume that somebody thought that exceptions might be too hard for some PHP users, so they figured it would be better to use error codes by default. Kind of like how they figured securing your server was too hard, so they added the fake band-aid of safe_mode. Or how they figured a simple function call or array look-up was too hard, so the invented register_globals. Or how they figured that cleaning form input was too hard, so the invented "magic quotes."

PHP isn't a bad language, and I do enjoy working with it, but it has gone through a long list of really bone-headed design decisions. In some ways, it seems like it's succeeded in spite of itself. I guess this is really just evolution in action. Take a simple template engine, let it mutate into a full-blown programming language, and this is what you get: a platypus. Lots of endearing features and lots of brain-dead features, but it's still alive and still evolving.

Dual monitors rock

Well, it's only been a little a couple of weeks, but I'm already spoiled. You see, at my new job I have dual monitors. And you know what? I don't think I'll ever be able to go back. I'm already trying to figure out how I can talk Sarah into letting me buy another 19-inch widescreen LCD.

The benefit of dual monitors is exactly the same as that of having a bigger single monitor - you can see more things at one time. This leads to greater productivity, as Jeff Atwood has been saying for years. And it's not just opinion and anecdote - studies have shown that increased monitor space can improve productivity by 9 to 50 percent, depending on the task.

For programmers, having an extra monitor is very handy. You can have your IDE full-screen on one monitor and use the other one for testing, documentation, supporting files and programs, and so forth. No more flipping between windows. It sounds like a small thing, but once you've tried it, it really does make a difference.

Of course, setting up dual monitors is naturally trickier than a single monitor. In my case, I'm working with an Nvidia card under Kubuntu 7.04. The setup wasn't too difficult, but there were a few hitches. I simply had to install the nvidia packages from the APT repositories and run the installer from Nvidia's website, which built the kernel modules. However, for some reason the kernel module doesn't work the when it is loaded after boot, but it's fine if you reload it. That makes no sense to me. Plus, it seems to have broken guidance-power-manager. Annoying, but not a big deal since this is a desktop. On the up side, the Nvidia configuration tool works well and is easy to use.