Exception misinformation

From: Date: Tue, 06 Jul 2004 19:37:18 +0000
Subject: Exception misinformation
Groups: php.pear.dev 
Request: Send a blank email to pear-dev+get-31662@lists.php.net to get a copy of this message
There seems to be a lot of (m|d)isinformation about Exceptions on this list. I urge those of you involved in these discussions about Exceptions to actually try using them -- build an application with them. I have used exceptions, PEAR_Error, and other stack-based approaches in PHP and I am not using Exceptions because they are a "trend", but because they have helped me solve patterns faster, write cleaner code, and better handle errors. Here's an attempt to correct what I feel are bits of misinformation from recent discussions. 1) Exceptions are not FATAL. They are designed to be caught; for an exception to become a fatal error is for a programming error to have been made (i.e. not catching). Calling private methods from outside of an object context also results in an E_FATAL error; does this mean that private methods are fatal? And, as mentioned below, forgetting to call PEAR::isError() on a returned object & then attempting to invoke a method will also result in fatal error. Is PEAR_Error also only for fatal errors, then? 2) Using exceptions does not mean wrapping every method call in a try/catch block. The idea with exceptions is that they allow you to group things into logical units -- think of business transactions, or Transaction Script pattern. try/catch blocks only need to exist where you want them to exist. It allows you to decide what contexts are relevant for exceptions. Here's an example [that I think I've given before]: function hydrate(ResultSet $rs) { try {
    $this->setName($rs->getString(1));
    $this->setEmail($rs->getString(2));
    $this->setLoginCount($rs->getInt(3));
    $this->setPicture($rs->getBlob(4));
    $this->setLastLogin($rs->getTimestamp(5));
    $this->setSignature($rs->getString(6));
    $this->setPassword($rs->getString(7));
} catch (SQLException $e) {
     throw new PropelException("Error populating Person object.", $e);
} } (btw, imagine doing that with PEAR_Error...) Of course the Person->hydrate() method can be invoked from larger blocks that have no try/catch. Applications (particularly ones that use PEAR) are built in layers. The strength of exceptions is that they move up the layers without needing to be explicitly passed. This means that very low-level exceptions can be passed up to high-level calling code with no additional effort by the developer. In some cases that is desireable, in other cases it may be more desireable to catch and rethrow if you want to provide some more information about the error. (As in the hydrate() method, which provides some contextual information to the exception so that the error doesn't simply ready "Illegal resultset offset: 1", for example). In either case, having the exception is preferrable to code that is oblivious. 3) Introducing new exceptions is not a BC break. New exceptions should be subclasses of some base exception (e.g. your application's base exception class). That causes no BC break. 4) No one is suggesting throwing WARNING or NOTICE exceptions. Warnings and notices deserve their own handling system (or trigger_error()). These have very little in common with exceptions, which are errors that need to be *handled* by the calling code. 5) Exceptions do not require more work than PEAR_Error. Both Klaus and Lukas seem to think that these Exception things are a lot of work. As I mentioned already, with PEAR_Error you are required to do just as much work, if not more. Raising an Error: [PEAR_Error]
    return PEAR::raiseError('test error', MYCLASS_ERROR_CODE);
[Exception]
    throw new Exception('test error', MYCLASS_ERROR_CODE);
Re-throwing an Error: [PEAR_Error]
     $res = $this->myMethod();
     if(PEAR::isError($res) {
       return $res;
     } else {
       // continue execution
     }
[Exception]
     $res = $this->myMethod();
Handling an Error: [PEAR_Error]
     $res = $this->myMethod();
     if(PEAR::isError($res) {
       // do something special here
     } else {
       // continue normal execution
     }
[Exception]
     try {
       $res = $this->myMethod();
       // continue execution here
     } catch (Exception $e) {
       // do something special here
     }
Hopefully that illustrates that it is never harder to catch or throw an exception than it is to handle a return-code error. Feels silly to explain this, but I do think many people need to see more examples of this stuff (and perhaps use it!!). As far as necessity of checking errors: When using PEAR_Error, if you forget to check the return type then you will have a FATAL error that is meaningless: "Fatal error: Call to undefined method PEAR_Error::foo() in .....". If you forget to catch an Exception you at least will still have a useful error: "Fatal error: Uncaught 'SQLException' with message 'Illegal resultset offset: 1' Stack trace: #0 ResultSet ...... OK, there are some more misconceptions too, I think, but this email is plenty long. Klaus suggested that PEAR group should make a decision about exceptions. I think that would be premature, as apparently the majority of the group have no experience using exceptions for error handling. I think that there are definitely reasons to be cautious of using exceptions, and I think it would be good to discuss those. E.g. use of exceptions as flow control, how packages should re-throw or wrap exceptions from other packages, guidelines for when it is appropriate to throw exceptions, how packages should subclass extensions, how they should be documented. I believe in good discussions & I have a *lot* to learn about application architecture myself. I'm not trying to say that I know the right answer, but I am trying to say that it seems like a lot of people on this list are espousing strong opinions that clearly have no basis in experience. What is really irritating is that this (i.e. ignorance) would be a basis for PEAR policy decisions! It's like the frickin' '_' prefix for protected members. It's crazy to think that the prefix requirement could have actually been enacted by people who had no experience with PHP5 (or any other lang that has protected members) with the result that subclasses would be exposing legitmate public members with '_' prefix .... !! Please folks, do everyone a favor and actually get some experience with this stuff before trying to make requirements for how the rest of us should use it! Hans

« previous php.pear.dev (#31662) next »