« Lessons Learned From An Impromptu Job Search | Exceptional PHP: Extending The Base Exception Class » |
In the last two entries we have talked about the concept of layer abstraction: that is, that exceptions should not be allowed to pass out of one layer and into another. So, when an exception is raised in the database layer it should be caught in the controller. But how do we go about making sure that exceptions raised in the database layer are properly recorded and processed, ensuring that we have error logging and don’t simply silence our exceptions?
There are a number of ways to encapsulate one exception within another, or “nest” our exceptions. Let’s discuss the ways.
Nesting Exception Messages
The base Exception class has a built-in __toString() magic method, meaning that we can automatically convert our exceptions into strings. This makes the following possible:
try { throw new Exception('Message 1'); } catch (Exception $e) { $e = new Exception('Message 2: ' . $e); } try { throw $e; } catch (Exception $e) { $e = new Exception('Message 3: ' . $e); } try { throw $e; } catch (Exception $e) { throw new Exception('Message 4: ' . $e); }
We get back the following exception error message:
The messages are nested, but this does generate some potential issues. First, our exceptions do not have stack traces; the logged stack trace will be the stack trace given in the final exception. Second, the exception message doesn’t contain the exception message codes. However, for simple nested exceptions, this is a good strategy.
Extending Base Exception And Writing Nesting Code
It is possible in PHP to nest exceptions by writing code to do so. For example:
<?php class MyException extends Exception { protected $priorException; public function __construct($message, $code = 0, Exception $previous = null) { $this->priorException = $previous; parent::__construct($message, $code); } public function getPrior() { return $this->priorException; } }
This exception will take a third optional argument of a previous exception, allowing you to nest the exceptions. When preparing to log your exception, you can opt to iterate through any possible previously thrown and nested exceptions, and log any of the data you need.
Using PHP 5.3’s Built-In Nested Exceptions
Anyone that noticed the kludgy way I named the $priorException variable probably wondered why; the reason is that PHP 5.3 introduces nested exceptions as a default part of the PHP base Exception class. While the above code will work, if you are utilizing PHP 5.3, you can pass any previous exception as a third argument (like above), and use the Exception::getPrevious() method to get a previously raised exception.
It would still be wise to develop code that would iterate through the exceptions and log the various data you need; PHP doesn’t incorporate a way to automatically do this.
Summary
You can honor layer abstraction and still ensure that the errors raised are logged and handled appropriately using the various exception nesting techniques above. Ultimately, as PHP improves, so will the nesting options in exceptions.
Brandon Savage is the author of Mastering Object Oriented PHP and Practical Design Patterns in PHP
Posted on 11/12/2009 at 1:00 am
Chris D wrote at 11/13/2009 3:44 pm:
I just wanted you to know how glad I am to see someone in the PHP-osphere is still creating blog posts that teach something about the language. When I first started with PHP about 5 years ago, Planet PHP was a good source for learning about the language.
Anymore, it’s just a clearing house for for conference announcements
« Lessons Learned From An Impromptu Job Search | Exceptional PHP: Extending The Base Exception Class » |