Page 1 of 1

Error handling strategy

Posted: Tue Jan 10, 2006 4:02 am
by Ree
Lately I've been thinking about error handling throughout the whole application (the whole class hierarchy). Note: I'm not talking about development error handling/debugging (with line indications, logging, paths and any other stuff) as I don't see much point in implementing that. What I'm interested in is end-user error handling, that is, errors (or situations, conditions) that happen when the finished application is used. Some examples: 'Unable to connect to db', 'Requested record doesn't exist', 'Invalid input' and so on. Currently in all classes where some error(s) could occur, I have $error property and getError() method. That means I have the same error methods in my db connection class, authorization class, validation classes...

For example, you have two classes - a db connection, and a class to get records from the db. The db object can produce 'unable to connect', 'SQL error' etc.

Code: Select all

class DB
{
  var $error;

  function DB()
  {
    $this->error = '';
  }

  /* ... */

  function getError()
  {
    return $this->error;
  }
}

Code: Select all

class DataFetcher
{
  var $error;
  var $db;

  function DataFetcher
  {
    $this->db = &new DB();
    $this->error = '';
  }

  /* ... */


  function fetch()
  {
    /* ... */

    //if db error occured...
    $this->error = $this->db->getError();

    /* ... */
  }

  function getError()
  {
    return $this->error;
  }
}

Code: Select all

$fetcher = &new DataFetcher();
$fetcher->fetch();

//if an error occured
$error = $fetcher->getError();
That's a very basic example, which is based on what I have been using. Basically, if some error occurs 'deep' in the object hierarchy, it must be brought up via the same getError() method... thus each class involved must have the method, which could be used by the classes 'above'. In the example above, DataFetcher calls getError() of DB, later the script calls getError() of DataFetcher. It's just an example, but there might be more levels of the hierarchy. This does work, however, there might be a better (more OO? :)) way to do it. What strategy do you use?

Posted: Tue Jan 10, 2006 4:07 am
by Jenk
Up till now I have just used trigger_error() and set_error_handler() and a custom error handler - usually with a template error page to display the error.

I prefer errors to make themselves known, rather than depend on me havint to look to see if there was an error - people are lazy and forgetful and thus if anyone uses your classes, they might forget or neglect to check if there was an error with $error = $obj->getError();

Posted: Tue Jan 10, 2006 5:01 am
by Maugrim_The_Reaper
I'm probably similar - I use trigger_error() wherever its an obvious fatal error in the application. If an error occurs it should be reported (well, logged if on a production server).

I'm currently trying to find a PHP Logging Library with little success - the three good ones are either under a license incompatible with GPL, or specific to PHP5. Logging (for any reason) is usually pretty useful. I hope to migrate the trigger_error calls to a logger error() method - give me a lot more control over how errors are handled - I should for example be able to set up fatal errors to be emailed to the Tech team, rather than sitting in a log unaddressed until someone looks...

There's still a place for custom error handling in the sense you describe - you may not want all errors to be reported, or some errors may be expected and non-fatal, etc. So long as error so get either reported or logged somehow it should be fine.

Posted: Tue Jan 10, 2006 5:11 am
by Ree
I probably should have emphasized one thing. The errors that I am talking about are not considered fatal - they're special conditions/situations rather than 'errors'. For example, 'unauthorized access', 'requested record not found' - those happen at user level when the application functions properly and in no way they are fatal and I don't want/need to log such errors - only display them to the user in one way or another.

Re: Error handling strategy

Posted: Tue Jan 10, 2006 6:54 am
by BDKR
Ree wrote: It's just an example, but there might be more levels of the hierarchy. This does work, however, there might be a better (more OO? :)) way to do it. What strategy do you use?
How about using a Singleton? A single instance of the error object that all other objects have a handle to. phpPatterns.com should have a bit about it.

Cheers

Posted: Tue Jan 10, 2006 7:46 am
by Ree
I would rather stay away from globals.

Posted: Tue Jan 10, 2006 8:00 am
by John Cartwright
There is nothing wrong with using a singleton. Yes it is a global, but it does not have the shortcomings of a global.

Posted: Tue Jan 10, 2006 8:07 am
by BDKR
Jcart wrote:There is nothing wrong with using a singleton. Yes it is a global, but it does not have the shortcomings of a global.
Exactly! The amount of overheat and complexity needed without using a Singleton is what you should be trying to avoid.

Posted: Tue Jan 10, 2006 8:40 am
by Ree
Jcart wrote:There is nothing wrong with using a singleton. Yes it is a global, but it does not have the shortcomings of a global.
It does have its problems. Some good points can be found here.

Posted: Tue Jan 10, 2006 8:42 am
by Jenk
I'm looking forward to PHP5 becoming the standard platform, I already make use of it's try-catch-throw methods (coming from a Java background, so of course I love it,) but am still restricted to trigger_error() for multi-platform applications.

trigger_error() doesn't just incur fatal errors, it's used specifically for user errors, thus your error handler, should you develop one, be designed to handle such errors appropriately.

In other words - an error is an error. It's the level/type of error that defines what should happen, not if an error should occur at all. Be it a E_USER_NOTICE or an E_USER_FATAL, they both needing handling. :)

Posted: Tue Jan 10, 2006 9:39 am
by Maugrim_The_Reaper
That's largely what a logger can do... Sorry I'm obsessed with logging today - I spent 3 days hunting down PHP Loggers and found nothing I could use under the GPL for PHP4. I'm now facing writing my own logging library. I'm having difficulty believing there's not a gem of a PHP Logger just waiting out there somewhere...

What a logger can do however is be used within your own error handler to take highly specific actions based on the level of the error from either within PHP, or via trigger_error(). So I could set up a logger to send me an HTML formatted description of any FATAL errors via emails, and log any lower levels to a file or the database. Of course the logger could just as easily output the error to the browser (with a nice debug_backtrace() scheme attached) just as easily.

Definitely logger mad today...;)

Posted: Tue Jan 10, 2006 9:50 am
by raghavan20
as presented as an example in PHP manual, it is one of the better ways to handle errors. It talks about writing a function with a case statement for E_USER_ERROR, E_USER_WARNING, etc., and making the same function as the error handler function using the set_error_handler(); this might be good to catch errors sometimes you fail to realize that they would happen...

trigger_error() can be used to implement business logic,,,if you expect something or same value to occur and there is a deviation, you can raise errors.


But the problem is, at more grained level, how you are going to identify which exception is it...which of these NumberFormatException, ArrayOutOfBounds, IOException, NullPointerException, etc., has occured....I do not think PHP 4 atleast has distinction between exception or clearly says that some distinct exception has occured.

I still have this doubt, how do you guys can validate whether a mysql connection has been established without using something like this...

Code: Select all

if ($dbConnection = mysql_connect(,,))
else
trigger_error();
Something should be like Java where if you try to use dbConnection anywhere else it raises NullPointerException.

Do you guys have ever seen a list of possible exceptions that can occur with PHP...a complete list of all exceptions?

Posted: Tue Jan 10, 2006 10:07 am
by Jenk
the problem with comparing Java to PHP is they are, for a start, almost completely different. Everything in Java is an object, or static class.

When reading the man pages for Java classes, such as InputStreamReader, you will see the line "throws IOException".

If you look at the source of the InputStreamReader class, you will notice a line:

Code: Select all

throw new IOException;
so you will need to catch the IOException and handle accordingly. Normally with a "file not found" error message or such.

In PHP, this is a different case, as the throw-catch has not been available to PHP developers as long as it has in Java, thus hardly any functions or methods available to us make use of it.

For now atleast, it may seem tedious to do it this way.. but in the long run I think it is a much better way of handling errors, or rather, handling errors in an OO fashion.

Last year I wrote my first PHP class that utilises the throw-catch methodology - funnily enough it is a MySQL DB class - which is probably the first class every PHP developer writes :P - I was using lines such as:

Code: Select all

<?php 

class MySQLException extends Exception {}

class MySQLDB
{
    function DoSomething ()
    {
        if (!mysql_connect($blah, $blah, $blah)) {
            throw new MySQLException('Cannae connect to DB Cap\'in!');
        }
    }
}

try {
    MySQLDB::DoSomething();
} catch (MySQLException $e) {
    echo $e->getMessage(); //output's Cannae connect to DB Cap'in! and continues
} catch (Exception $e) {
    die('Fatal Error: ' . $e->getMessage()); //dies
}
?>
Now, the nice bit about this method of error handling is I can specify in my doc's/comments that my method DoSomething throws a MySQLException - which is a bit more specific than just a generic E_USER_WARNING or such. :)

--

The difference between throw/catch and PHP's function trigger_error() is that the method of detecting what type/level of error is not based upon what arbitrary argument you provide a function, but on what type of error has been thrown.


This is slowly but surely happening in PHP5+.

From Zend's PHP-101:

Code: Select all

try { 
    execute this block 
} 
catch (exception type 1) { 
    execute this block to resolve exception type 1 
} 
catch (exception type 2) { 
    execute this block to resolve exception type 2 
} 
... and so on ...

Posted: Tue Jan 10, 2006 10:15 am
by raghavan20
not necessarily i think, that classes can only provide distinct exceptions....
normal procedure-oriented programming should also be able to identify each unique exception...identifying an error as IOError or NullPointerException does not necessarily require that classes could only do it....even a procedure-oriented language like PHP 4 could do it....

Posted: Tue Jan 10, 2006 10:31 am
by Jenk
you are quite right - as you can probably see from the now edited multiple times jumble of a post my previous reply is, I hadn't quite spat out what I wanted to spit out.

Anyway - try-catch does offer the ability to make very specific exceptions, rather than band them in a level, which is the key point.

The only issue I can see with using it in a procedural fashion is purely the amount of coding the developer would have to type.

If the project dictates it is a procedural solution, then it wouldbe better to handle errors in such a way:

Code: Select all

<?php

mysql_connect('blah', 'blah', 'blah') or die('Cannae connect to DB Cap\'in!');

//instead of:
class MySQLException extends Exception {}

try {
    if (!mysql_connect('blah', 'blah', 'blah')) {
        throw new MySQLException('Cannae connect to DB Cap\'in!');
    }
} catch (MySQLException $e) {
    echo $e->getMessage();
}
?>
My previous point was because the native functions to PHP do not utilise the try-catch-throw commands, we as the developers have to make up for that. Notably in the if(!mysql_connect) challenge in the above example - but as time goes by, we will hopefully see classes and functions developed that use this method of error handling, in libraries such as PEAR. (a good example is at the bottom of the Zend PHP-101 link)