Extending CodeIgniter’s Exceptions Class
CodeIgniter, my favorite open-source PHP framework, provides functions (full-on, globally available functions) for dealing with errors. It’s not such a bad thing, because you don’t have to worry too much about variable scope or any other troublesome programming concepts. The downside of having such a simple approach to error handling, at least in CodeIgniter’s default implementation, is that there isn’t much information about your application available once an error is thrown. For example, if you’re in a controller and decide that http://example.com/foo/bar should only be accessible by users with certain permissions (according to some authentication system you’ve set up on your own since CodeIgniter, unfortunately, doesn’t come with a built-in authentication library), you can do something like the following in the bar() method of your Foo controller (assuming you’re routing your requests using the default setup):
if (!$this->auth->is_allowed()) {
show_error('Not for you, jack!', 401);
}
According to the definitions of the HTTP status codes, a 401 should be sent when “the request requires user authentication”. The above bit of code will send a 401 status code and display the “Not for you, jack!” message in a fairly vanilla error page. What that message will NOT do, however, is:
- Log the error. You have to explicitly call the logging function yourself. Crazy, right?!?!
- Provide any useful information about the error. Again, no logging.
- Display a page that looks like the rest of your site. You can, of course, style the error views any way you want, but you can’t include common headers or footers used elsewhere in your application because the variables used in those view files won’t be in scope.
So I did some digging around. As it turns out, those error handling functions instantiate and call methods in an exception class named CI_Exceptions (note that it’s “CI_Exceptions” and not “CI_Exception”). CodeIgniter let’s you extend and/or replace native libraries, so long as you tell CI what prefix you’re gonna use on your versions of those libraries. So, if you want your own version of the exception class, and your prefix is OOR_, you can create a file in the libraries folder under your application folder (or wherever your application logic lives) named OOR_Exceptions.php. In that file, you can include the following:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class OOR_Exceptions extends CI_Exceptions
{
}
You, my friend, are banging on all cylinders. Let’s add some functionality.
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class OOR_Exceptions extends CI_Exceptions
{
public function show_error($heading, $message, $template = '', $status_code = 500)
{
$ci =& get_instance();
if (!$page = $ci->uri->uri_string()) {
$page = 'home';
}
switch($status_code) {
case 403: $heading = 'Access Forbidden'; break;
case 404: $heading = 'Page Not Found'; break;
case 503: $heading = 'Undergoing Maintenance'; break;
}
log_message('error', $status_code . ' ' . $heading . ' --> '. $page);
return parent::show_error($heading, $message, 'error_general', $status_code);
}
}
CI_Exceptions::show_error() takes the following parameters: $heading, which determines what heading message will shown on your error page; $message, which determines the message displayed under the heading; $template, which determines which error view to use; and $status_code, which determines which HTTP status code to send in the header.
The following line of code is important:
$ci =& get_instance();
The get_instance() function returns an instance of the CI super object (more about that in the libraries section of the CI user guide). With that super object, you have access to just about everything you need to grab, information normally available in the rest of your application through the controllers. Now, there is something to be said about making your error pages blind to whatever is going on in the rest of your application (for security reasons), so I advise you to consider the implications of what you could potentially be doing and whether or not this sort of code-wrangling is right for you. If you’re OK with doing this, then notice of much more awesome the show_error() function just got. You now have access to CodeIgniter’s URI library, which provides information about the request using a straightforward API; so you can now log a useful error message that, in subsequent log audits, will yield useful information about that or any other error, giving you a better idea of how people are actually using your application.
There’s so much you can now do. The above example uses the error_general.php view for all errors, so you don’t have to worry about styling or maintaining more than one error view. You can default to a different status code (although I’m not sure why you’d really want to do that). You can redirect people to some random external page. The possibilities are endless.
Hope this helps someone. Tootles.