The process of developing a universal error handler is quite similar to the preceding recipe. There are certain differences, however. First of all, in PHP 7, some errors are thrown and can be caught, whereas others simply stop your application dead in its tracks. To further confuse matters, some errors are treated like exceptions, whereas others are derived from the new PHP 7 Error class. Fortunately for us, in PHP 7, both Error and Exception implement a new interface called Throwable. Accordingly, if you are not sure whether your code will throw an Exception or an Error, simply catch an instance of Throwable and you'll catch both.
Application\Error\Handler class defined in the preceding recipe. In the constructor, set a new errorHandler() method as the default error handler:public function __construct($logFileDir = NULL, $logFile = NULL)
{
$logFile = $logFile ?? date('Ymd') . '.log';
$logFileDir = $logFileDir ?? __DIR__;
$this->logFile = $logFileDir . '/' . $logFile;
$this->logFile = str_replace('//', '/', $this->logFile);
set_exception_handler([$this,'exceptionHandler']);
set_error_handler([$this, 'errorHandler']);
}public function errorHandler($errno, $errstr, $errfile, $errline)
{
$message = sprintf('ERROR: %s : %d : %s : %s : %s' . PHP_EOL,
date('Y-m-d H:i:s'), $errno, $errstr, $errfile, $errline);
file_put_contents($this->logFile, $message, FILE_APPEND);
}EXCEPTION to the message sent to the log file in the exceptionHandler() method:public function exceptionHandler($ex)
{
$message = sprintf('EXCEPTION: %19s : %20s : %s' . PHP_EOL,
date('Y-m-d H:i:s'), get_class($ex), $ex->getMessage());
file_put_contents($this->logFile, $message, FILE_APPEND);
}First, make the changes to Application\Error\Handler as defined previously. Next, create a class that throws an error that, for this illustration, could be defined as Application\Error\ThrowsError. For example, you could have a method that attempts a divide by zero operation, and another that attempts to parse non-PHP code using eval():
<?php
namespace Application\Error;
class ThrowsError
{
const NOT_PARSE = 'this will not parse';
public function divideByZero()
{
$this->zero = 1 / 0;
}
public function willNotParse()
{
eval(self::NOT_PARSE);
}
}You can then define a calling program called chap_13_error_throwable.php that sets up autoloading, uses the appropriate classes, and creates an instance of ThrowsError:
<?php
require __DIR__ . '/../Application/Autoload/Loader.php';
Application\Autoload\Loader::init(__DIR__ . '/..');
use Application\Error\ { Handler, ThrowsError };
$error = new ThrowsError();If you then call the two methods, without a try/catch block and without defining the universal error handler, the first method generates a Warning, whereas the second throws a ParseError:
$error->divideByZero(); $error->willNotParse(); echo 'Application continues ... ' . PHP_EOL;
Because this is an error, program execution stops, and you will not see Application continues ...:

If you wrap the method calls in try/catch blocks and catch Throwable, the code execution continues:
try {
$error->divideByZero();
} catch (Throwable $e) {
echo 'Error Caught: ' . get_class($e) . ':'
. $e->getMessage() . PHP_EOL;
}
try {
$error->willNotParse();
} catch (Throwable $e) {
echo 'Error Caught: ' . get_class($e) . ':'
. $e->getMessage() . PHP_EOL;
}
echo 'Application continues ... ' . PHP_EOL;From the following output, you will also note that the program exits with code 0, which tells us all is OK:

Finally, after the try/catch blocks, run the errors again, moving the echo statement to the end. You will see in the output that the errors were caught, but in the log file, notice that DivisionByZeroError is caught by the exception handler, whereas the ParseError is caught by the error hander:
$handler = new Handler(__DIR__ . '/logs'); $error->divideByZero(); $error->willNotParse(); echo 'Application continues ... ' . PHP_EOL;
