Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Extract base class for exception rendering.
Extract base class + template method for exception handling, implement
it in both the console and web error handlers.
  • Loading branch information
markstory committed Aug 22, 2013
1 parent fda5907 commit c3225d3
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 131 deletions.
12 changes: 3 additions & 9 deletions lib/Cake/Console/ConsoleErrorHandler.php
Expand Up @@ -14,10 +14,8 @@
*/
namespace Cake\Console;

use Cake\Core\Configure;
use Cake\Error\FatalErrorException;
use Cake\Error\BaseErrorHandler;
use Cake\Error\ErrorHandler;

/**
* Error Handler for Cake console. Does simple printing of the
Expand Down Expand Up @@ -53,12 +51,12 @@ public function __construct($options = []) {
}

/**
* Handle a exception in the console environment. Prints a message to stderr.
* Prints an exception to stderr.
*
* @param Exception $exception The exception to handle
* @return integer Exit code from exception caught.
* @return void
*/
public function handleException(\Exception $exception) {
protected function _displayException($exception) {
$errorName = __d('cake_console', 'Error:');
if ($exception instanceof FatalErrorException) {
$errorName = __d('cake_console', 'Fatal Error:');
Expand All @@ -72,10 +70,6 @@ public function handleException(\Exception $exception) {
$exception->getTraceAsString()
);
$this->_stderr->write($message);
return $exception->getCode() ?: 1;
}

protected function _displayException($exception) {
}

/**
Expand Down
2 changes: 0 additions & 2 deletions lib/Cake/Console/ShellDispatcher.php
Expand Up @@ -21,8 +21,6 @@

/**
* Shell dispatcher handles dispatching cli commands.
*
* @package Cake.Console
*/
class ShellDispatcher {

Expand Down
69 changes: 67 additions & 2 deletions lib/Cake/Error/BaseErrorHandler.php
Expand Up @@ -16,6 +16,7 @@

use Cake\Core\Configure;
use Cake\Log\Log;
use Cake\Routing\Router;
use Cake\Utility\Debugger;

/**
Expand Down Expand Up @@ -84,14 +85,30 @@ public function handleError($code, $description, $file = null, $line = null, $co
if ($debug) {
$data += [
'context' => $context,
'start' => 2,
'start' => 3,
'path' => Debugger::trimPath($file)
];
}
$this->_displayError($data, $debug);
$this->_logError($log, $data);
}

/**
* Handle uncaught exceptions.
*
* Uses a template method provided by subclasses to display errors in an
* environment appropriate way.
*
* @param \Exception $exception
* @return void
* @throws Exception When renderer class not found
* @see http://php.net/manual/en/function.set-exception-handler.php
*/
public function handleException($exception) {
$this->_displayException($exception);
$this->_logException($exception);
}

/**
* Display/Log a fatal error.
*
Expand Down Expand Up @@ -148,6 +165,55 @@ protected function _logError($level, $data) {
return Log::write($level, $message);
}

/**
* Handles exception logging
*
* @param Exception $exception
* @return boolean
*/
protected function _logException($exception) {
$config = $this->_options;
if (empty($config['log'])) {
return false;
}

if (!empty($config['skipLog'])) {
foreach ((array)$config['skipLog'] as $class) {
if ($exception instanceof $class) {
return false;
}
}
}
return Log::error($this->_getMessage($exception));
}

/**
* Generates a formatted error message
*
* @param Exception $exception Exception instance
* @return string Formatted message
*/
protected function _getMessage($exception) {
$message = sprintf("[%s] %s",
get_class($exception),
$exception->getMessage()
);
if (method_exists($exception, 'getAttributes')) {
$attributes = $exception->getAttributes();
if ($attributes) {
$message .= "\nException Attributes: " . var_export($exception->getAttributes(), true);
}
}
if (php_sapi_name() !== 'cli') {
$request = Router::getRequest();
if ($request) {
$message .= "\nRequest URL: " . $request->here();
}
}
$message .= "\nStack Trace:\n" . $exception->getTraceAsString();
return $message;
}

/**
* Map an error code into an Error word, and log location.
*
Expand Down Expand Up @@ -190,5 +256,4 @@ public static function mapErrorCode($code) {
return array($error, $log);
}


}
129 changes: 20 additions & 109 deletions lib/Cake/Error/ErrorHandler.php
Expand Up @@ -17,9 +17,6 @@
namespace Cake\Error;

use Cake\Core\App;
use Cake\Core\Configure;
use Cake\Log\Log;
use Cake\Routing\Router;
use Cake\Utility\Debugger;

/**
Expand Down Expand Up @@ -107,90 +104,6 @@ public function __construct($options = []) {
$this->_options = array_merge($defaults, $options);
}

/**
* Set as the default exception handler by the CakePHP bootstrap process.
*
* This will either use custom exception renderer class if configured,
* or use the default ExceptionRenderer.
*
* @param \Exception $exception
* @return void
* @throws Exception When renderer class not found
* @see http://php.net/manual/en/function.set-exception-handler.php
*/
public function handleException(\Exception $exception) {
$config = $this->_options;
self::_log($exception, $config);

$renderer = isset($config['exceptionRenderer']) ? $config['exceptionRenderer'] : 'Cake\Error\ExceptionRenderer';
$renderer = App::classname($renderer, 'Error');
try {
if (!$renderer) {
throw new \Exception("$renderer is an invalid class.");
}
$error = new $renderer($exception);
$error->render();
} catch (\Exception $e) {
// Disable trace for internal errors.
$this->_options['trace'] = false;
$message = sprintf("[%s] %s\n%s", // Keeping same message format
get_class($e),
$e->getMessage(),
$e->getTraceAsString()
);
trigger_error($message, E_USER_ERROR);
}
}

/**
* Generates a formatted error message
*
* @param Exception $exception Exception instance
* @return string Formatted message
*/
protected static function _getMessage($exception) {
$message = sprintf("[%s] %s",
get_class($exception),
$exception->getMessage()
);
if (method_exists($exception, 'getAttributes')) {
$attributes = $exception->getAttributes();
if ($attributes) {
$message .= "\nException Attributes: " . var_export($exception->getAttributes(), true);
}
}
if (php_sapi_name() !== 'cli') {
$request = Router::getRequest();
if ($request) {
$message .= "\nRequest URL: " . $request->here();
}
}
$message .= "\nStack Trace:\n" . $exception->getTraceAsString();
return $message;
}

/**
* Handles exception logging
*
* @param Exception $exception
* @param array $config
* @return boolean
*/
protected static function _log(\Exception $exception, $config) {
if (empty($config['log'])) {
return false;
}

if (!empty($config['skipLog'])) {
foreach ((array)$config['skipLog'] as $class) {
if ($exception instanceof $class) {
return false;
}
}
}
return Log::write('error', self::_getMessage($exception));
}

/**
* Display an error.
*
Expand All @@ -210,31 +123,29 @@ protected function _displayError($error, $debug) {
}

/**
* Generate an error page when some fatal error happens.
* Displays an exception response body.
*
* @param integer $code Code of error
* @param string $description Error description
* @param string $file File on which error occurred
* @param integer $line Line that triggered the error
* @return boolean
* @param \Exception $exception The exception to display
* @return void
*/
public function handleFatalError($code, $description, $file, $line) {
$logMessage = 'Fatal Error (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']';
Log::write(LOG_ERR, $logMessage);

if (ob_get_level()) {
ob_end_clean();
}

if (Configure::read('debug')) {
$this->handleException(new FatalErrorException($description, 500, $file, $line));
} else {
$this->handleException(new InternalErrorException());
}
return false;
}

protected function _displayException($exception) {
$renderer = App::classname($this->_options['exceptionRenderer'], 'Error');
try {
if (!$renderer) {
throw new \Exception("$renderer is an invalid class.");
}
$error = new $renderer($exception);
$error->render();
} catch (\Exception $e) {
// Disable trace for internal errors.
$this->_options['trace'] = false;
$message = sprintf("[%s] %s\n%s", // Keeping same message format
get_class($e),
$e->getMessage(),
$e->getTraceAsString()
);
trigger_error($message, E_USER_ERROR);
}
}

}
1 change: 0 additions & 1 deletion lib/Cake/Error/ExceptionRenderer.php
Expand Up @@ -25,7 +25,6 @@
use Cake\Network\Response;
use Cake\Routing\Router;
use Cake\Utility\Inflector;
use Cake\View\View;

/**
* Exception Renderer.
Expand Down
12 changes: 4 additions & 8 deletions lib/Cake/Test/TestCase/Console/ConsoleErrorHandlerTest.php
Expand Up @@ -84,8 +84,7 @@ public function testCakeErrors() {
$this->stderr->expects($this->once())->method('write')
->with($this->stringContains($message));

$result = $this->Error->handleException($exception);
$this->assertEquals(404, $result);
$this->Error->handleException($exception);
}

/**
Expand All @@ -99,8 +98,7 @@ public function testNonCakeExceptions() {
$this->stderr->expects($this->once())->method('write')
->with($this->stringContains('Too many parameters.'));

$result = $this->Error->handleException($exception);
$this->assertEquals(1, $result);
$this->Error->handleException($exception);
}

/**
Expand All @@ -114,8 +112,7 @@ public function testError404Exception() {
$this->stderr->expects($this->once())->method('write')
->with($this->stringContains('dont use me in cli.'));

$result = $this->Error->handleException($exception);
$this->assertEquals(404, $result);
$this->Error->handleException($exception);
}

/**
Expand All @@ -129,8 +126,7 @@ public function testError500Exception() {
$this->stderr->expects($this->once())->method('write')
->with($this->stringContains('dont use me in cli.'));

$result = $this->Error->handleException($exception);
$this->assertEquals(500, $result);
$this->Error->handleException($exception);
}

}

0 comments on commit c3225d3

Please sign in to comment.