Skip to content

Commit

Permalink
Implementing a backwards compatible way of handling PHP 7 Errors
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzo committed Nov 15, 2015
1 parent f91dea1 commit 8410fd4
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 8 deletions.
29 changes: 27 additions & 2 deletions src/Error/BaseErrorHandler.php
Expand Up @@ -18,6 +18,8 @@
use Cake\Log\Log;
use Cake\Routing\Router;
use Exception;
use Error;
use Cake\Error\PHP7ErrorException;

/**
* Base error handler that provides logic common to the CLI + web
Expand Down Expand Up @@ -65,7 +67,7 @@ public function register()
}
error_reporting($level);
set_error_handler([$this, 'handleError'], $level);
set_exception_handler([$this, 'handleException']);
set_exception_handler([$this, 'wrapAndHandleException']);
register_shutdown_function(function () {
if (PHP_SAPI === 'cli') {
return;
Expand Down Expand Up @@ -139,6 +141,22 @@ public function handleError($code, $description, $file = null, $line = null, $co
return true;
}

/**
* Checks the passed exception type. If it is an instance of `Error`
* then, it wraps the passed object inside another Exception object
* for backwards compatibility purposes.
*
* @param Exception|Error $exception The exception to handle
* @return void
*/
public function wrapAndHandleException($exception)
{
if ($exception instanceof Error) {
$exception = new PHP7ErrorException($exception);
}
$this->handleException($exception);
}

/**
* Handle uncaught exceptions.
*
Expand Down Expand Up @@ -231,13 +249,17 @@ protected function _logError($level, $data)
protected function _logException(Exception $exception)
{
$config = $this->_options;
$unwrapped = $exception instanceof PHP7ErrorException ?
$exception->getError() :
$exception;

if (empty($config['log'])) {
return false;
}

if (!empty($config['skipLog'])) {
foreach ((array)$config['skipLog'] as $class) {
if ($exception instanceof $class) {
if ($unwrapped instanceof $class) {
return false;
}
}
Expand All @@ -253,6 +275,9 @@ protected function _logException(Exception $exception)
*/
protected function _getMessage(Exception $exception)
{
$exception = $exception instanceof PHP7ErrorException ?
$exception->getError() :
$exception;
$config = $this->_options;
$message = sprintf(
"[%s] %s",
Expand Down
30 changes: 24 additions & 6 deletions src/Error/ExceptionRenderer.php
Expand Up @@ -19,6 +19,7 @@
use Cake\Core\Configure;
use Cake\Core\Exception\Exception as CakeException;
use Cake\Core\Exception\MissingPluginException;
use Cake\Error\PHP7ErrorException;
use Cake\Event\Event;
use Cake\Network\Exception\HttpException;
use Cake\Network\Request;
Expand Down Expand Up @@ -91,6 +92,18 @@ public function __construct(Exception $exception)
$this->controller = $this->_getController();
}

/**
* Returns the unwrapped exception object in case we are dealing with
* a PHP 7 Error object
*
* @param \Exception $exception The object to unwrap
* @return \Exception|\Error
*/
protected function _unwrap($exception)
{
return $exception instanceof PHP7ErrorException ? $exception->getError() : $exception;
}

/**
* Get the controller instance to handle the exception.
* Override this method in subclasses to customize the controller used.
Expand Down Expand Up @@ -143,12 +156,13 @@ public function render()
$code = $this->_code($exception);
$method = $this->_method($exception);
$template = $this->_template($exception, $method, $code);
$unwrapped = $this->_unwrap($exception);

$isDebug = Configure::read('debug');
if (($isDebug || $exception instanceof HttpException) &&
method_exists($this, $method)
) {
return $this->_customMethod($method, $exception);
return $this->_customMethod($method, $unwrapped);
}

$message = $this->_message($exception, $code);
Expand All @@ -161,21 +175,21 @@ public function render()
$viewVars = [
'message' => $message,
'url' => h($url),
'error' => $exception,
'error' => $unwrapped,
'code' => $code,
'_serialize' => ['message', 'url', 'code']
];
if ($isDebug) {
$viewVars['trace'] = Debugger::formatTrace($exception->getTrace(), [
$viewVars['trace'] = Debugger::formatTrace($unwrapped->getTrace(), [
'format' => 'array',
'args' => false
]);
$viewVars['_serialize'][] = 'trace';
}
$this->controller->set($viewVars);

if ($exception instanceof CakeException && $isDebug) {
$this->controller->set($this->error->getAttributes());
if ($unwrapped instanceof CakeException && $isDebug) {
$this->controller->set($unwrapped->getAttributes());
}
return $this->_outputMessage($template);
}
Expand Down Expand Up @@ -205,6 +219,7 @@ protected function _customMethod($method, $exception)
*/
protected function _method(Exception $exception)
{
$exception = $this->_unwrap($exception);
list(, $baseClass) = namespaceSplit(get_class($exception));

if (substr($baseClass, -9) === 'Exception') {
Expand All @@ -224,7 +239,8 @@ protected function _method(Exception $exception)
*/
protected function _message(Exception $exception, $code)
{
$message = $this->error->getMessage();
$exception = $this->_unwrap($exception);
$message = $exception->getMessage();

if (!Configure::read('debug') &&
!($exception instanceof HttpException)
Expand All @@ -249,6 +265,7 @@ protected function _message(Exception $exception, $code)
*/
protected function _template(Exception $exception, $method, $code)
{
$exception = $this->_unwrap($exception);
$isHttpException = $exception instanceof HttpException;

if (!Configure::read('debug') && !$isHttpException) {
Expand Down Expand Up @@ -285,6 +302,7 @@ protected function _template(Exception $exception, $method, $code)
protected function _code(Exception $exception)
{
$code = 500;
$exception = $this->_unwrap($exception);
$errorCode = $exception->getCode();
if ($errorCode >= 400 && $errorCode < 506) {
$code = $errorCode;
Expand Down

0 comments on commit 8410fd4

Please sign in to comment.