Magento’s default error and exception handlers provide more information than the default php handlers, as they print out a basic backtrace as opposed to just printing out the file and line number of the error but there are many times when Magento’s handlers just don’t cut it; times when you need to see the fully expanded variables that get passed to the functions/methods in the backtrace.
In the Initial Magento Setup Development Tips blog post, I wrote about how Xdebug overrides the default php error handler with a detailed, customizable error backtrace. I explained how to modify Magento’s core code so that php errors get handled by Xdebug, not Magento’s default error handler.
In this blog post, I’m going to explain how to modify Magento so that exceptions get handled by Xdebug, not Magento’s defaultexception handler.
The Mage::run() method is the method that gets the entire Magento request cycle started, so it makes sense that this is where the exceptions get handled:
/**
* Front end main entry point
*
* @param string $code
* @param string $type
* @param string|array $options
*/
public static function run($code = '', $type = 'store', $options=array())
{
try {
Varien_Profiler::start('mage');
self::setRoot();
self::$_app = new Mage_Core_Model_App();
self::$_events = new Varien_Event_Collection();
self::$_config = new Mage_Core_Model_Config();
self::$_app->run(array(
'scope_code' => $code,
'scope_type' => $type,
'options' => $options,
));
Varien_Profiler::stop('mage');
} catch (Mage_Core_Model_Session_Exception $e) {
header('Location: ' . self::getBaseUrl());
die();
} catch (Mage_Core_Model_Store_Exception $e) {
require_once(self::getBaseDir() . DS . 'errors' . DS . '404.php');
die();
} catch (Exception $e) {
if (self::isInstalled() || self::$_isDownloader) {
self::printException($e);
exit();
}
try {
self::dispatchEvent('mage_run_exception', array('exception' => $e));
if (!headers_sent()) {
header('Location:' . self::getUrl('install'));
} else {
self::printException($e);
}
} catch (Exception $ne) {
self::printException($ne, $e->getMessage());
}
}
}
The problem with the above code is that any exceptions that get thrown by any Magento code get handled by the Mage::printException() method. What we need to do is run the essential code without wrapping it in any try/catch blocks if the developer mode is enabled from the index.php. This will result in all exceptions being handled by the Xdebug exception handler. Here is the resulting code:
/**
* Front end main entry point
*
* @param string $code
* @param string $type
* @param string|array $options
*/
public static function run($code = '', $type = 'store', $options=array())
{
if(!self::getIsDeveloperMode()){
try {
Varien_Profiler::start('mage');
self::setRoot();
self::$_app = new Mage_Core_Model_App();
self::$_events = new Varien_Event_Collection();
self::$_config = new Mage_Core_Model_Config();
self::$_app->run(array(
'scope_code' => $code,
'scope_type' => $type,
'options' => $options,
));
Varien_Profiler::stop('mage');
} catch (Mage_Core_Model_Session_Exception $e) {
header('Location: ' . self::getBaseUrl());
die();
} catch (Mage_Core_Model_Store_Exception $e) {
require_once(self::getBaseDir() . DS . 'errors' . DS . '404.php');
die();
} catch (Exception $e) {
if (self::isInstalled() || self::$_isDownloader) {
self::printException($e);
exit();
}
try {
self::dispatchEvent('mage_run_exception', array('exception' => $e));
if (!headers_sent()) {
header('Location:' . self::getUrl('install'));
} else {
self::printException($e);
}
} catch (Exception $ne) {
self::printException($ne, $e->getMessage());
}
}
} else { // If we're running in developer mode, we want all exceptions to be handled by the php|xdebug error handler
Varien_Profiler::start('mage');
self::setRoot();
self::$_app = new Mage_Core_Model_App();
self::$_events = new Varien_Event_Collection();
self::$_config = new Mage_Core_Model_Config();
self::$_app->run(array(
'scope_code' => $code,
'scope_type' => $type,
'options' => $options,
));
Varien_Profiler::stop('mage');
}
}
This code allows Xdebug’s exception handler to do its magic, resulting in fully detailed exception backtraces like this:
Hopefully, this little trick allows you to code Magento more effectively!