Friday, March 30, 2007

php: When ob_start Is Not Your Friend

When programming for the web, you are sooner or later going to start having to look beyond the contents of <body> and messing about with html headers.

And, in php in particular, you will soon encounter the dreaded error which arises when you call 'header()' after having output some characters.

Fortunately, this problem is easily fixed by a call to ob_start, which turns on output buffering and stores and collates your html until it's all done. Headers are placed they're meant to be and all is sweetnes and light.

One common use of headers (which I have just started to get into) is to allow someone to view a file by streaming it through the browser rather than just setting up a url link. (as you might want to do when access to a particular set of files is restricted)

I'm not going to go into great explanation on how this is done; other and more experienced heads have done this very well already (eg: Leon Atkinson's 'Tricks of the Trade'). I'm more interested in describing a subtle but very annoying gotcha when you start doing this with output buffering in play.

You see, if you're streaming a binary file (like pdf or jpeg) for viewing, the binary data has to be very precise or the result will be unintelligible garbage. You must be very sure that no other data sneaks into the stream.

Data such as might be output prior to header calls...

With ob_start on, you will get no warning of this and, like me, you may spend a good deal of time scratching your head wondering why this stuff doesn't work and probably cursing IE (which is a bit more sensitive to this than is Firefox).

Without ob_start you will get an illuminating error saying that there already data in the output buffer. In this situation, *any* prior output is poisonous. Not just the result of print and echo statements, but anything that sits outside the <php? and ?> tags. html tags, spaces, carriage returns, anything.

Fortunately, the error message will usually give you enough information to track down and eliminate the rubbish.

Spurious output *after* the headers is probably less dangerous, but it's probably just as well to get rid of that as well.

Hope this helps someone out there.

Tuesday, March 27, 2007

The Links That Bind

My daughter is usually driven to and from school during the week by her nanny. For this reason, the child seat usually stays in the nanny's car until the weekend.

This has been fine up until recently, when the nanny's car started going through a rough patch mechanically. Twice, in the last fortnight, I have received a call that the car has broken down, or won't start. Apart from the inconvenience of taking 'little Missy' to school ourselves before going to work, we have had to retrieve the means of getting her to school: ie the child seat! This, at a time when an already overloaded traffic system was thrown into chaos by the domain tunnel closure, made for even more time lost.

We've actually been pretty lucky wrt absences. If the nanny has called in sick, it's usually been at the start or end of the week, and we have had the seat. Not this time!

The simple interim solution has been to agree that the seat is to be left at our house in the evenings.

So, you might be wondering what this little domestic drama has to do with bad programming? Simply to point out that, while it might be convenient to have underlying links between class objects that have a lot to do with each other, greater flexibility is achieved when you define the links explicitly with each transaction.

Just as we now put in the child seat each morning and take it out again in the evening, always think about passing a reference to another class as a parameter and don't expect it to be stored, or picked it up from somewhere. The onus on providing the necessary information for an object* to do its job should rest with the client program. That way, your objects can exist a little more independently of each other.

And so can you!
*For the record, I do not consider my daughter's nanny as an object!

Monday, March 19, 2007

Ons Ain't Ons

An interesting little javascript gotcha bit me just recently.

... only I'm not sure whether it was javascript, or Firefox, or (oh, surely not!?) Internet Explorer.

To begin at the beginning, I had a fairly straightforward piece of javascript code working quite nicely in Firefox. Then I tried it in IE, and encountered a truly bizarre disconnect (literally)

What it boiled down to was that the js script was delayed in execution after clicking a checkbox: You'd click on the checkbox and nothing would happen. Then you'd click on something else, and the script would wake up and hurry to the bus stop.

After a bit of poking around, I found the problem: for reasons that escape me now (probably taking the lead from another script meant for use in a combo box), I was using an 'onModify' event to trigger my script, rather than 'onClick'. The problem this was cauing me arises in the way that different browsers handle these events.

Firefox thinks you change the content of a checkbox when you click on it, so it runs the script immediately.

Explorer, on the other hand, decides that changes to data should be acted on when the control has lost focus (and no one's looking), so it waits.

Solution: use 'onClick' whenever you wish to react to changed content of a checkbox.

One is inclined to give in to the temptation to knock IE6 because it's the done thing but, to be honest, I couldn't call the IE behaviour explicitly wrong and I don't know what, if anything, the standards say about when event handling should occur.

Tuesday, March 13, 2007

What's This About?

"To err is you, man!"
As part of my general online presence, I very occasionally print a couple of coding suggestions.

Prompted, in part, by Tim Bray's brief note about 'Beautiful Coding', I thought I'd start gathering these together in one spot.

They won't be anything to set the world abuzz, but there have been times when I've trawled the web looking for how to handle that 'obvious' situation, only to find my brain seems to operate in widely divergent ways to everyone else's.

Of course, this is an affectation: I'm sure there have been others that have asked some of the questions I have. So this is an attempt to answer them.

Now, I'm not sure whether it's better to do this as a blog, or as a wiki. At the time of writing, my thinking is obviously inclined to the former. Plus, it allows the passing traffic to offer the occasional friendly addendum and raspberry (..NB: I did say friendly!)

"Learn from other people's mistakes. It's less embarrassing that way."