LogoPhly, boy, phly
the weblog and site of Matthew Weier O'Phinney

Wednesday, July 6. 2005

Notes on PEAR_Exception

I've been doing some thinking on exceptions, and PEAR_Exception in particular. You may want to skip ahead to read about how to use PEAR_Exception, as well as some of my thoughts on the class on first use. If you want the background, read on.

I've created a package proposal on PEAR for a package called File_Fortune, an OOP interface to reading and writing fortune files. I've been using a perl module for this on the family website for years, and now that I'm starting work on the PHP conversion, I thought I'd start with the building blocks.

In creating the proposal, I started with a PHP5-only version, though I found that I wasn't using much in PHP5 beyond the public/private/protected/static keywords. For error handling, I decided to try out PEAR_ErrorStack, as I'd been hearing buzz about it being the new "preferred" method for error handling in PEAR. (Honestly, after using it, I'm not too happy with it; throwing PEAR_Errors was much easier, and easier to manipulate as well -- but that's a subject for another post -- and exceptions were easier still, though more typing.)

The first comment I got on the proposal was the question: "Why PHP5?" (Paul wasn't too surprised by that reaction.) I thought about it, and decided it wasn't really all that necessary, beyond the fact that I'd need to take some extra steps to be able to actually test a PHP4 version. So, I did a PHP4 version.

Well, then some chatter happened, and a number of developers said, "Why not PHP5?" So, I went back to PHP5. And then somebody else said, "Use PEAR_Exception." So, I started playing with that, and we finally get to the subject of this post.

Exception handling is one of the advances PHP5 brought to PHP. I was very excited to have it available, as I'm accustomed to exception handling in perl (which is actually quite different than PHP's model, but the basics are the same). When I saw the suggestion to use it, I realized that exception handling would make the package solidly a PHP5 package. Simultaneously, I wondered why it hadn't occurred to me. Guess I've been coding more PHP than perl for a while now...

The problem is that there's very little documentation on PEAR_Exception, and the tips I got on list, while helpful in getting my proposal out the door, left me with a lot of questions.

For those who haven't used PEAR_Exception, here's the basics:

  • Create a file in which to hold your exceptions classes. (Yes, plural; I'll get to that). If you're developing a PEAR-style package, you want to put it in the directory pertaining to your package name. So, since I was developing File_Fortune, my exception class became File/Fortune/Exception.php.
  • Create a base exception class for your class that extends PEAR_Exception:
            class File_Fortune_Exception extends PEAR_Exception
            {
            }
        
    Note: it doesn't override anything. It just creates a pseudo-namespace.
  • For each unique exception type, extend your base class:
            class File_Fortune_FileException extends File_Fortune_Exception
            {
            }
    
            class File_Fortune_HeaderException extends File_Fortune_Exception
            {
            }
        
    (Yes, you should create docblocks for each, describing their purpose.)
  • In your code, throw exceptions instead of raising errors:
            if (false === ($fh = fopen($filename))) {
                throw new File_Fortune_FileException('Unable to open file');
            }
        
  • In your phpDoc blocks, use '@throws Exception_Class_Name' with some descriptive text

And that's it in a nutshell.

The beauty of it is that you can really separate errors from return values -- errors are no longer a possible return value:

    try {
        $fortune = $ff->getRandom();
    } catch (File_Fortune_Exception $e) {
        echo "Couldn't get fortune: " . $e->getMessage();
    }

The problem I saw with the system is that you end up with a bunch of exception classes, each of which has a veeeerrryyy looooonnnnngggg name, which leads to lots of typing, the possibility for typos (I had one in the version I used for the call to votes), and the possibility for more error handling than code, depending on the number of possible exceptions and how carefully you want to check for them:

    try {
        $fortune = $ff->getRandom();
    } catch (File_Fortune_BadHeaderFileException $e) {
        echo "Could not parse header file";
    } catch (File_Fortune_HeaderFileException $e) {
        echo "Could not open header file";
    } catch (File_Fortune_BadFileException $e) {
        echo "Badly formed fortune file";
    } catch (File_Fortune_Exception $e) {
        // Catch-all for File_Fortune errors...
        echo "Couldn't get fortune: " . $e->getMessage();
    }

I got to thinking that there must be a better way. I haven't actually come up with one yet, though. My idea so far, however, is to have a single exception class, and in it define a number of class constants or statics -- much like PEAR_Error/PEAR_ErrorStack, where they map to integer values -- and to have these values map to actual error messages (which could possibly be localized within the class as well). Then, when throwing an error, it might be something like:

    if (false === ($fh = fopen($filename))) {
        throw new File_Fortune_Exception(1);
    } 
    // or
    if (false === ($fh = fopen($filename))) {
        throw new File_Fortune_Exception(File_Fortune_Exception::FILE);
    } 

The constructor would be overridden to set the code and message based on the code passed (if a string was passed, that would be the message). Then you could test for a single exception class, and use $e->getCode() to check for the type if you need more fine-grained control.

I'd be more than happy to discuss possibilities. Exceptions are a fantastic way to check for truly exceptional behaviour in code; in PHP5, they also seem to be incredibly fast and lightweight (though I have no substantive data to back that statement, other than API responsiveness). I'd like to see more people developing with them.

On that note, what do other PHP develpers think of exception handling? I've heard some say it's too 'goto-ish' (I'm not sure I follow that train of thought), others prefer the simplicity of PEAR_Error. Leave a comment!

Posted by Matthew Weier O'Phinney in PHP at 22:26 | Comments (7) | Trackbacks (0)

Trackbacks
Trackback specific URI for this entry

No Trackbacks

Comments
Display comments as (Linear | Threaded)

Hey Matthew -- of course I have to say something about this. ;-)

I can't say I'm a fan of exceptions; but to be honest, I have not used them much. The goto-ish-ness of their apparent use does rub my fur the wrong way.

At the same time, PEAR_Error is easy, but not robust enough; and while PEAR_ErrorStack looks to solve the ills of PEAR_Error, I never did get my head wrapped around it properly.

Like you, I thought there had to be an easier way. My solution for the time being appears to be the Solar_Error class (there's the Solar plug we knew was coming) ;-) and it has handled everything I've thrown at it so far. It's as easy to work with as PEAR_Error, and provides stacking of errors like PEAR_ErrorStack.

It doesn't do exceptions, though. Perhaps an exception-based version of the Solar_Error class would be an answer for you?

Documentation on Solar_Error is here, for anyone interested:

http://solarphp.com/home/?area=Solar_Error
#1 Paul M. Jones (Link) on 2005-07-07 00:38 (Reply)
Hey, Matthew -- as a followup, you may also wish to look at Davey Shafik's solution in Cerebral Cortex.

http://crtx.org/index.php?page=CrtxErrorStack
#1.1 Paul M. Jones (Link) on 2005-07-10 11:36 (Reply)
you can simply catch the parent class just as well if you dont care about finely graining your exception catching. i dont really see where you are saving characters in your solution. all you are doing is saving exception classes in favor of constants.

anyways .. exceptions suck in the php world, since they prevent quick and dirty prototyping, since they force you to fix issues, where as previously all you had would be an out of wack display or a warning you could hide when you show off the prototype you now have a fatal error you have to handle.

*sigh*
#2 Lukas on 2005-07-07 01:55 (Reply)
Regarding using the parent class to catch exceptions, I'm aware that that's possible, and it's even how I'm doing things. It just makes me wonder how useful it is to have an exception class per error type, when I could just as easily parse the message returned by the exception in many cases.

Regarding fatal errors: I was not aware that exceptions generated fatal errors if uncaught. I just tested this myself, and saw what you meant -- and then saw that this is the intended behaviour by visiting the PHP docs.

Hmmm... In perl, you can do something like: eval { #some code here } if ($!) { # caught exception } -- but the beauty is that if you don't do the eval block or test for the exception, then you'll get your normal runtime or compile-time errors. It looks like there's no equivalent in PHP5, which makes them a bit less attractive.
#2.1 Matthew Weier O'Phinney (Link) on 2005-07-07 12:37 (Reply)
Your naming strategy is wrong, otherwise nice article
It should be
class File_Fortune_Exception_File extends File_Fortune_Exception { }
class File_Fortune_Exception_Header extends File_Fortune_Exception {}

Otherwise the file->class mapping doesnt really work..

Having just reverted some PHP5 exception code back to PHP4 pear, I'd be very tempted to just have one excpetion class as well..
#3 Alan Knowles (Link) on 2005-07-07 02:09 (Reply)
I did the naming based on what somebody on pear-dev suggested. I myself thought it was odd, but went ahead with it. I'll change it for the official release (assuming I get enough votes).
#3.1 Matthew Weier O'Phinney (Link) on 2005-07-07 09:11 (Reply)
Great article, thanks!
#4 Nola (Link) on 2005-07-07 11:10 (Reply)

Add Comment

Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

 
 
  • Home
  • Resume
  • Blog
  • Phly PEAR Channel
  • Contact Me
  • About this site

ZCE

Zend Education Advisory Board Member

Add to Technorati Favorites

Calendar

Back January '09 Forward
Mon Tue Wed Thu Fri Sat Sun
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

Quicksearch

Links

  • PHLY - PHp LibrarY
  • Paul M. Jones
  • Mike Naberezny
  • Shahar Evron
  • Planet PHP
  • Zend Where I now work
  • Garden.org Where I once worked

Archives

January 2009
December 2008
November 2008
Recent...
Older...

Categories

XML Linux
XML Personal
XML Aikido
XML Family
XML Programming
XML Dojo
XML Perl
XML PHP

All categories

Syndicate This Blog

XML RSS 0.91 feed
XML RSS 1.0 feed
XML RSS 2.0 feed
ATOM/XML ATOM 0.3 feed
ATOM/XML ATOM 1.0 feed
XML RSS 2.0 Comments

Show tagged entries

xml best practices
xml books
xml conferences
xml dojo
xml dpc08
xml file_fortune
xml linux
xml mvc
xml oop
xml pear
xml perl
xml personal
xml php
xml phpworks08
xml programming
xml ubuntu
xml vim
xml webinar
xml zendcon
xml zendcon08
xml zend framework
© 2004 - present, Matthew Weier O'Phinney
matthew-web <at> weierophinney.net