Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="toc"><ac:parameter ac:name="maxLevel">3</ac:parameter></ac:macro>
<h2>Overview</h2>

<p>Zend Framework exceptions use the following pattern:</p>

<ul>
<li>A top-level <code>Zend_Exception</code> class is defined, extending <code>Exception</code>, and providing forward compatibility with PHP 5.3 exception support (namely, the "additional exception" argument).</li>
<li>Each component defines a component-level exception extending <code>Zend_Exception</code>, named after the component: e.g., <code>Zend_Application_Exception</code>.</li>
<li>Subcomponents may optionally define additional exceptions, extending from their component exception class.</li>
<li>Only one exception per level in the hierarchy is supported.</li>
</ul>

<p>This approach, while pragmatic, introduces some inflexibility:</p>

<ul>
<li>No component may be distributed without <code>Zend_Exception</code>; this becomes a hard dependency.</li>
<li>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 <code>Zend_Exception</code>, this is currently impossible.</li>
<li>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.</li>
</ul>

<p>To illustrate, with the current situation, if we want to handle different exceptions from the same component separately, we need to know (a) what type of exception is thrown, and (b) some static part of the exception message:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
try {
$someComponent->doSomething();
} catch (Some_Component_Exception $e) {
if (strstr($e->getMessage(), 'unknown'))

Unknown macro: { // handle one type of exception }

elseif (strstr($e->getMessage(), 'not found'))

Unknown macro: { // handle another type of exception }

else

Unknown macro: { throw $e; }

}
]]></ac:plain-text-body></ac:macro>

<p>If we were to allow simply using SPL exceptions when they seem appropriate, suddenly you need multiple <code>catch</code> blocks to catch all possible types of exceptions for a given operation:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
try {
$component->doSomething();
} catch (Some_Component_Exception $e) {
// handle exceptions that derive from this class
} catch (InvalidArgumentException $e) {
// handle a particular type of exception
}
]]></ac:plain-text-body></ac:macro>

<p>What we've found is that developers typically want one of the following situations:</p>

<ul>
<li>Catch specific exceptions</li>
<li>Catch all exceptions by component</li>
<li>Catch exceptions by SPL exception type (including the base Exception class)</li>
</ul>

<p>Our recommendation is to modify how exceptions are defined in Zend Framework 2.</p>

<ul>
<li>There <strong>WILL NOT</strong> be a top level <code>Zend\Exception</code> class</li>
<li>Each component <strong>WILL</strong> define a marker <code>Exception</code> interface; subcomponents may optionally define a similar marker interface extending that interface.</li>
<li>Each component <strong>WILL</strong> define a generic <code>ComponentException</code> class, extending <code>\Exception</code> and implementing the component level <code>Exception</code> marker interface.</li>
<li>Exceptions extending other SPL exception classes and implementing the marker <code>Exception</code> interface <strong>MAY</strong> be created.
<ul>
<li>Exceptions deriving from SPL exception classes <strong>SHOULD</strong> be named after the SPL exception they extend, but <strong>MAY</strong> be named uniquely. In most cases, we would recommend using the original SPL exception name to prevent a proliferation of exceptions.</li>
<li>Exceptions not named after SPL exceptions <strong>WILL</strong> be named meaningfully: <code>InvalidTemplateException</code>, <code>UnbalancedTagException</code>, etc. Exception types can be re-used, but only if the name has a similar meaning in each context in which it is used.</li>
</ul>
</li>
<li>Because the previous requirement may lead to a proliferation of exception classes, exception classes <strong>MAY</strong> 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.</li>
</ul>

<h2>Theory of Operation</h2>

<p>The theory of operation is best summed up in a use case.</p>

<p>Consider the following directory hierarchy:</p>

<ac:macro ac:name="code"><ac:default-parameter>text</ac:default-parameter><ac:plain-text-body><![CDATA[
Mustache

– _autoload.php
– Exception
  – DomainException.php
  – InvalidArgumentException.php
  – InvalidStateException.php
  – TemplateNotFoundException.php
– Exception.php
– Lexer.php
– Mustache.php
– MustacheException.php
– Pragma
  – AbstractPragma.php
`-- ImplicitIterator.php
– Pragma.php
`-- Renderer.php
]]></ac:plain-text-body></ac:macro>

<p><code>Exception.php</code> contains a single interface, <code>Exception</code> in the current namespace. The class <code>Mustache</code> throws exceptions in several cases:</p>

<ul>
<li>When given an invalid template path, it throws <code>Exception\InvalidArgumentException</code>.</li>
<li>When it is unable to resolve a template file, it throws <code>Exception\TemplateNotFoundException</code>, which extends <code>\DomainException</code></li>
</ul>

<p>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., <code>DomainException</code>, <code>InvalidArgumentException</code>), or the marker interface for the component's exceptions (e.g., <code>Zend\Markup\Exception</code>):</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
use Zend\Markup;
try {
$output = $markup->render($string);
} catch (Markup\Exception $e) {
// do something...
}
]]></ac:plain-text-body></ac:macro>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Sep 02, 2010

    <p>Just to make sure I understand this, the proposal will add three points of identity (essentially) to an Exception:</p>

    <ol>
    <li>The base \Exception class or SPL Exception class types</li>
    <li>A base Exception interface type on a per component basis</li>
    <li>A specifically named Exception type for varying contexts</li>
    </ol>

    <p>All three points seem to indicate that this will require a proliferation of Exception files which doesn't seem likely (programmers are inherently lazy animals). How many Exception files will we need to cover the framework by the time everyone covers all possible contexts?</p>

    <p>What is the thinking behind the proposal? Is there something inherently problematic with a) using specific sub-classes of SPL Exceptions, and/or b) using SPL Exceptions as is? I'm guessing that you're targeting a three-point ID system - allowing users to ID an Exception by base type, component name and context - and that seems to be its main advantage.</p>

    <p>The downside is that the benefits come with the obvious costs of requiring Exception proliferation, denying code reuse (context is King), increasing complexity, and memory capacity issues (in brains) over hundreds of Exception types which I suspect will lead to anti-proliferation without enforcement.</p>

    <p>I recognise the potential benefits but it just seems to be too complex for my tastes.</p>

    1. Sep 03, 2010

      <p>You hit the nail on the head: the point is to be able to catch exceptions by either generic PHP types, by component type, or by specific type.</p>

      <p>The problem with simply using SPL exceptions is that you cannot catch based on component exception:</p>

      <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
      try {
      $component->doSomething();
      } catch (Component\Exception $e) {
      // will not catch for SPL exceptions
      }
      ]]></ac:plain-text-body></ac:macro>

      <p>If we have a base exception class per component, we then run into the situation that when there are more valid, descriptive SPL exception types, we cannot extend both the SPL exception type AND the component's exception type – multiple inheritance works only in the case of interfaces. This leads to needing constructs like this:</p>

      <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
      try {
      $component->doSomething();
      } catch (Component\Exception $e) {
      // handle exceptions that derive from this class
      } catch (InvalidArgumentException $e) {
      // handle a particular type of exception
      }
      ]]></ac:plain-text-body></ac:macro>

      <p>This is putting the onus on the end-user to know what all the different exception types are – instead of being able to use a single "catch-all" type for a given component.</p>

      <p>Yes, it will potentially lead to a proliferation of exception classes. That's part of why I created the proposal – to find out if perhaps we need to refine the proposal. <ac:emoticon ac:name="smile" /></p>

      <p>What about if we were to do the following:</p>
      <ul>
      <li>Base exception marker interface per-component</li>
      <li>Base exception <em>class</em> per component implementing the interface: e.g., MustacheException</li>
      <li>If desiring to extend SPL exceptions, create variants in the component: e.g. DomainException extends \DomainException implements Exception
      <ul>
      <li>Names could vary <strong>IF</strong> a good case is made (e.g., multiple DomainException types might clarify intent)</li>
      </ul>
      </li>
      </ul>

      <p>Thoughts?</p>

      1. Sep 03, 2010

        <p>Last comment was me thinking out loud - I think the proposal could elaborate on the whys and why nots because it's very vague at the moment making it hard to judge the proposal.</p>

        <p>That said, I think your later comments clear up the main point of contention I had so I'm all out of arguments <ac:emoticon ac:name="wink" />. Once you can put some slowdown on the need to proliferate Exception types, the benefits are worth it.</p>

        1. Sep 03, 2010

          <p>I've updated the proposal to reflect the discussion here in the comments – and glad to hear that my clarifications/revisions so far have made you more comfortable with the proposal!</p>

      2. Sep 04, 2010

        <p>This is a huge improvement over ZF1. Branching off of message text is silly and makes catching exceptions awkward.</p>

        <p><strong>> Base exception marker interface per-component</strong><br />
        <strong>> Base exception class per component implementing the interface: e.g., MustacheException</strong></p>

        <p>This makes it easy to catch everything under the component, so it helps early prototypes and really helps the lazy. This is a good thing <ac:emoticon ac:name="smile" /></p>

        <p><strong>> If desiring to extend SPL exceptions, create variants in the component: e.g. DomainException extends \DomainException implements Exception</strong></p>

        <p>This makes getting granular less awkward and it is what other frameworks have found to make sense (because, well, it makes sense).</p>

  2. Sep 03, 2010

    <p>I would be against the proposal.<br />
    There will be potentially an explosion of exceptions all with a different use case. If some new functionality is added which can throw an exception we are almost bound to also create an exception class for this.</p>

    <p>Only one exception per level in the hierarchy is supported is a good thing. The context can be given as the message param. You can always getPrevious() and get the message from the previous Exception if needed.</p>

    <p>One benefit I see is you can catch the individual exception types, but do we really want to be that specific in our error handling?</p>

    <p>I can see this system is already implemented at \Zend\Loader but in my honest taste it shouldn't, together with my arguments from above, just take a look at that folder and you know what I mean.</p>

    1. Sep 03, 2010

      <p>Please see my comment to Paddy's notes, and comment on whether the revisions I suggest would answer your concerns?</p>

      <p>The problem I see with continuing to use the exception message as context include:</p>

      <ul>
      <li>Exception messages are often unclear about intent</li>
      <li>Non-fluent English speakers often have trouble understanding the message</li>
      <li>For messages with variable content, it's often difficult to search code for the message, making identifying the source more difficult. (Yes, that's what the trace string is supposed to help you with, but not all developers understand how to read a trace, and instead search for the string.)</li>
      </ul>

      <p>The above issues have led to calls for "translatable exceptions", which would add a ton of complexity to the current exception system, for what I feel is very little benefit (this is not intended to slam or slight non-fluent English speakers; however, if you consider that the API documentation, class names, variable names, etc. are all English, translating the exception messages seems fairly pointless). However, if we don't change the way in which we approach exceptions and make them more self-describing, I don't see how we can stand in the way of adding some sort of functionality for categorizing exceptions (exception codes, translations, etc.). And this will be as much or more of a burden as having a larger number of exceptions when it comes to maintenance and planning (as each exception would require a separate code, and we'd need to ensure these are unique across components).</p>

      1. Sep 04, 2010

        <p>At the beginning of the proposal everything didn't seem very clear to me, but as the proposal has been refined, made more clear, and the discussion grew which addressed the cons and pros, I now feel more comfortable with the proposal. Thanks for taking the time to optimize the proposal. </p>

  3. Sep 03, 2010

    <p>This looks good to me, I can understand peoples worries about exception proliferation. Could we address that by providing more concrete guidelines? </p>

    <p>Currently we have:</p>

    <p>Exceptions WILL be named meaningfully: InvalidTemplateException, UnbalancedTagException, etc.</p>

    <p>This seems a bit open to me, are we going to end up with lots of the same types of exception named differently? </p>

    <p>I suppose what I am trying to ask is is there a way we can manage the exception proliferation?</p>

    1. Sep 03, 2010

      <p>See my comments to Paddy – I think if we were to have the following structure, we could manage the proliferation:</p>

      <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
      namespace Zend\Component;

      interface Exception {}

      class ComponentException extends \Exception implements Exception {}

      class DomainException extends \DomainException implements Exception {}
      ]]></ac:plain-text-body></ac:macro>

      <p>Typically, exceptions would be named after their SPL counterparts with this paradigm, meaning we'd only have a handful per component. If a situation truly demanded a more semantic name for the exception, we'd allow it, but we'd discourage proliferation in that way.</p>

      <p>Make sense?</p>

  4. Sep 03, 2010

    <p>I think that this is a strong improvement over exceptions in ZF1. My primary complaint with Exceptions in ZF1 is that they do not have meaningful names. Exceptions should express what went wrong, not where it went wrong and I think that this proposal addresses that issue.</p>

    <p>Yes, it sounds a bit onerous, but I think it is correct. The use of interfaces is a clever way to communicate the 'where' when throwing SPL exceptions.</p>

  5. Sep 09, 2010

    <p>I like the new way of dealing with exceptions. It makes it more clear what was wrong and adds better support to do different actions on different types of exceptions.</p>

    <p>My only notation is the following:<br />
    The exception interface it located directly within base component namespace and all exception classes are located on an own namespace within the component.<br />
    This is fine but why the base exception class per component isn't located within the own namespace? The only different is it extends the base exception class instead of one of the SPL exceptions.</p>

    <p>For me it would make it more clear to move this into the own exception namespace, too:<br />
    e.g. Mustache\MustacheException -> Mustache\Exception\MustacheException</p>

    <p>-> A component can only throw one of the SPL based exception classes or the base exception class on failure - this means - not all components need the base exception class if all failures will throw SPL based exceptions.</p>

    1. Sep 10, 2010

      <p>"This is fine but why the base exception class per component isn't located within the own namespace?" – it would be, if exceptions are moved into their own sub-namespace.</p>

      <p>"not all components need the base exception class if all failures will throw SPL based exceptions" – Correct. In fact, the trial run Ralph did in Zend\Authentication falls under this category, as all of the exceptions he defined fell under the aegis of one of the SPL exceptions. A base exception class is only necessary for components having exceptions that cannot easily be categorized by one of the SPL exception types.</p>

      1. Sep 15, 2010

        <p>Would there be a problem with always putting the exception classes/interface under a sub-namespace? I would personally prefer the consistency knowing it is always done that way.</p>

  6. Sep 11, 2010

    <p>The SPL exceptions are:</p>

    <p>BadFunctionCallException<br />
    BadMethodCallException<br />
    DomainException<br />
    InvalidArgumentException<br />
    LengthException<br />
    LogicException<br />
    OutOfBoundsException<br />
    OutOfRangeException<br />
    OverflowException<br />
    RangeException<br />
    RuntimeException<br />
    UnderflowException<br />
    UnexpectedValueException</p>

    <p>(from <a class="external-link" href="http://www.php.net/manual/en/spl.exceptions.php">http://www.php.net/manual/en/spl.exceptions.php</a>)</p>

    <p>Being able to use these will be very handy.</p>

    1. Sep 13, 2010

      <p>One thing to note: almost all of these exceptions descend from either LogicException or RuntimeException – which gives another level of granularity you can use when catching.</p>

  7. Sep 14, 2010

    <ac:macro ac:name="note"><ac:rich-text-body><p><strong>Community Review Team Recommendation</strong></p>

    <p>The CR-Team recommends accepting this proposal as-is.</p>
    </ac:rich-text-body></ac:macro>

  8. Oct 01, 2010

    <p>As I am working on the exceptions milestone at the Zend\Text Component I see another point.<br />
    I my opinion it would be usefull to add another Interface, a Zend\Exception.</p>

    <p>namespace Zend;<br />
    interface Exception {}</p>

    <p>namepace Zend\SomeComponent;<br />
    interface Exception extends \Zend\Exception {}</p>

    <p>namespace Zend\SomeComponent\Exception;<br />
    class InvalidArgumentException extends \InvalidArgumentException implements \Zend\SomeComponent\Exception {}</p>

    <p>This way we can test, whether an Exception has been thrown through Zend Framework.</p>

    1. Oct 01, 2010

      <p>We actually specifically chose <em>not</em> to do that.</p>

      <p>The reason is that having a base exception class provides little value, and adds an extra dependency to each and every component. This latter makes packaging individual components more difficult – and in ZF2, we plan to package components individually for those that want to cherry-pick what they use.</p>

      <p>As to my assertion that adding it provides little value, I ask you this: What value is there to catching any exception thrown by <em>any</em> component of the framework? If you're catching something that broad, you can likely simply catch on Exception instead; otherwise, it's better to know what components you're invoking, and catch based on the component exceptions. Even when mixing and matching components from a variety of libraries, you will likely still want to react to specific types of exceptions, and not at the library-level; in fact, in this latter case, you probably have <em>more</em> insight into what exceptions may be thrown than in any other situation.</p>

  9. Oct 06, 2010

    <p>I have a question regarding how to treat exceptions of subcomponents.<br />
    F.e. we take a look at Zend\Search\Lucene:<br />
    Formally we had exceptions for all the subcomponents of that, f.e. Zend\Search\Lucene\Exception, Zend\Search\Lucene\Document\Exception, and so on.<br />
    If we change all exceptions to: Zend\Search\Exception\InvalidArgumentException, Zend\Search\Exception\RuntimeException, and so on, we will lose the extra benefit of knowing whoch subcomponent throwed that exception.<br />
    For the example of Zend\Search\Lucene, we probably have one day a Zend\Search\Solr and others, so a marker interface Zend\Search\Exception would be good, and an additional marker interface Zend\Search\Lucene\Exception, with an exception folder in Zend\Search\Lucene and some exceptions in there. Additionally for deeper subcomponents, like Document, Analysis, Search, and so on, it would be a good idea, to add another marker interface, Zend\Search\Lucene\Document\Exception, and so on, with another Exception folder in Zend\Search\Lucene\Document.<br />
    We gain our extra value back, but in some cases, we need a autoload many interfaces and classes to throw just a simple exception.<br />
    What do you think?</p>

    1. Oct 06, 2010

      <p>My comment is not only about subcomponents, but also about subsubcomponents, subsubsubcomponents, and so on. Do we need an extra marker interface for every (sub-)subcomponent with exception folder inside?</p>

    2. Oct 06, 2010

      <p>It makes complete sense to create a marker interface for discrete sub-components. In fact, IIRC, Zend\Authentication does exactly that – it has a separate sub-component marker for adapter exceptions. In such cases, the marker interface extends the exception interface from its parent component.</p>

      <p>Basically, whenever it makes sense to add an additional level of granularity, do it.</p>

  10. Jan 16, 2011

    <p>I write some code using dev versions of ZF2, and this idea with overwriting default SPL exceptions with same names makes big problems in code writing.</p>

    <p>Just an example of how it looks like in IDE. <a href="http://img375.imageshack.us/img375/5236/exceptions.png" title="View image">Netbeans use autocomplete</a></p>

    <p>In most modern IDE whuch supports php 5.3 we can write a short name and autocomplete it to full, generete use, etc. So having tons of identical short names makes autocomplete and class search much harder (moreover IDE's sometimes can't catch so many variants, ex. Netbeans kill my Compiz/X-server, when press alt+enter on InvalidArgumentException <ac:emoticon ac:name="smile" /> )</p>

    1. Jan 16, 2011

      <p>I think you should blame your IDE for this. The thing about namespaces is that you will have classes with the same classname. The thing that tells them apart is the namespace they're in.</p>

      <p>What you should do is to autocomplete Zend\Controller\Exception\InvalidArgumentException is (the backslashes are probably redundant too):<br />
      Z<tab>\Co<tab>\Ex<tab>\Inv<tab> </p>

      <blockquote><p>ex. Netbeans kill my Compiz/X-server</p></blockquote>

      <p>That is what they invented real IDE's for.</p>

    2. Jan 16, 2011

      <p>This is an issue you should raise with the Netbeans developers. One reason for having namespaces in PHP in the first place is to allow for classes of the same name living under different namespaces. This allows "overriding" the functionality of one class within another namespace – another good example is extending SPL data structures such as SplPriorityQueue – you can then do something like the following in your code:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      namespace Foo\Bar;

      $queue = new SplPriorityQueue(); // actually Foo\Bar\SplPriorityQueue!
      ]]></ac:plain-text-body></ac:macro>

      <p>or:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      use Project\Stlib\SplPriorityQueue; // References to SplPriorityQueue within this file now refer
      // to this implementation
      ]]></ac:plain-text-body></ac:macro>

      <p>If the IDE is having issues with autocompletion, then it's likely not doing static analysis on the file in order to determine the namespaces and aliases in play.</p>

      1. Jan 19, 2011

        <p>I'm a little bit disagree. Namespaces (packages/etc) in different languages is an ability to avoid name conflicts with external libs, and not use the same short name. Also, i think, that short name should give a maximum information about class.</p>

        <p>In your example <strong><em>Spl</em></strong> prefix just sais that it's an <strong><em>SPL</em></strong> 'package' and <strong><em>PriorityQueue</em></strong> gives a full info about this class. So I don't understand what is the reason to name a custom class as <strong><em>NS\SplPriorityQueue</em></strong>, what the difference with <strong><em>SplPriorityQueue</em></strong>? this name give us nothing, like tons of <strong><em>InvalidArgumentException</em></strong>s which doesn't modify anything, just implement an interface </p>

        <p>ex. our class can be locked to prevent future modification, than name it <strong><em>NS\LockablePriorityQueue.</em></strong> Or it has some persistent operations inside, ok, name it <strong><em>NS\Orm\Collections\PersistentPriorityQueue</em></strong>.<br class="atl-forced-newline" /></p>

        <p>Let's return to IDE's. All modern IDEs support shortname => fullname class completition for languages with namespaces/packages/etc, use/import/etc auto generation and other features (For example Java language and Idea,Netbeans,Eclipse). And when you have too many classes with same short names, this features become useless, it's faster to write a full name by your self, but there is no such problems in other languages with IDEs, because programmers try to have different shortnames for classes by giving full descriptive names.</p>

    3. Jan 16, 2011

      <p>This is likely a problem with a specific version of netbeans or a configuration issue. I am using IntelliJ (+IdeaVim) and my other two team members are using Netbeans. Nobody on our team has had a similar problem with either IDE thus far...</p>

      <p>BTW, it is not such a hot idea to allow your IDE to tie you down or stop you from using more advanced language features. I suggest that if the IDE is in the way, raise an issue with the IDE vendor (I've done this several times), and until they get it corrected, set the IDE aside or ignore the missing feature. In other words, please do not change the code to support the IDE <ac:emoticon ac:name="smile" /></p>

      1. Jan 19, 2011

        <p>The problem that we miss IDE features, using same short names for every exception also i disagree with php namespace understanding, i still think that we must give descriptive short names, not just copy it from an overrided class. I replied one post upper about IDE features that we miss (this feature are standard for package/namespace support). JetBrains IDE's unfortunately don't support such features for PHP yet (just pulled them a request), Netbeans already supported partialy, don't know about current version of PDT.</p>

  11. Jan 19, 2011

    <p>Also, is there an example of real advantage of using exception interfaces for built-in or external exceptions?<br />
    I opened such discussion in our Russian community few months agom but nobody from this propsal defenders could give a real example. Most of examples had an OOP problems, when trying to catch exceptions, which should be unknown for current code.</p>