Athos@BalaBit

IT's fun! :-)

Try-Catch-Finally in PHP

Sunday, February 20, 2011 @ 07:02 PM Author: athos

As of PHP 5.3 web developers can use a wonderful feature of the language, the __invoke() magic method. This enables PHP to provide the Closure class and based on it the ability to define anonymous functions. Anonymous function is a powerful language construct, it can come handy in a number of ways. For example, it makes the code easier to manage by letting you define locally visible small functions inside a method or a function. This is useful when you want to sort an array by some special property of the elements in it – you don’t have to define a named function or method in your global scope or in a class, you can just hide the comparison callback inside the method that needs a specially sorted array. But anonymous functions combined with other language constructs, can even be used to emulate some features missing from PHP: finally for instance.

Some say the need for a finally keyword is the result of bad design. Most people would use finally in a similar situation like this to avoid code duplication in the catch block and after the try-catch statement:

class A { public function doSomething() { $my_resource = open_my_resource(); try { $result = use_my_resource($my_resource); } catch (Exception $e) { free_my_resource($my_resource); throw $e; } free_my_resource($my_resource); return $result; } }

Using finally the above code snippet would look like this:

class A { public function doSomething() { $my_resource = open_my_resource(); try { $result = use_my_resource($my_resource); } finally { free_my_resource($my_resource); } return $result; } }

In this situation rethinking the design can help avoid the need for finally:

class MyResource { public function __construct() { $this->resource = open_my_resource(); } public function __destruct() { free_my_resource($this->resource); } public function use() { return use_my_resource($this->resource); } private $resource; } class A { public function doSomething() { $my_resource = new MyResource(); $result = $my_resource->use(); return $result; } }

But sometimes life’s not that easy. Sometimes your hands are bound, sometimes it’s not that trivial to circumvent the need for finally. That is the case when a nice hack based on closures can help:

class FinallyEmulator { public function __construct($callable) { if (!is_callable($callable)) throw new ErrorException('Ooops, bad callable.'); $this->callable = $callable; } public function __destruct() { $this->invoke(); } public function __invoke() { $this->invoke(); } private function invoke() { if ($this->callable) { $callable = $this->callable; $this->callable = NULL; call_user_func($callable); } } private $callable; } /** * Note that return value of $callable is totally ignored. */ function finally($callable) { return new FinallyEmulator($callable); }

Using that class you can transform the second code snippet in this blog post like this:

class A { public function doSomething() { $my_resource = open_my_resource(); $finally = finally(function () use ($my_resource) { free_my_resource($my_resource); }); try { $result = use_my_resource($my_resource); } catch (Exception $e) { fprintf(STDERR, "Error: %sn", $e->getMessage()); throw $e; } $finally(); return $result; } }

Note that this workaround does not handle the return-inside-finally problem at all as you cannot return from the function from the simulated finally block

Usage examples and the code of FinallyEmulator class can be found at my GitHub PoC repository.