guillermo, out of repose

Extending CodeIgniter’s Exceptions Class

CodeIgniter, my favorite open-source PHP frame­work, pro­vides func­tions (full-on, glob­ally avail­able func­tions) for deal­ing with errors. It’s not such a bad thing, because you don’t have to worry too much about vari­able scope or any other trou­ble­some pro­gram­ming con­cepts. The down­side of hav­ing such a sim­ple approach to error han­dling, at least in CodeIgniter’s default imple­men­ta­tion, is that there isn’t much infor­ma­tion about your appli­ca­tion avail­able once an error is thrown. For exam­ple, if you’re in a con­troller and decide that http://example.com/foo/bar should only be acces­si­ble by users with cer­tain per­mis­sions (accord­ing to some authen­ti­ca­tion sys­tem you’ve set up on your own since CodeIgniter, unfor­tu­nately, doesn’t come with a built-in authen­ti­ca­tion library), you can do some­thing like the fol­low­ing in the bar() method of your Foo con­troller (assum­ing you’re rout­ing your requests using the default setup):

if (!$this->auth->is_allowed()) {
    show_error('Not for you, jack!', 401);
}

Accord­ing to the def­i­n­i­tions of the HTTP sta­tus codes, a 401 should be sent when “the request requires user authen­ti­ca­tion”. The above bit of code will send a 401 sta­tus code and dis­play the “Not for you, jack!” mes­sage in a fairly vanilla error page. What that mes­sage will NOT do, how­ever, is:

  • Log the error. You have to explic­itly call the log­ging func­tion your­self. Crazy, right?!?!
  • Pro­vide any use­ful infor­ma­tion about the error. Again, no logging.
  • Dis­play 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 com­mon head­ers or foot­ers used else­where in your appli­ca­tion because the vari­ables used in those view files won’t be in scope.

So I did some dig­ging around. As it turns out, those error han­dling func­tions instan­ti­ate and call meth­ods in an excep­tion 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 pre­fix you’re gonna use on your ver­sions of those libraries. So, if you want your own ver­sion of the excep­tion class, and your pre­fix is OOR_, you can cre­ate a file in the libraries folder under your appli­ca­tion folder (or wher­ever your appli­ca­tion 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 bang­ing on all cylin­ders. 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 fol­low­ing para­me­ters: $heading, which deter­mines what head­ing mes­sage will shown on your error page; $message, which deter­mines the mes­sage dis­played under the head­ing; $template, which deter­mines which error view to use; and $status_code, which deter­mines which HTTP sta­tus code to send in the header.

The fol­low­ing line of code is important:

$ci =& get_instance();

The get_instance() func­tion returns an instance of the CI super object (more about that in the libraries sec­tion of the CI user guide). With that super object, you have access to just about every­thing you need to grab, infor­ma­tion nor­mally avail­able in the rest of your appli­ca­tion through the con­trollers. Now, there is some­thing to be said about mak­ing your error pages blind to what­ever is going on in the rest of your appli­ca­tion (for secu­rity rea­sons), so I advise you to con­sider the impli­ca­tions of what you could poten­tially 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 awe­some the show_error() func­tion just got. You now have access to CodeIgniter’s URI library, which pro­vides infor­ma­tion about the request using a straight­for­ward API; so you can now log a use­ful error mes­sage that, in sub­se­quent log audits, will yield use­ful infor­ma­tion about that or any other error, giv­ing you a bet­ter idea of how peo­ple are actu­ally using your application.

There’s so much you can now do. The above exam­ple uses the error_general.php view for all errors, so you don’t have to worry about styling or main­tain­ing more than one error view. You can default to a dif­fer­ent sta­tus code (although I’m not sure why you’d really want to do that). You can redi­rect peo­ple to some ran­dom exter­nal page. The pos­si­bil­i­ties are endless.

Hope this helps some­one. Tootles.