do { ... } while (0);

I’m currently buried in over 20” of snow, so I might as well post something.

I wrote a piece of PHP code a while back to grab some data from a cache; if it couldn’t find what it needed, it should try a couple of other scenarios before giving up. The code looked something like the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$category = 'someCategory';
$cacheHandler->setCategory($category);
$data = $cacheHandler->getData();
if (!count($data)) {
$cacheHandler->setCategory($category + '-someAlternative');
$data = $cacheHandler->getData();
}
if (!count($data)) {
$cacheHandler->setCategory($category + '-someOtherAlternative');
$data = $cacheHandler->getData();
}
if (!count($data)) {
$data = null;
}

Yuck. An obvious solution would have been to refactor this logic into a method and return the data if it’s found, avoiding the extra checks. If this were JavaScript, a function expression would have done the job:

1
2
3
4
5
6
7
8
9
var result = (() => {
let category = 'someCategory';
cacheHandler.setCategory(category);
if (data = cacheHandler.getData()) return data;
category = 'someCategory2';
cacheHandler.setCategory(category);
if (data = cacheHandler.getData()) return data;
// etc...
})();

But this was PHP. I’m guessing at the time I didn’t want to bother making a new method for a one-off operation, so I left it as is. Maybe. There are many times I look at my old code and wonder what the hell I was thinking.

Months later, a veteran on my team refactored the file. When I checked the code again, I saw this block in its place:

1
2
3
4
5
6
7
8
9
do {
$category = 'someCategory';
$cacheHandler->setCategory($category);
if (!$data = $cacheHandler->getData()) break;
$cacheHandler->setCategory($category + '-someAlternative');
if (!$data = $cacheHandler->getData()) break;
$cacheHandler->setCategory($category + '-someOtherAlternative');
if (!$data = $cacheHandler->getData()) break;
} while (0);

The do-while loop will only execute once and creates a separate block that can be broken out of at any time. It was a pretty neat trick for avoiding another method that I hadn’t seen before.