PHP5.5: Try/Catch/Finally
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:
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:
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:
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:
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:
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:
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.