Zend Framework exceptions use the following pattern:
- A top-level Zend_Exception class is defined, extending Exception, and providing forward compatibility with PHP 5.3 exception support (namely, the "additional exception" argument).
- Each component defines a component-level exception extending Zend_Exception, named after the component: e.g., Zend_Application_Exception.
- Subcomponents may optionally define additional exceptions, extending from their component exception class.
- Only one exception per level in the hierarchy is supported.
This approach, while pragmatic, introduces some inflexibility:
- No component may be distributed without Zend_Exception; this becomes a hard dependency.
- In many cases, it would make sense to utilize and/or extend one of the various SPL exception classes. However, due to the requirement that all exceptions derive from Zend_Exception, this is currently impossible.
- Currently, developers must rely on exception messages to understand why and/or where an exception was thrown. This has led to several requests for translatable exceptions and/or development of unique exception codes – all of which lead to increased maintenance costs.
Our recommendation is to modify how exceptions are defined in Zend Framework 2.
- There WILL NOT be a top level Zend\Exception class
- Each component WILL define a marker Exception interface; subcomponents may optionally define a similar marker interface extending that interface.
- Exceptions WILL extend either \Exception or an SPL exception class, and implement the component (or subcomponent) exception interface.
- Exceptions WILL be named meaningfully: InvalidTemplateException, UnbalancedTagException, etc. Exception types can be re-used, but only if the name has a similar meaning in each context in which it is used.
- Because the previous requirement may lead to a proliferation of exception classes, exception classes MAY be grouped into an "Exception" subcomponent. The rule of thumb will be that if the number of exception classes exceeds 1/2 the number of regular classes and interfaces, they should be segregated.
The theory of operation is best summed up in a use case.
Consider the following directory hierarchy:
Exception.php contains a single interface, Exception in the current namespace. The class Mustache throws exceptions in three cases:
- When given an invalid template path, it throws Exception\InvalidTemplatePathException.
- When a bad partials alias is provided, it throws Exception\InvalidPartialsException
- When it is unable to resolve a template file, it throws Exception\TemplateNotFoundException
While you can catch the individual exception types, it will often be easier to catch more general types. In these cases, you can use one of the SPL exception types (e.g., DomainException, InvalidArgumentException), or the marker interface for the component's exceptions (e.g., Zend\Markup\Exception):