Friday 9 January, 2009

Falling off the ob_start stack

Recursive output buffering is a great tool in web-oriented programming, but it can go _really_ wrong.

Caleb and I spent a large chunk of yesterday finding out a bunch of bugs that had been caused by incorrect use of output buffering.

PHP lets you call some commands called ob_start and ob_get_clean, which let you buffer content and save it into a variable. This is great as it allows you to do use the templating abilities to PHP to place output in variables. This lets you do things like:


<?php 

function my_output($name) {
  ob_start();
?>
  
Hi <?= $name ?>, how's it going today?
<? return ob_get_clean(); }

What had happened in our case however, was that functions of the form:


function my_output($blah) {
  ob_start();
  really_long_function_call();
  return ob_get_clean();
}

Had been turned into:


function my_output($blah) {
  ob_start();
  if($cache = cached('really_long_function_call') {
    return $cache;

  really_long_function_call();
  return ob_get_clean();
}

The ob_start() stack was now out of whack. Whenever the top level layout code then tried to close an ob_start(), it would actually close the level that had been started in my_output(), which would cause page elements to show up out of order.

To make things more interesting, when PHP reaches the end of a program, it closes all open buffers and flushes their output out to its output. This really through us off as we spent the first hour of debugging into looking why the buffered output was being flushed when it shouldn't, when this actually wasn't happening.

This is one of those standard cases that when you suspect the bug is in the system libraries instead of your code, you're probably doing something wrong.

Market Place
 

TechWorld Jobs (beta)

c

Recent comments

- + c

Techworld Australia Member Login

c