Browse Source

Add types to error package classes.

I've removed the PHP7Error class as it is no longer needed as we don't
have to be compatible with PHP5 anymore. I've also changed all the
\Exception typehints to \Throwable as that is more appropriate and
possible now.
Mark Story 7 years ago
parent
commit
7d1e0694ce

+ 23 - 23
src/Error/BaseErrorHandler.php

@@ -20,6 +20,7 @@ use Cake\Log\Log;
 use Cake\Routing\Router;
 use Error;
 use Exception;
+use Throwable;
 
 /**
  * Base error handler that provides logic common to the CLI + web
@@ -53,7 +54,7 @@ abstract class BaseErrorHandler
      * @param bool $debug Whether or not the app is in debug mode.
      * @return void
      */
-    abstract protected function _displayError($error, $debug);
+    abstract protected function _displayError(array $error, bool $debug): void;
 
     /**
      * Display an exception in an environment specific way.
@@ -61,17 +62,17 @@ abstract class BaseErrorHandler
      * Subclasses should implement this method to display an uncaught exception as
      * desired for the runtime they operate in.
      *
-     * @param \Exception $exception The uncaught exception.
+     * @param \Throwable $exception The uncaught exception.
      * @return void
      */
-    abstract protected function _displayException($exception);
+    abstract protected function _displayException(Throwable $exception): void;
 
     /**
      * Register the error and exception handlers.
      *
      * @return void
      */
-    public function register()
+    public function register(): void
     {
         $level = -1;
         if (isset($this->_options['errorLevel'])) {
@@ -79,7 +80,7 @@ abstract class BaseErrorHandler
         }
         error_reporting($level);
         set_error_handler([$this, 'handleError'], $level);
-        set_exception_handler([$this, 'wrapAndHandleException']);
+        set_exception_handler([$this, 'handleException']);
         register_shutdown_function(function () {
             if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && $this->_handled) {
                 return;
@@ -129,7 +130,7 @@ abstract class BaseErrorHandler
      * @param array|null $context Context
      * @return bool True if error was handled
      */
-    public function handleError($code, $description, $file = null, $line = null, $context = null)
+    public function handleError(int $code, string $description, ?string $file = null, ?int $line = null, ?array $context = null): bool
     {
         if (error_reporting() === 0) {
             return false;
@@ -167,14 +168,13 @@ abstract class BaseErrorHandler
      * then, it wraps the passed object inside another Exception object
      * for backwards compatibility purposes.
      *
-     * @param \Exception|\Error $exception The exception to handle
+     * @param \Throwable $exception The exception to handle
      * @return void
+     * @deprecated 4.0.0 Unused method will be removed in 5.0
      */
-    public function wrapAndHandleException($exception)
+    public function wrapAndHandleException(Throwable $exception): void
     {
-        if ($exception instanceof Error) {
-            $exception = new PHP7ErrorException($exception);
-        }
+        deprecationWarning('This method is no longer in use. Call handleException instead.');
         $this->handleException($exception);
     }
 
@@ -184,12 +184,12 @@ abstract class BaseErrorHandler
      * Uses a template method provided by subclasses to display errors in an
      * environment appropriate way.
      *
-     * @param \Exception $exception Exception instance.
+     * @param \Throwable $exception Exception instance.
      * @return void
      * @throws \Exception When renderer class not found
      * @see https://secure.php.net/manual/en/function.set-exception-handler.php
      */
-    public function handleException(Exception $exception)
+    public function handleException(Throwable $exception): void
     {
         $this->_displayException($exception);
         $this->_logException($exception);
@@ -218,7 +218,7 @@ abstract class BaseErrorHandler
      * @param int $line Line that triggered the error
      * @return bool
      */
-    public function handleFatalError($code, $description, $file, $line)
+    public function handleFatalError(int $code, string $description, string $file, int $line): bool
     {
         $data = [
             'code' => $code,
@@ -241,7 +241,7 @@ abstract class BaseErrorHandler
      * @param int $additionalKb Number in kilobytes
      * @return void
      */
-    public function increaseMemoryLimit($additionalKb)
+    public function increaseMemoryLimit(int $additionalKb): void
     {
         $limit = ini_get('memory_limit');
         if (!strlen($limit) || $limit === '-1') {
@@ -267,11 +267,11 @@ abstract class BaseErrorHandler
     /**
      * Log an error.
      *
-     * @param string $level The level name of the log.
+     * @param int|string $level The level name of the log.
      * @param array $data Array of error data.
      * @return bool
      */
-    protected function _logError($level, $data)
+    protected function _logError($level, array $data): bool
     {
         $message = sprintf(
             '%s (%s): %s in [%s, line %s]',
@@ -301,10 +301,10 @@ abstract class BaseErrorHandler
     /**
      * Handles exception logging
      *
-     * @param \Exception $exception Exception instance.
+     * @param \Throwable $exception Exception instance.
      * @return bool
      */
-    protected function _logException(Exception $exception)
+    protected function _logException(Throwable $exception): bool
     {
         $config = $this->_options;
         $unwrapped = $exception instanceof PHP7ErrorException ?
@@ -332,7 +332,7 @@ abstract class BaseErrorHandler
      * @param \Cake\Http\ServerRequest $request The request to read from.
      * @return string
      */
-    protected function _requestContext($request)
+    protected function _requestContext($request): string
     {
         $message = "\nRequest URL: " . $request->getRequestTarget();
 
@@ -351,10 +351,10 @@ abstract class BaseErrorHandler
     /**
      * Generates a formatted error message
      *
-     * @param \Exception $exception Exception instance
+     * @param \Throwable $exception Exception instance
      * @return string Formatted message
      */
-    protected function _getMessage(Exception $exception)
+    protected function _getMessage(Throwable $exception): string
     {
         $exception = $exception instanceof PHP7ErrorException ?
             $exception->getError() :
@@ -394,7 +394,7 @@ abstract class BaseErrorHandler
      * @param int $code Error code to map
      * @return array Array of error word, and log location.
      */
-    public static function mapErrorCode($code)
+    public static function mapErrorCode(int $code): array
     {
         $levelMap = [
             E_PARSE => 'error',

+ 18 - 18
src/Error/Debugger.php

@@ -166,7 +166,7 @@ class Debugger
      * @param string|null $class Class name.
      * @return \Cake\Error\Debugger
      */
-    public static function getInstance($class = null)
+    public static function getInstance(?string $class = null)
     {
         static $instance = [];
         if (!empty($class)) {
@@ -204,7 +204,7 @@ class Debugger
      *
      * @return array
      */
-    public static function outputMask()
+    public static function outputMask(): array
     {
         return static::configInstance('outputMask');
     }
@@ -220,7 +220,7 @@ class Debugger
      * @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true.
      * @return void
      */
-    public static function setOutputMask(array $value, $merge = true)
+    public static function setOutputMask(array $value, bool $merge = true): void
     {
         static::configInstance('outputMask', $value, $merge);
     }
@@ -234,7 +234,7 @@ class Debugger
      * @see \Cake\Error\Debugger::exportVar()
      * @link https://book.cakephp.org/3.0/en/development/debugging.html#outputting-values
      */
-    public static function dump($var, $depth = 3)
+    public static function dump($var, int $depth = 3): void
     {
         pr(static::exportVar($var, $depth));
     }
@@ -248,7 +248,7 @@ class Debugger
      * @param int $depth The depth to output to. Defaults to 3.
      * @return void
      */
-    public static function log($var, $level = 'debug', $depth = 3)
+    public static function log($var, $level = 'debug', int $depth = 3): void
     {
         $source = static::trace(['start' => 1]) . "\n";
         Log::write($level, "\n" . $source . static::exportVar($var, $depth));
@@ -373,7 +373,7 @@ class Debugger
      * @param string $path Path to shorten.
      * @return string Normalized path
      */
-    public static function trimPath($path)
+    public static function trimPath(string $path): string
     {
         if (defined('APP') && strpos($path, APP) === 0) {
             return str_replace(APP, 'APP/', $path);
@@ -409,7 +409,7 @@ class Debugger
      * @see https://secure.php.net/highlight_string
      * @link https://book.cakephp.org/3.0/en/development/debugging.html#getting-an-excerpt-from-a-file
      */
-    public static function excerpt($file, $line, $context = 2)
+    public static function excerpt(string $file, int $line, int $context = 2): array
     {
         $lines = [];
         if (!file_exists($file)) {
@@ -448,7 +448,7 @@ class Debugger
      * @param string $str The string to convert.
      * @return string
      */
-    protected static function _highlight($str)
+    protected static function _highlight(string $str): string
     {
         if (function_exists('hphp_log') || function_exists('hphp_gettid')) {
             return htmlentities($str);
@@ -491,7 +491,7 @@ class Debugger
      * @param int $depth The depth to output to. Defaults to 3.
      * @return string Variable as a formatted string
      */
-    public static function exportVar($var, $depth = 3)
+    public static function exportVar($var, int $depth = 3): string
     {
         return static::_export($var, $depth, 0);
     }
@@ -504,7 +504,7 @@ class Debugger
      * @param int $indent The current indentation level.
      * @return string The dumped variable.
      */
-    protected static function _export($var, $depth, $indent)
+    protected static function _export($var, int $depth, int $indent): string
     {
         switch (static::getType($var)) {
             case 'boolean':
@@ -550,7 +550,7 @@ class Debugger
      * @param int $indent The current indentation level.
      * @return string Exported array.
      */
-    protected static function _array(array $var, $depth, $indent)
+    protected static function _array(array $var, int $depth, int $indent): string
     {
         $out = '[';
         $break = $end = null;
@@ -591,7 +591,7 @@ class Debugger
      * @return string
      * @see \Cake\Error\Debugger::exportVar()
      */
-    protected static function _object($var, $depth, $indent)
+    protected static function _object($var, int $depth, int $indent): string
     {
         $out = '';
         $props = [];
@@ -656,7 +656,7 @@ class Debugger
      *
      * @return string Returns the current format when getting.
      */
-    public static function getOutputFormat()
+    public static function getOutputFormat(): string
     {
         return Debugger::getInstance()->_outputFormat;
     }
@@ -668,7 +668,7 @@ class Debugger
      * @return void
      * @throws \InvalidArgumentException When choosing a format that doesn't exist.
      */
-    public static function setOutputFormat($format)
+    public static function setOutputFormat(string $format): void
     {
         $self = Debugger::getInstance();
 
@@ -721,7 +721,7 @@ class Debugger
      * @param array $strings Template strings, or a callback to be used for the output format.
      * @return array The resulting format string set.
      */
-    public static function addFormat($format, array $strings)
+    public static function addFormat(string $format, array $strings): array
     {
         $self = Debugger::getInstance();
         if (isset($self->_templates[$format])) {
@@ -746,7 +746,7 @@ class Debugger
      * @param array $data Data to output.
      * @return void
      */
-    public function outputError($data)
+    public function outputError(array $data): void
     {
         $defaults = [
             'level' => 0,
@@ -834,7 +834,7 @@ class Debugger
      * @param mixed $var The variable to get the type of.
      * @return string The type of variable.
      */
-    public static function getType($var)
+    public static function getType($var): string
     {
         if (is_object($var)) {
             return get_class($var);
@@ -874,7 +874,7 @@ class Debugger
      *    data in a browser-friendly way.
      * @return void
      */
-    public static function printVar($var, $location = [], $showHtml = null)
+    public static function printVar($var, array $location = [], ?bool $showHtml = null): void
     {
         $location += ['file' => null, 'line' => null];
         $file = $location['file'];

+ 6 - 7
src/Error/ErrorHandler.php

@@ -111,7 +111,7 @@ class ErrorHandler extends BaseErrorHandler
      * @param bool $debug Whether or not the app is in debug mode.
      * @return void
      */
-    protected function _displayError($error, $debug)
+    protected function _displayError(array $error, bool $debug): void
     {
         if (!$debug) {
             return;
@@ -126,7 +126,7 @@ class ErrorHandler extends BaseErrorHandler
      * @return void
      * @throws \Exception When the chosen exception renderer is invalid.
      */
-    protected function _displayException($exception)
+    protected function _displayException(Throwable $exception): void
     {
         $rendererClassName = App::className($this->_options['exceptionRenderer'], 'Error');
         try {
@@ -150,7 +150,7 @@ class ErrorHandler extends BaseErrorHandler
      *
      * @return void
      */
-    protected function _clearOutput()
+    protected function _clearOutput(): void
     {
         while (ob_get_level()) {
             ob_end_clean();
@@ -162,11 +162,10 @@ class ErrorHandler extends BaseErrorHandler
      *
      * The PHP5 part will be removed with 4.0.
      *
-     * @param \Throwable|\Exception $exception Exception.
-     *
+     * @param \Throwable $exception Exception.
      * @return void
      */
-    protected function _logInternalError($exception)
+    protected function _logInternalError(Throwable $exception): void
     {
         // Disable trace for internal errors.
         $this->_options['trace'] = false;
@@ -185,7 +184,7 @@ class ErrorHandler extends BaseErrorHandler
      * @param string|\Cake\Http\Response $response Either the message or response object.
      * @return void
      */
-    protected function _sendResponse($response)
+    protected function _sendResponse($response): void
     {
         if (is_string($response)) {
             echo $response;

+ 25 - 40
src/Error/ExceptionRenderer.php

@@ -29,7 +29,9 @@ use Cake\Utility\Inflector;
 use Cake\View\Exception\MissingTemplateException;
 use Exception;
 use PDOException;
+use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
+use Throwable;
 
 /**
  * Exception Renderer.
@@ -54,7 +56,7 @@ class ExceptionRenderer implements ExceptionRendererInterface
     /**
      * The exception being handled.
      *
-     * @var \Exception
+     * @var \Throwable
      */
     public $error;
 
@@ -92,10 +94,10 @@ class ExceptionRenderer implements ExceptionRendererInterface
      * If the error is a Cake\Core\Exception\Exception it will be converted to either a 400 or a 500
      * code error depending on the code used to construct the error.
      *
-     * @param \Exception $exception Exception.
+     * @param \Throwable $exception Exception.
      * @param \Psr\Http\Message\ServerRequestInterface $request The request - if this is set it will be used instead of creating a new one
      */
-    public function __construct(Exception $exception, ServerRequestInterface $request = null)
+    public function __construct(Throwable $exception, ServerRequestInterface $request = null)
     {
         $this->error = $exception;
         $this->request = $request;
@@ -103,18 +105,6 @@ class ExceptionRenderer implements ExceptionRendererInterface
     }
 
     /**
-     * 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.
      * This method returns the built in `ErrorController` normally, or if an error is repeated
@@ -139,7 +129,7 @@ class ExceptionRenderer implements ExceptionRendererInterface
             $controller = new $class($request, $response);
             $controller->startupProcess();
             $startup = true;
-        } catch (Exception $e) {
+        } catch (Throwable $e) {
             $startup = false;
         }
 
@@ -150,7 +140,7 @@ class ExceptionRenderer implements ExceptionRendererInterface
             try {
                 $event = new Event('Controller.startup', $controller);
                 $controller->RequestHandler->startup($event);
-            } catch (Exception $e) {
+            } catch (Throwable $e) {
             }
         }
         if (empty($controller)) {
@@ -165,19 +155,18 @@ class ExceptionRenderer implements ExceptionRendererInterface
      *
      * @return \Cake\Http\Response The response to be sent.
      */
-    public function render()
+    public function render(): ResponseInterface
     {
         $exception = $this->error;
         $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, $unwrapped);
+            return $this->_customMethod($method, $exception);
         }
 
         $message = $this->_message($exception, $code);
@@ -194,12 +183,12 @@ class ExceptionRenderer implements ExceptionRendererInterface
         $viewVars = [
             'message' => $message,
             'url' => h($url),
-            'error' => $unwrapped,
+            'error' => $exception,
             'code' => $code,
             '_serialize' => ['message', 'url', 'code']
         ];
         if ($isDebug) {
-            $viewVars['trace'] = Debugger::formatTrace($unwrapped->getTrace(), [
+            $viewVars['trace'] = Debugger::formatTrace($exception->getTrace(), [
                 'format' => 'array',
                 'args' => false
             ]);
@@ -210,8 +199,8 @@ class ExceptionRenderer implements ExceptionRendererInterface
         }
         $this->controller->set($viewVars);
 
-        if ($unwrapped instanceof CakeException && $isDebug) {
-            $this->controller->set($unwrapped->getAttributes());
+        if ($exception instanceof CakeException && $isDebug) {
+            $this->controller->set($exception->getAttributes());
         }
         $this->controller->setResponse($response);
 
@@ -222,10 +211,10 @@ class ExceptionRenderer implements ExceptionRendererInterface
      * Render a custom error method/template.
      *
      * @param string $method The method name to invoke.
-     * @param \Exception $exception The exception to render.
+     * @param \Throwable $exception The exception to render.
      * @return \Cake\Http\Response The response to send.
      */
-    protected function _customMethod($method, $exception)
+    protected function _customMethod(string $method, Throwable $exception)
     {
         $result = call_user_func([$this, $method], $exception);
         $this->_shutdown();
@@ -239,12 +228,11 @@ class ExceptionRenderer implements ExceptionRendererInterface
     /**
      * Get method name
      *
-     * @param \Exception $exception Exception instance.
+     * @param \Throwable $exception Exception instance.
      * @return string
      */
-    protected function _method(Exception $exception)
+    protected function _method(Throwable $exception)
     {
-        $exception = $this->_unwrap($exception);
         list(, $baseClass) = namespaceSplit(get_class($exception));
 
         if (substr($baseClass, -9) === 'Exception') {
@@ -259,13 +247,12 @@ class ExceptionRenderer implements ExceptionRendererInterface
     /**
      * Get error message.
      *
-     * @param \Exception $exception Exception.
+     * @param \Throwable $exception Exception.
      * @param int $code Error code.
      * @return string Error message
      */
-    protected function _message(Exception $exception, $code)
+    protected function _message(Throwable $exception, int $code): string
     {
-        $exception = $this->_unwrap($exception);
         $message = $exception->getMessage();
 
         if (!Configure::read('debug') &&
@@ -284,14 +271,13 @@ class ExceptionRenderer implements ExceptionRendererInterface
     /**
      * Get template for rendering exception info.
      *
-     * @param \Exception $exception Exception instance.
+     * @param \Throwable $exception Exception instance.
      * @param string $method Method name.
      * @param int $code Error code.
      * @return string Template name
      */
-    protected function _template(Exception $exception, $method, $code)
+    protected function _template(Throwable $exception, string $method, int $code): string
     {
-        $exception = $this->_unwrap($exception);
         $isHttpException = $exception instanceof HttpException;
 
         if (!Configure::read('debug') && !$isHttpException || $isHttpException) {
@@ -315,13 +301,12 @@ class ExceptionRenderer implements ExceptionRendererInterface
     /**
      * Get an error code value within range 400 to 506
      *
-     * @param \Exception $exception Exception.
+     * @param \Throwable $exception Exception.
      * @return int Error code value within range 400 to 506
      */
-    protected function _code(Exception $exception)
+    protected function _code(Throwable $exception): int
     {
         $code = 500;
-        $exception = $this->_unwrap($exception);
         $errorCode = $exception->getCode();
         if ($errorCode >= 400 && $errorCode < 506) {
             $code = $errorCode;
@@ -336,7 +321,7 @@ class ExceptionRenderer implements ExceptionRendererInterface
      * @param string $template The template to render.
      * @return \Cake\Http\Response A response object that can be sent.
      */
-    protected function _outputMessage($template)
+    protected function _outputMessage(string $template)
     {
         try {
             $this->controller->render($template);
@@ -368,7 +353,7 @@ class ExceptionRenderer implements ExceptionRendererInterface
      * @param string $template The template to render.
      * @return \Cake\Http\Response A response object that can be sent.
      */
-    protected function _outputMessageSafe($template)
+    protected function _outputMessageSafe(string $template)
     {
         $helpers = ['Form', 'Html'];
         $builder = $this->controller->viewBuilder();

+ 3 - 1
src/Error/ExceptionRendererInterface.php

@@ -15,6 +15,8 @@ declare(strict_types=1);
  */
 namespace Cake\Error;
 
+use Psr\Http\Message\ResponseInterface;
+
 /**
  * Interface ExceptionRendererInterface
  */
@@ -25,5 +27,5 @@ interface ExceptionRendererInterface
      *
      * @return \Cake\Http\Response The response to be sent.
      */
-    public function render();
+    public function render(): ResponseInterface;
 }

+ 0 - 6
src/Error/Middleware/ErrorHandlerMiddleware.php

@@ -20,7 +20,6 @@ use Cake\Core\Configure;
 use Cake\Core\Exception\Exception as CakeException;
 use Cake\Core\InstanceConfigTrait;
 use Cake\Error\ExceptionRenderer;
-use Cake\Error\PHP7ErrorException;
 use Cake\Log\Log;
 use Error;
 use Exception;
@@ -154,11 +153,6 @@ class ErrorHandlerMiddleware
             $this->exceptionRenderer = $this->getConfig('exceptionRenderer') ?: ExceptionRenderer::class;
         }
 
-        // For PHP5 backwards compatibility
-        if ($exception instanceof Error) {
-            $exception = new PHP7ErrorException($exception);
-        }
-
         if (is_string($this->exceptionRenderer)) {
             $class = App::className($this->exceptionRenderer, 'Error');
             if (!$class) {

+ 0 - 64
src/Error/PHP7ErrorException.php

@@ -1,64 +0,0 @@
-<?php
-declare(strict_types=1);
-/**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @since         3.1.5
- * @license       https://opensource.org/licenses/mit-license.php MIT License
- */
-namespace Cake\Error;
-
-use Exception;
-
-/**
- * Wraps a PHP 7 Error object inside a normal Exception
- * so it can be handled correctly by the rest of the
- * error handling system
- */
-class PHP7ErrorException extends Exception
-{
-
-    /**
-     * The wrapped error object
-     *
-     * @var \Error
-     */
-    protected $_error;
-
-    /**
-     * Wraps the passed Error class
-     *
-     * @param \Error $error the Error object
-     */
-    public function __construct($error)
-    {
-        $this->_error = $error;
-        $this->message = $error->getMessage();
-        $this->code = $error->getCode();
-        $this->file = $error->getFile();
-        $this->line = $error->getLine();
-        $msg = sprintf(
-            '(%s) - %s in %s on %s',
-            get_class($error),
-            $this->message,
-            $this->file ?: 'null',
-            $this->line ?: 'null'
-        );
-        parent::__construct($msg, $this->code, $error->getPrevious());
-    }
-
-    /**
-     * Returns the wrapped error object
-     *
-     * @return \Error
-     */
-    public function getError()
-    {
-        return $this->_error;
-    }
-}

+ 1 - 1
tests/TestCase/Error/DebuggerTest.php

@@ -119,7 +119,7 @@ class DebuggerTest extends TestCase
         $result = Debugger::excerpt(__FILE__, 11, 2);
         $this->assertCount(5, $result);
 
-        $pattern = '/<span style\="color\: \#\d{6}">\*<\/span>/';
+        $pattern = '/<span style\="color\: \#\d{6}">.*?<\/span>/';
         $this->assertRegExp($pattern, $result[0]);
 
         $return = Debugger::excerpt('[internal]', 2, 2);

+ 2 - 18
tests/TestCase/Error/ErrorHandlerTest.php

@@ -18,7 +18,6 @@ namespace Cake\Test\TestCase\Error;
 use Cake\Core\Configure;
 use Cake\Core\Plugin;
 use Cake\Error\ErrorHandler;
-use Cake\Error\PHP7ErrorException;
 use Cake\Http\Exception\ForbiddenException;
 use Cake\Http\Exception\NotFoundException;
 use Cake\Http\ServerRequest;
@@ -46,7 +45,7 @@ class TestErrorHandler extends ErrorHandler
      *
      * @return void
      */
-    protected function _clearOutput()
+    protected function _clearOutput(): void
     {
         // noop
     }
@@ -56,7 +55,7 @@ class TestErrorHandler extends ErrorHandler
      *
      * @return void
      */
-    protected function _sendResponse($response)
+    protected function _sendResponse($response): void
     {
         $this->response = $response;
     }
@@ -443,21 +442,6 @@ class ErrorHandlerTest extends TestCase
     }
 
     /**
-     * Tests Handling a PHP7 error
-     *
-     * @return void
-     */
-    public function testHandlePHP7Error()
-    {
-        $this->skipIf(!class_exists('Error'), 'Requires PHP7');
-        $error = new PHP7ErrorException(new ParseError('Unexpected variable foo'));
-        $errorHandler = new TestErrorHandler();
-
-        $errorHandler->handleException($error);
-        $this->assertContains('Unexpected variable foo', (string)$errorHandler->response->getBody(), 'message missing.');
-    }
-
-    /**
      * Data provider for memory limit changing.
      *
      * @return array

+ 5 - 2
tests/test_app/Plugin/TestPlugin/src/Error/TestPluginExceptionRenderer.php

@@ -20,6 +20,7 @@
 namespace TestPlugin\Error;
 
 use Cake\Error\ExceptionRenderer;
+use Psr\Http\Message\ResponseInterface;
 
 /**
  * TestPluginExceptionRenderer
@@ -32,8 +33,10 @@ class TestPluginExceptionRenderer extends ExceptionRenderer
      *
      * @return string
      */
-    public function render()
+    public function render(): ResponseInterface
     {
-        return 'Rendered by test plugin';
+        $response = $this->controller->getResponse();
+
+        return $response->withStringBody('Rendered by test plugin');
     }
 }