There are some techniques that I use in almost all of my projects, but I hardly see in other projects. To my opinion others are missing out, so I’ll discuss a few of them. This time I want to talk about exceptions. If you are already using exceptions, the first 2 paragraph won’t be anything new to you, but the third one might.
Why you should use exceptions
I often see something like this:
$db = new mysqli('localhost', 'user', 'pwd', 'mydb');
...
function activateCustomer($id) {
$result = $db->query("UPDATE customer SET status='ACTIVE' WHERE id = " . (int)$id) or die("Query failed: " . $db->error);
...
}
I know that an example like this even made it into the PHP documentation, but I don’t like it at all. When an error occurs, you want the error handler to pick it up. The handler might simply display the error, which is fine for the test environment, but in a live environment it should mail you the error and display a ‘Sorry, unexpected error’ message to the user. This is not possible when using die().
Instead you can use trigger_error($msg, E_USER_ERROR). However this will still end the execution of the script, while the failure might cause an action to while, while the page can still be displayed. We can solve this nicer with an exception.
function activateCustomer($id) {
$result = $db->query("UPDATE customer SET status='ACTIVE' WHERE id = " . (int)$id);
if (!$result) throw new Exception("Query failed: " . $db->error);
...
}
If the exceptions isn’t caught, it will just produce a fatal error. However, in this example, only the action fails on a query error, the page can probably be rendered. So we can catch the exception and display a ‘Sorry, action failed’ message.
try {
activateCustomer($_GET['id']);
} catch (Exception $e) {
mailExceptionToSiteadmin($e); # a user function that mails the error info to you
echo "Sorry, an unexpected error occurred when activating the customer";
}
...
Catching specific exceptions
In the previous example I threw and caught a general Exception. However in some cases you only want to catch certain exceptions. Have a look at the following example.
class Item_Exception extends Exception {}
class ItemNotFound_Exception extends Item_Exception {}
try {
...
if (!isset($items[$_GET['id'])) throw new ItemNotFound_Exception("Could not find item '{$_GET['id']}'");
} catch (Item_Exception $e) {
echo "This item can't be used: " . $e->getMessage();
}
...
Using interface to specify exceptions
Until so far, nothing new here. The examples above cover 90% of all the situations. However at times, you need to specify than an exception can be cached in certain occasions while it is already extending a class. To solve this we can use an interface. PHP actually uses something like ‘instanceof’ with the catch, so instead of using a class name, we can also use an interface.
interface InputFault {}
class Item_Exception extends Exception {}
class ItemNotFound_Exception extends Item_Exception implements InputFault {}
try {
...
if (!isset($items[$_GET['id'])) throw new ItemNotFound_Exception("Could not find item '{$_GET['id']}'");
} catch (InputFault $e) {
echo "Invalid data was entered: " . $e->getMessage();
} catch (Exception $e) {
mailExceptionToSiteadmin($e); # a user function that mails the error info to you
echo "Sorry, Something went wrong";
}
...