Josh
Josh

Reputation: 12566

When should you use PHP Exceptions?

I've seen lots of tutorials that demo simple try catches with, say, the act of opening a file. But I've never seen a big, "real" example. Can someone provide me with a few cases in which they have or would use exceptions? And is it really necessary to extend the exception class just to throw an exception? And finally, when throwing exceptions, does it cause the script to exit(); ? Or, does it log it and continue on with script execution?

Upvotes: 15

Views: 8147

Answers (7)

erenon
erenon

Reputation: 19118

Exceptions are meant to handle errors (at least in PHP). Suppose you are in a routine, and an error is occurred that you can't handle in the current context.

Example:

<?php
/**
 * @throws Exception_NoFile
 */
function read_file($filePath) {
    if(!file_exists($filePath)) {
        throw new Exception_NoFile($filePath);
    }

    /* ... nominal case */
}

In this situation you can't continue with the nominal case, because there is no file to process. You have to choose:

  • return with an invalid return value (this is the C practice, e.g: return -1 or using status flags)

  • throw an exception, and hope, someone will catch it above. If your client code excepts it, no problem, it may try an other path or re-throw an exception. If your client isn't ready to handle those situations where the requested file doesn't exist... your code will fail with an uncaught exception, as it would do with a read of a non-existing file in the other approach.

Upvotes: 4

Stan Ivanov
Stan Ivanov

Reputation: 329

Exception handling is tricky. It requires careful consideration of the project at hand and the way errors can be dealt with. You should try to define your Exceptions Guidelines early in your project and adhere to it.

I have written a general Exceptions Guidelines best practices that I have come up with after extensive research of the subject. Most of these guidelines can be used to all projects in any language that supports exceptions. Some of the guidelines will be Java specific. At the end you need to have a robust set of guidelines that can help you handle and deal with exceptions and error conditions.

Here are some points to consider

Don’t expose internal, implementation specific details to your clients

Avoid exposing internal implementation specific exceptions to your clients, especially those contained in a third party library. This is a general object oriented rule of thumb and it’s as valid for your exceptions hierarchy design. You have no control over the third party library which can change its exceptions signatures and break all of your API contracts with your clients. Instead wrap those third party exceptions (such as an SQLException) in your own custom exceptions. This way you’ll have much greater flexibility to change the third party library in the future without breaking your clients’ API contract.

Create your own exceptions hierarchy for complex projects

Generally speaking create your own exceptions hierarchy for more complex modules especially if you are dealing with implementation specific exceptions in third party libraries. Each of your packages/modules could have its own top-level generic exceptions. For Java at least one should be defined that inherits from RuntimeException. Wrap all implementation specific exceptions in your custom exceptions so that your clients should only depend on your custom exceptions and/or generic Java exceptions. This will give you greater flexibility to refactor the implementation specific code later without breaking your API contracts.

If more fine grained error handling is necessary then you can further subclass your custom exceptions to handle specific cases and allow for error recovery. For example if you are connecting to an SQL database you can throw a ConnectionTimeoutException in such a way that if needed the client can retry the connection N times before giving up. This way you can later change your database engine to NoSQL and still allow for reconnects and the client code will stay the same.

Document all exceptions

Carefully document all exceptions your package/module/app throws in the javadoc definition of each public method. Failing to do so will frustrate your API users and cause them to not trust your API docs. You don’t really want your clients to dig in your source just to find out that you are throwing a specific exception, right?

Throw exceptions as early as possible.

Check all inputs to your public API methods and throw an exception as soon as you find inconsistencies between your expected parameters and what has been supplied. The earlier you throw an exception the less will be the chance of data corruption, because bad data won’t make it into the deeper parts of your code. It also gives valuable feedback to your clients in a timely manner instead of deep in your code where something throws an obscure exception with a bad message such as ‘Internal Error’ or NullPointerException.

Log exceptions properly

Follow the guidelines of your logging framework in order to properly log exceptions with their message and stack trace. You don’t want to loose either

Upvotes: 5

Aditya Mittal
Aditya Mittal

Reputation: 1771

I feel a lot of people confuse 'failures' and 'exceptions' as the same thing. The word 'error' could refer to either but I use it for failure.

Failure - when the operation doesn't succeed

Exception - when an unexpected or out of the normal flow condition arises

For example, if a robot tries to walk to a destination and misses the mark - that's failure. But if it breaks a leg or the roof falls on it, that's an exception.

If the roof falls, I throw an exception that the roof fell.

If the robot misses the mark, I don't throw an Exception, I return false or return an error message like "Could not reach the destination because the roof fell."

try {
  Walk to the cabinet;
}
catch (RoofFell_Exception $e) {
  return "Could not reach the destination because the roof fell.";
}
catch (Legbroke_Exception $e) {
  return "Could not reach the destination because a leg broke.";
}

if($current_location == 'cabinet') {
  return "Destination reached";
}
return false;

Upvotes: 8

Anthony Rutledge
Anthony Rutledge

Reputation: 7564

A great place to use exception handling is when your program attempts to connect to, or access, I/O (files, databases, networking, devices).

  1. Use exception handling when a block of calling code (function/method) attempts to access a file.

  2. Use exception handling when a block of calling code (function/method) attempts a database connection.

  3. Use exception handling when a block of calling code (function/method) attempts to run a query on a database (any attempt at accessing the database tables/views and such).

  4. You could say the same about a network connection or access.

Memory access requires I/O (that includes storing things in $_SESSION files), but most beginners don't put their entire program within the confines of a try...catch structure. A good example of the use of exceptions and extending the Exception class can be found in Matt Doyle's book Beginning PHP 5.3, ch. 20, p. 652-60.

I might also say that learning to combine the use of exception handling with set_error_handler(), trigger_error(), error_log() inside of the catch block can allow you to preserve custom, developer friendly, error messages you may have been echoing to your output device (browser/stdout) in development. That is, in production, your php.ini will have display_errors off and log_errors will be on. Where you may have echoed something like "Oops, I cannot connect to the accounting database" when a connection to said database failed, simply send that same string of text to error_log() and your personal error messages can still be logged.

Example:

function custom_handler($arg1, $arg2, $arg3, $arg4, $arg5)
{
  ...
  ...
  error_log(blah, blah, blah)
}

set_error_handler('custom_handler');  <--takes over this duty from PHP
$error = NULL;

try
{
     if(!connect_to_mythical_database('accounting'))
     {
          $error = 'I cannot connect to the accounting database';
          throw new Exception(supply-correct-arguments);
     }
}
catch (Exception $e)
{
     trigger_error(supply-correct-arguments); <-- does what 'custom_handler' instructs.
     error_log($error, blah, blah); <---Your friendly message here
     header('Location: http://www.myhomepage.com');
     exit;
}

Note: Inside of custom_handler(), you can use error_log() to log PHP error messages in a file, email them, or both log and email the standard PHP error messages. Thus, trigger_error() is either controlled by PHP (default) or by your 'custom_handler', which can implement error_log()', all of which can be activated by thecatch` block of your exception handler.

Upvotes: 0

German Rumm
German Rumm

Reputation: 5812

You should check out symfony framework - they really use a lot of Exceptions there.

They use Exception for configuration errors, say you forgot to put a file where the controller expects to find it - this will be an Exception, because there isn't anything framework can do it about it.

They use Exception for unknown errors: database failed for some weird reason, there's nothing framework can do about it - so it throws an Exception

And they have different Exception handlers for different environments. When exception occurs in "devel" mode, you get a nice page with stack trace and an explanation, when you are in "production" mode, you are redirect to custom 500 page.

Upvotes: 4

Jonathan Beebe
Jonathan Beebe

Reputation: 5226

We use exceptions extensively within our projects.

One specific instance is for actions that require the user to be logged in or upon registration. We use Exceptions for flow control on error conditions. If the current user is not logged in we throw an exception. The exception handler then redirects them to the loggin page.

Using our registration action as an example, we extend the Exception like this:

class RegistrationFailed extends Exception {}

Now in our catch statement within the registration code we can test for the RegistrationFailed exception and handle it accordingly. Otherwise, when the exception is not a RegistrationFailed, we allow it to bubble up because we are not interested in it.

try {
    // do registration here
}
catch(RegistrationFailed $e) {
    // handle the failed registration
}
catch(SomeOtherException $e) {
    // handle other errors like this...
}

// All other errors will not be caught and will bubble up

Another example is within our wrapper classes which developers must extended. We use Reflection to ensure the child classes have properly implemented their methods and provided the correct interface. If not we notify the developer of that class via Exceptions letting them know a specific interface or method must be provided by the child class.


Edit: I can already hear the comments about "You shouldn't use error handling for flow control!" however, for the project discussed above, it was necessary.

In the normal flow of the program a failed registration is expected due to the many validation rules that might fail, like a password that's too short.

However, it's an ajax application, so it's possible that someone might try to access an ajax url manually, when they are not logged in. This is as exception and thus we handle it as such.

Upvotes: 9

DampeS8N
DampeS8N

Reputation: 3621

Exceptions are simply a way to move edge cases or errors (which are really just big edge-case events) out of the larger body of code to stop them from making 99% of the basic stream of code cluttered with tons of switches/ifs.

You can think of them as a kind of reverse switch statement, where the events inside the try{} determine which, if any, catch block happens too.

What that means is that you don't ever have to use them if you don't like them. But they can make code easier to read.

Upvotes: 0

Related Questions