PHP5.5: Try/Catch/Finally

Warning: This blogpost has been posted over two years ago. That is a long time in development-world! The story here may not be relevant, complete or secure. Code might not be complete or obsoleted, and even my current vision might have (completely) changed on the subject. So please do read further, but use it with caution.
Posted on 12 Feb 2013
Tagged with: [ catch ]  [ exception ]  [ finally ]  [ PHP ]  [ try

Exception handling is available in PHP since version 5.  It allows you to have a more fine-grained control over code when things go wrong ie, when exceptions occur. But since PHP 5.5, exception handling has finally evolved into what it should have been from the beginning: the finally part has been implemented.

Let’s start with a simple example on what finally actually does:

<?php

try { 
  print "this is our try block\n";
  throw new Exception();
} catch (Exception $e) {
  print "something went wrong\n";
} finally {
  print "This part is always executed\n";
}

/* Output:
this is our try block
something went wrong
This part is always executed
*/

Whether or not an exception is thrown, the finally block will always be executed. This allows us to do some cleanup and maintenance. Now, one COULD argue that this is easy to do without a finally block by simply adding the cleanup code directly after the try/catch block. But finally is a bit more sophisticated than that.

As a rule of thumb: a finally block will always be executed before PHP leaves the try/catch/finally block. Suppose you have an unhandled exception:

<?php

// Runtime Exception is not caught by the catch-blocks
try { 
  print "this is our try block\n";
  throw new RuntimeException();
} catch (LogicException $e) {
  print "something logical went wrong\n";
} catch (BadMethodCallException $e) {
  print "something made a bad method call\n";
} finally {
  print "This part is always executed\n";
}

/* Output:
this is our try block
This part is always executed

Fatal error: Uncaught exception 'RuntimeException' in 003.php:6
Stack trace:
#0 {main}
  thrown in 003.php on line 6
*/

Neither one of the exceptions inside our catch-blocks match, thus the exception will “trickle down” to the caller (for instance: when this code is called from another file, which also was surrounded by try/catch). In this case, the finally part will be called BEFORE the exception will be raised to the parent.

A simple example:

<?php

function foo() {
  try { 
    print "Try block\n";
    throw new RuntimeException("foobar!");
  } catch (LogicException $e) {
    print "Exception raised: " . $e->getMessage() . "\n";
  } finally {
    print "Finally, some cleanup!\n";
  }
}

try {
  foo();
} catch (RuntimeException $e) {
  print "outer exception: " . $e->getMessage() . "\n";
} finally {
  print "Outer finally\n";
}

/* Output:
Try block
Finally, some cleanup!
outer exception: foobar!
Outer finally
*/

Do you see how the finally block of the outer loop also gets called? So as said before, the finally part allows you do to (local) cleanups that are actually part of the current try/catch block. They keep code neatly grouped together.

Finally and return: all bets are off

I told you that a finally block will always be called, whether or not an exception has been thrown. But what would happen if we issue a return statement inside our try block? Surely, the finally block will not be called, since we are returning from the function? Well, actually, the finally STILL gets called before returning. This is where some weird internal exception magic comes in, that will postpone the return, do the finally block first, and then return. If you think using goto can seriously mess up your workflow, try returning in try/catch/finally blocks :)

Again, an example:

<?php
function foobar() {
  try {  
    $a = 42;
    return $a;
  } catch (Exception $e) {
    print "Exception!\n";
  } finally {
    print "Finally called!\n";
  }
  
  return -1;
}

print foobar() . "\n";

/* Output:
Finally called!
42
*/

Finally still is getting called, even though the try-block returns. But be careful with return values, as they can cause lots of problems that are very hard to spot:

<?php
function foobar() {
  try {
    throw new Exception();
    return 1;
  } catch (Exception $e) {
    print "Exception!\n";
    return 2;
  } finally {
    print "Finally called!\n";
    return 3;
  }

  return -1;
}

print foobar() . "\n";

/* Output:
Exception!
Finally called!
3
*/

This code snippet is a great question on your next Zend Certified Exam :)  What would be the value of foobar()? Let’s assume the try-block will throw the exception, so we move to the matching catch block. Here, we find a return 2. However, before leaving the function, PHP will execute the finally block. Inside this block, there is a return 3. In these cases, PHP will overwrite any postponed return values, so the correct answer in this case will be 3.

So the finally keyword seems very easy, and IS very easy. But there are lots of things that can go wrong if you use it in combination with the return-statement. Here are 3 “rules” that you must be aware of when using return statements:

  • Postponed returns can be overwritten by another return statement Using a return value inside your finally block will override any return statements inside your try or catch blocks.
  • When returning a value, the exception will NOT be reraised.
    If an exception is not caught, it would normally trickle down to the parent (until we hit the bottom, and PHP will throw an uncaught exception error). But as soon as we return a value inside a try/catch/finally block, the exception will not be reraised to a parent, even when its not caught in the current try/catch block.
  • Postponed returns are still evaluated beforehand. Even though it might be possible that our return values get overwritten by other return values in our finally block, the postponed return statement gets evaluated nevertheless. Take a look at the follow example:
<?php
function bar1() {
  print "bar1 called\n";
  return 1;
}

function bar2() {
  print "bar2 called\n";
  return 2;
}
function bar3() {
  print "bar3 called\n";
  return 3;
}

function foobar() {
  try {
    throw new Exception();
    return bar1();
  } catch (Exception $e) {
    print "Exception!\n";
    return bar2();
  } finally {
    print "Finally called!\n";
    return bar3();
  }
  
  return -1;
}
 
print foobar() . "\n";
 
/* Output:
Exception!
bar2 called
Finally called!
bar3 called
3
*/

Here the bar2() and the bar3() functions are called, even though the result of the bar2() is never returned. It might seem counter-intuitive from the php-developers point of view, for PHP internally, it really isn’t.

Conclusion

The finally keyword is something you will be using more and more over time, and with good reasons. But be careful when using return values inside your constructions. They can seriously mess up your flow, and they might be very hard to detect. But by just keeping in mind the last 3 rules, and you will be fine.