PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1

CHAPTER 4 ■ ADVANCED FEATURES


// broken xml
} catch ( ConfException $e ) {
// wrong kind of XML file
} catch ( Exception $e ) {
// backstop: should not be called
}
}
}


I provide a catch clause for each class type. The clause invoked depends on the exception type
thrown. The first to match will be executed, so remember to place the most generic type at the end and
the most specialized at the start. For example, if you were to place the catch clause for Exception ahead
of the clause for XmlException and ConfException, neither of these would ever be invoked. This is
because both of these classes belong to the Exception type, and would therefore match the first clause.
The first catch clause (FileException) is invoked if there is a problem with the configuration file (if
the file is non-existent or unwriteable). The second clause (XmlException) is invoked if an error occurs in
parsing the XML file (if an element is not closed, for example). The third clause (ConfException) is
invoked if a valid XML file does not contain the expected root conf element. The final clause (Exception)
should not be reached, because my methods only generate the three exceptions, which are explicitly
handled. It is often a good idea to have a “backstop” clause like this, in case you add new exceptions to
the code during development.
The benefit of these fine-grained catch clauses is that they allow you to apply different recovery or
failure mechanisms to different errors. For example, you may decide to end execution, log the error and
continue, or explicitly rethrow an error:


try {
//...
} catch ( FileException $e ) {
throw $e;
}


Another trick you can play here is to throw a new exception that wraps the current one. This allows
you to stake a claim to the error, to add your own contextual information, while retaining the data
encapsulated by the exception you have caught. You can read more about this technique in Chapter 15.
So what happens if an exception is not caught by client code? It is implicitly rethrown, and the
client’s own calling code is given the opportunity to catch it. This process continues either until the
exception is caught or until it can no longer be thrown. At this point, a fatal error occurs. Here’s what
would happen if I did not catch one of the exceptions in my example:


PHP Fatal error: Uncaught exception 'FileException' with message
'file 'nonexistent/not_there.xml' does not exist' in ...


So when you throw an exception, you force the client to take responsibility for handling it. This is
not an abdication of responsibility. An exception should be thrown when a method has detected an error
but does not have the contextual information to be able to handle it intelligently. The write() method in
my example knows when the attempt to write will fail, and it knows why, but it does not know what to do
about it. This is as it should be. If I were to make the Conf class more knowledgeable than it currently is,
it would lose focus and become less reusable.

Free download pdf