MainelyDesign.com Blog

Controlling CakePHP Error Handling When Debug = 0

Posted on 04/19/2010 at 04:27 pm by Kevin Wentworth
Viewed 28,180 times | 1 comment

I finally had a client request a piece of functionality that required me to program CakePHP so I could better control the default error handling when the site is in production mode (i.e. debug is set to 0).  By default, CakePHP will throw a 404 page not found header whenever ANY errors occur on a production site with debug equal to zero.  This works great, most of the time.  But what about when you are having database connectivity issues?  Say when there are too many mysql connections to that overloaded shared hosting box?  Modifed CakePHP error handling to the rescue.

Override Default AppError Functionality

By default, when your website has a debug level of 0, CakePHP will automatically use AppError::error404() to communicate error messages.  This is done to prevent any sensitive data being released through error messages.  However, if you want to actually use error methods other than error404() on a production site, you must do a little trick, I picked up:

  1. function __construct($method, $messages) {
  2.         Configure::write('debug', 1);
  3.         parent::__construct($method, $messages);
  4.     }

This basically makes your site work as if Configure::write('debug', 0); but sets the erorr class to have a debug of 1, so different messages can be used and not just the 404 error message function.

Hijack CakePHP's Default Error Handling

The next step is hijacking all the CakePHP error methods that you need and make them do what you want.  For me, this was the missing table and missing database errors.  But, I also wanted to check the 404 error and make sure there wasn't database connectivity issues (My 404 page requires a database lookup, so it wouldn't work if there wasn't any DB connection).  I decided to make a few functions that would simplify the code:

Checking for DB Connectivity: (Thanks to Miles)

  1. function _testDBConnectivity() {
  2.         Configure::write('debug', 0); //hide messages in case DB isn't available
  3.         $this->DB = ConnectionManager::getDataSource('db_connection_name');
  4.         if(!$this->DB->isConnected()) {
  5.             return false;
  6.         } else {
  7.             return true;
  8.         }
  9.     }

Hooking into missingTable() and missingDatabase():

Notice the _genericErrorMessage(); calls in the first line of each function.

  1. function missingTable($params) {
  2.         $this->_genericErrorMessage();  //won't return unless debug is > 0
  3.         extract($params, EXTR_OVERWRITE);
  4.  
  5.         $this->controller->set(array(
  6.             'model' => $className,
  7.             'table' => $table,
  8.             'title' => __('Missing Database Table', true)
  9.         ));
  10.         $this->_outputMessage('missingTable');
  11.     }
  12.    
  13.     function missingDatabase($params = array()) {
  14.         $this->_genericErrorMessage();  //won't return unless debug is > 0
  15.         $this->controller->set(array(
  16.             'title' => __('Scaffold Missing Database Connection', true)
  17.         ));
  18.         $this->_outputMessage('missingScaffolddb');
  19.     }

Hooking into error404() (needed to force the message, b/c I know an error has happened):

If DB fails, show the genericErrorMessage() and force it to show, even if debug isn't 0

  1. function error404($params) {
  2.         if(!$this->_testDBConnectivity()) {
  3.             $this->_genericErrorMessage(true);  //won't return unless debug is > 0
  4.         }
  5.         extract($params, EXTR_OVERWRITE);
  6.  
  7.         if (!isset($url)) {
  8.             $url = $this->controller->here;
  9.         }
  10.         $url = Router::normalize($url);
  11.         header("HTTP/1.0 404 Not Found");      
  12.        
  13.         $this->controller->set(array(
  14.             'code' => '404',
  15.             'name' => __('Not Found', true),
  16.             'message' => h($url),
  17.             'base' => $this->controller->base
  18.         ));
  19.        
  20.        
  21.         $this->__outputMessage('error404');
  22.        
  23.     }

Function to output customized error message when database connectivity goes down:

  1. //checks public debug level setting and "hijacks" errors if public debug level is set to 0
  2.     //....and no DB connection, otherwise cakephp's error message show (for development)
  3.  
  4.     function _genericErrorMessage($override = false) {
  5.         $publicDebugLevel = Configure::read('public.debugLevel');
  6.         if($publicDebugLevel == 0 or $override) {
  7.             ob_clean(); //eliminate all previous warnings (b/c debug is set to 1)
  8.             header('HTTP/1.1 500 Internal Server Error');
  9.             Configure::write('debug', 0);   //eliminate all future warnings
  10.             $this->controller->layout = Configure::read('siteLayout');  //set layout
  11.             $this->controller->theme = Configure::read('siteTheme');    //set theme
  12.             $this->_outputMessage('genericError');  //output generic error message
  13.             die();  //make this the last thing done in app call
  14.         }
  15.     }

Keeping the Search Engines in Mind

I'm sending a 500 error along with the customized error message, because I want search engines to know there was an issue with the site, not a 404 page missing error.  Hopefully, this will help eliminate the risk of search engines thinking the page is gone for good.

Wrapping Up CakPHP Error Handling Overrides

What I like most about this approach is the ability to still use the CakePHP error handling when your site isn't in production mode.  And, when it's in production mode it will show a 404 error message unless it's a database error. One difference between my setup and the default CakePHP setup, is that I have an additional configuration variable that I use for setting the debug level (it's part of CMS logic).  You cannot use just CakePHP's debug setting, because it will be set to 1 by the __constructor() function of the AppError class.

Cheers,
-Kevin

Bookmark and Share

Tags for Controlling CakePHP Error Handling When Debug = 0

Cakephp | Web Programming | Seo | Php | Mysql | Database | Example | Tutorial

Comments for this Posting

Posted by chad

on 15/9/10

I updated the error404() in my AppError class to start with:


// When CakePHP's 'debug' is set to 0 for production mode the
// Cake library ignores the method name you passed to the
// cakeError() function. Instead it calls this error404()
// function.
//
// To be able to call our own error views in production mode
// we pass the error name in using the $params argument and
// then here we check for that parameter name and call the
// correct method.
//
// Example: to call the soapLookupFailed() method call
// cakeError() via:
//
// $error_params = array('error_method' => 'soapLookupFailed');
// $this->cakeError('soapLookupFailed', $error_params);

if(array_key_exists('error_method', $params)) {
call_user_func(array($this, $params['error_method']), $params);
return;
}

Sorry, comments are closed for this posting.

Please Email Kevin if you have any questions. Thanks!

Meet Site Avenger - Hosted Content Management System

Powered By: Site Avenger | Site Production: Saco Design