DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

All Exceptions Created Equal

02.17.2006
| 3729 views |
  • submit to reddit
        All exceptions are created equal. But what if you have a very good and pressing reason to serialize one to save it in a database? For instance, you had a collection of checks you wanted to run against some data, and save the results?

Ah, that's easy! serialize() was made for that! So off you trot, you make a few changes to the code, add a blank line here, a blank line there, and suddenly your code can't find the exact matches of the serialized object in the database.

HUH? What's going on? You're the exact same Exception I just threw three minutes ago, and you've sporadically broken?

It took me a while to twig. When you create an exception ($e = new Exception("foo");), it's shiny and new and listens when you do equality comparisions (==).

But things go awry: you throw a new Exception from your filter, catch it, and serialize it. You haven't remembered that...


$a = new Exception("foo");
try {
    throw $a; //Line 1
} catch (Exception $e) {
    throw $a; //Line 10;
}


will result in two difference traces. One saying "I was thrown on on line 1", the other "I was thrown on on line 10"...

Fuck oath, hello stupid coder. You've been wracking your brains wondering why every time you go off and edit a different bit of code it serializes differently; and there you have it.

How the hell do I fix it? Going to __sleep() on the job actually helps.



<?php
class DumbException extends Exception {
    /**
     * Cleanup anything we need before serialisation
     *
     * @return  string[]    An array of member varible names to serialize
     * @see     http://php.planetmirror.com/manual/en/language.oop5.magic.php
     */
    public function __sleep() {
        return array('string','code');
    }

    /**
     * Compare against another DumbException for equality.
     *
     * Since two exceptions can be !== because the trace / line / file
     * information is different, we need to do this.
     */
    public function cmp(DumbException $e) {
        return (serialize($e) == serialize($this));
    }
}

print '<pre>';
$a = new DumbException();
$b = new DumbException();


try {
    try {
        throw $b;
    } catch (Exception $e) {
        throw $a;
    }
} catch (Exception $e) {

    var_dump($a === $b);
    var_dump($a == $b);
    var_dump($b === $e);
    var_dump($b == $e);
    var_dump($a === $e);
    var_dump($a == $e);

    var_dump($b->cmp($e));
    var_dump($a->cmp($e));
}
print '</pre>';
?>
    

Comments

Snippets Manager replied on Fri, 2006/02/10 - 8:57pm

There is a better answer: Don't store your exceptions in the database as exceptions! Exceptions that are stored in the database using serialize() are only usefull to PHP, unless you want to write a whole bunch of code to parse the serialize syntax. I store my exceptions in my error log by manually reading out the type, message, line, filename and stack trace and then storing that, along with the current UTC time, in a table with a field for each one. The resulting data is much easier to report on, query and work with. (For example, I can count the number of exceptions in a given file or on a given line using SELECT COUNT(*) instead of having to pull the data into PHP and work with it there) If you worry that you might miss some data that is specific to some fancy exception class, you may store the result of __toString() and serialize() in another two fields. IMHO this is overkill. You can also add fields to store the file and line that actually caught the exception along with where it was thrown. For example, if you have a database abstraction layer and a select command fails, the exception will tell you that it was thrown within that function. Although this data is usefull, it is often necessary to know where this exception was caught because the select command function is called from many different places. In the case of a re-usable function like that one, the error is probably in the caller. Finally, the serialize function was never intended for exceptions. The serialize function is intended for objects that represent data, like a sales order or user profile. Propert exception handling would interrogate the exception immediately apon catching it.