Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In PHP5, should I use Exceptions or trigger_error/set_error_handler? [closed]

Tags:

exception

php

People also ask

What is the use of trigger_error ()?

The trigger_error() function creates a user-level error message. The trigger_error() function can be used with the built-in error handler, or with a user-defined function set by the set_error_handler() function.

What is use of set_error_handler in PHP?

The set_error_handler() function sets a user-defined error handler function. Note: The standard PHP error handler is completely bypassed if this function is used, and the user-defined error handler must terminate the script, die(), if necessary.


If you want to use exceptions instead of errors for your entire application, you can do it with ErrorException and a custom error handler (see the ErrorException page for a sample error handler). The only downside to this method is that non-fatal errors will still throw exceptions, which are always fatal unless caught. Basically, even an E_NOTICE will halt your entire application if your error_reporting settings do not suppress them.

In my opinion, there are several benefits to using ErrorException:

  1. A custom exception handler will let you display nice messages, even for errors, using set_exception_handler.
  2. It does not disrupt existing code in any way... trigger_error and other error functions will still work normally.
  3. It makes it really hard to ignore stupid coding mistakes that trigger E_NOTICEs and E_WARNINGs.
  4. You can use try/catch to wrap code that may generate a PHP error (not just exceptions), which is a nice way to avoid using the @ error suppression hack:

    try {
        $foo = $_GET['foo'];
    } catch (ErrorException $e) {
        $foo = NULL;
    }
    
  5. You can wrap your entire script in a single try/catch block if you want to display a friendly message to your users when any uncaught error happens. (Do this carefully, because only uncaught errors and exceptions are logged.)


You should use exceptions in "Exceptional circumstances", that is when you call a method doFoo() you should expect it to perform, if for some reason doFoo is unable to do it's job then it should raise an exception.

A lot of old php code would take the approach of returning false or null when a failure has occured, but this makes things hard to debug, exceptions make this debugging much easier.

For example say you had a method called getDogFood() which returned an array of DogFood objects, if you called this method and it returns null when something goes wrong how will your calling code be able to tell whether null was returned because there was an error or there is just no dog food available?

Regarding dealing with legacy code libraries that use php's inbuilt error logging, you can override the error logging with the set_error_handler() function, which you could use to then rethrow a generic Exception.

Now that you have all of your code throwing detailed exceptions, you are free to decide what to do with them, in some parts of your code you may wish to catch them and try alternative methods or you can log them using your own logging functions which might log to a database, file, email - whichever you prefer. In short - Exceptions are more flexible .


I love the idea of using exceptions, but I often have third party libraries involved, and then if they don't use exceptions you end up with 3-4 different approaches to the problem! Zend uses exceptions. CakePHP uses a custom error handler, and most PEAR libraries use the PEAR::Error object.

I which there WAS one true way in this regard. The custom error handlers route is probably the most flexible in this situation. Exceptions are a great idea though if you're either only using your own code, or using libraries that use them.

Unfortunately in the PHP world we're still suffering from the refusal to die of PHP4, so things like exceptions, while they may represent best practise have been incredibly slow to catch on while everyone is still writing things to be able to work in both 4 and 5. Hopefully this debacle is now ending, though by the time it does, we'll have tensions between 6 and 5 instead...

/me holds head in hands...


It depends on the situation. I tend to use Exceptions when I am writing business logic/application internals, and trigger_error for Validator's and things of that sort.

The pro's of using Exceptions at the logic level is to allow your application to do in case of such an error. You allow the application to chose instead of having the business logic know how to present the error.

The pro's of using trigger_error for Validator's and things of that nature are, say,

try {
    $user->login();
}  catch (AuthenticationFailureException $e) {
    set_error_handler("my_login_form_handler");
    trigger_error("User could not be logged in. Please check username and password and try again!");
} catch (PersistenceException $pe) { // database unavailable
    set_error_handler("my_login_form_handler"); 
    trigger_error("Internal system error. Please contact the administrator.");
}

where my_login_form_handler pretties up the string and places the element in a visible area above the login form.


Obviously, there's no "One Right Way", but there's a multitude of opinions on this one. ;)

Personally i use trigger_error for things the exceptions cannot do, namely notices and warnings (i.e. stuff you want to get logged, but not stop the flow of the application in the same way that errors/exceptions do (even if you catch them at some level)).

I also mostly use exceptions for conditions that are assumed to be non-recoverable (to the caller of the method in which the exception occurs), i.e. serious errors. I don't use exceptions as an alternative to returning a value with the same meaning, if that's possible in a non-convoluted way. For example, if I create a lookup method, I usually return a null value if it didn't find whatever it was looking for instead of throwing an EntityNotFoundException (or equivalent).

So my rule of thumb is this:

  • As long as not finding something is a reasonable result, I find it much easier returning and checking for null-values (or some other default value) than handling it using a try-catch-clause.
  • If, on the other hand, not finding it is a serious error that's not within the scope of the caller to recover from, I'd still throw an exception.

The reason for throwing exceptions in the latter case (as opposed to triggering errors), is that exceptions are much more expressive, given that you use properly named Exception subclasses. I find that using PHP's Standard Library's exceptions is a good starting point when deciding what exceptions to use: http://www.php.net/~helly/php/ext/spl/classException.html

You might want to extend them to get more semantically correct exceptions for your particular case, however.


Intro

In my personal experience, as a general rule, I prefer to use Exceptions in my code instead of trigger_error. This is mainly because using Exceptions is more flexible than triggering errors. And, IMHO, this is also beneficial not only for myself as for the 3rd party developer.

  1. I can extend the Exception class (or use exception codes) to explicitly differentiate the states of my library. This helps me and 3rd party developers in handling and debugging the code. This also exposes where and why it can fail without the need for source code browsing.
  2. I can effectively halt the execution of my Library without halting the execution of the script.
  3. The 3rd party developer can chain my Exceptions (in PHP > 5.3.*) Very useful for debugging and might be handy in handling situations where my library can fail due to disparate reasons.

And I can do all this without imposing how he should handle my library failures. (ie: creating complex error handling functions). He can use a try catch block or just use an generic exception handler

Note:

Some of these points, in essence, are also valid for trigger_error, just a bit more complex to implement. Try catch blocks are really easy to use and very code friendly.


Example

I think this is an example might illustrate my point of view:

class HTMLParser {
    protected $doc;
    protected $source = null;
    public $parsedHtml;
    protected $parseErrors = array();
    public function __construct($doc) {
        if (!$doc instanceof DOMDocument) {
            // My Object is unusable without a valid DOMDOcument object
            // so I throw a CriticalException
            throw new CriticalException("Could not create Object Foo. You must pass a valid DOMDOcument object as parameter in the constructor");
        }
        $this->doc = $doc;
    }

    public function setSource($source) {
        if (!is_string($source)) {
            // I expect $source to be a string but was passed something else so I throw an exception
            throw new InvalidArgumentException("I expected a string but got " . gettype($source) . " instead");
        }
        $this->source = trim($source);
        return $this;
    }

    public function parse() {
        if (is_null($this->source) || $this->source == '') {
            throw new EmptyStringException("Source is empty");
        }
        libxml_use_internal_errors(true);
        $this->doc->loadHTML($this->source);
        $this->parsedHtml = $this->doc->saveHTML();
        $errors = libxml_get_errors();
        if (count($errors) > 0) {
            $this->parseErrors = $errors;
            throw new HtmlParsingException($errors[0]->message,$errors[0]->code,null,
                $errors[0]->level,$errors[0]->column,$errors[0]->file,$errors[0]->line);
        }
        return $this;
    }

    public function getParseErrors() {
        return $this->parseErrors;
    }

    public function getDOMObj() {
        return clone $this->doc;
    }
}

Explanation

In the constructor I throw a CriticalException if the param passed is not of type DOMDocument because without it my library will not work at all.

(Note: I could simply write __construct(DOMDocument $doc) but this is just an example).

In setsource() method I throw a InvalidArgumentException if the param passed is something other than a string. I prefer to halt the library execution here because source property is an essential property of my class and an invalid value will propagate the error throughout my library.

The parse() method is usually the last method invoked in the cycle. Even though I throw a XmlParsingException if libXML finds a malformed document, the parsing is completed first and the results usable (to an extent).


Handling the example library

Here's an example how to handle this made up library:

$source = file_get_contents('http://www.somehost.com/some_page.html');
try {
    $parser = new HTMLParser(new DOMDocument());
    $parser->setSource($source)
           ->parse();
} catch (CriticalException $e) {
    // Library failed miserably, no recover is possible for it.
    // In this case, it's prorably my fault because I didn't pass
    // a DOMDocument object.
    print 'Sorry. I made a mistake. Please send me feedback!';
} catch (InvalidArgumentException $e) {
    // the source passed is not a string, again probably my fault.
    // But I have a working parser object. 
    // Maybe I can try again by typecasting the argument to string
    var_dump($parser);
} catch (EmptyStringException $e) {
    // The source string was empty. Maybe there was an error
    // retrieving the HTML? Maybe the remote server is down?
    // Maybe the website does not exist anymore? In this case,
    // it isn't my fault it failed. Maybe I can use a cached
    // version?
    var_dump($parser);
} catch (HtmlParsingException $e) {
    // The html suplied is malformed. I got it from the interwebs
    // so it's not my fault. I can use $e or getParseErrors() 
    // to see if the html (and DOM Object) is usable
    // I also have a full functioning HTMLParser Object and can
    // retrieve a "loaded" functioning DOMDocument Object
    var_dump($parser->getParseErrors());
    var_dump($parser->getDOMObj());
}
$var = 'this will print wether an exception was previously thrown or not';
print $var;

You can take this further and nest try catch blocks, chain exceptions, run selective code following a determined exception chain path, selective logging, etc...

As a side note, using Exceptions does not mean that the PROGRAM execution will halt, it just means that the code depending of my object will be bypassed. It's up to me or the 3rd party developer to do with it as he pleases.


The idea of exception is elegant and makes the error handling process so smooth. but this only applies when you have appropriate exception classes and in team development, one more important thing is "standard" exceptions. so if you plan to use exceptions, you'd better first standardize your exception types, or the better choice is to use exceptions from some popular framework. one other thing that applies to PHP (where you can write your code object orienter combined with structural code), is that if you are writing your whole application using classes. If you are writing object oriented, then exceptions are better for sure. after all I think your error handling process will be much smoother with exception than trigger_error and stuff.