PHPのお勉強!

PHP TOP

ErrorException

(PHP 5 >= 5.1.0, PHP 7, PHP 8)

はじめに

エラー例外です。

クラス概要

class ErrorException extends Exception {
/* プロパティ */
protected int $severity = E_ERROR;
/* 継承したプロパティ */
protected string $message = "";
private string $string = "";
protected int $code;
protected string $file = "";
protected int $line;
private array $trace = [];
private ?Throwable $previous = null;
/* メソッド */
public __construct(
    string $message = "",
    int $code = 0,
    int $severity = E_ERROR,
    ?string $filename = null,
    ?int $line = null,
    ?Throwable $previous = null
)
final public getSeverity(): int
/* 継承したメソッド */
final public Exception::getCode(): int
final public Exception::getFile(): string
final public Exception::getLine(): int
final public Exception::getTrace(): array
}

プロパティ

severity

例外の深刻度

例1 set_error_handler() を使用した、エラーメッセージから ErrorException への変換

<?php

set_error_handler
(function (int $errno, string $errstr, string $errfile, int $errline) {
if (!(
error_reporting() & $errno)) {
// このエラーコードが error_reporting に含まれていない場合
return;
}

if (
$errno === E_DEPRECATED || $errno === E_USER_DEPRECATED) {
// 推奨されない警告の場合は、例外をスローしません。
// 新規または想定外の警告がアプリケーションを壊すからです。
return;
}

throw new
\ErrorException($errstr, 0, $errno, $errfile, $errline);
});

// 壊れたデータをアンシリアライズすると、エラーハンドラによって
// 警告は ErrorException に変換されます。
unserialize('broken data');

?>

上の例の出力は、 たとえば以下のようになります。

Fatal error: Uncaught ErrorException: unserialize(): Error at offset 0 of 11 bytes in test.php:16
Stack trace:
#0 [internal function]: {closure}(2, 'unserialize(): ...', 'test.php', 16)
#1 test.php(16): unserialize('broken data')
#2 {main}
  thrown in test.php on line 16

目次

add a note

User Contributed Notes 5 notes

up
18
randallgirard at hotmail dot com
15 years ago
E_USER_WARNING, E_USER_NOTICE, and any other non-terminating error codes, are useless and act like E_USER_ERROR (which terminate) when you combine a custom ERROR_HANDLER with ErrorException and do not CATCH the error. There is NO way to return execution to the parent scope in the EXCEPTION_HANDLER.

<?php

error_reporting
(E_ALL);
define('DEBUG', true);
define('LINEBREAK', "\r\n");

error::initiate('./error_backtrace.log');

try
trigger_error("First error", E_USER_NOTICE);
catch (
ErrorException $e )
print(
"Caught the error: ".$e->getMessage."<br />\r\n" );

trigger_error("This event WILL fire", E_USER_NOTICE);

trigger_error("This event will NOT fire", E_USER_NOTICE);

abstract class
error {

public static
$LIST = array();

private function
__construct() {}

public static function
initiate( $log = false ) {
set_error_handler( 'error::err_handler' );
set_exception_handler( 'error::exc_handler' );
if (
$log !== false ) {
if ( !
ini_get('log_errors') )
ini_set('log_errors', true);
if ( !
ini_get('error_log') )
ini_set('error_log', $log);
}
}

public static function
err_handler($errno, $errstr, $errfile, $errline, $errcontext) {
$l = error_reporting();
if (
$l & $errno ) {

$exit = false;
switch (
$errno ) {
case
E_USER_ERROR:
$type = 'Fatal Error';
$exit = true;
break;
case
E_USER_WARNING:
case
E_WARNING:
$type = 'Warning';
break;
case
E_USER_NOTICE:
case
E_NOTICE:
case @
E_STRICT:
$type = 'Notice';
break;
case @
E_RECOVERABLE_ERROR:
$type = 'Catchable';
break;
default:
$type = 'Unknown Error';
$exit = true;
break;
}

$exception = new \ErrorException($type.': '.$errstr, 0, $errno, $errfile, $errline);

if (
$exit ) {
exc_handler($exception);
exit();
}
else
throw
$exception;
}
return
false;
}

function
exc_handler($exception) {
$log = $exception->getMessage() . "\n" . $exception->getTraceAsString() . LINEBREAK;
if (
ini_get('log_errors') )
error_log($log, 0);
print(
"Unhandled Exception" . (DEBUG ? " - $log" : ''));
}

}
?>
up
18
triplepoint at gmail dot com
14 years ago
As noted below, it's important to realize that unless caught, any Exception thrown will halt the script. So converting EVERY notice, warning, or error to an ErrorException will halt your script when something harmlesss like E_USER_NOTICE is triggered.

It seems to me the best use of the ErrorException class is something like this:

<?php
function custom_error_handler($number, $string, $file, $line, $context)
{
// Determine if this error is one of the enabled ones in php config (php.ini, .htaccess, etc)
$error_is_enabled = (bool)($number & ini_get('error_reporting') );

// -- FATAL ERROR
// throw an Error Exception, to be handled by whatever Exception handling logic is available in this context
if( in_array($number, array(E_USER_ERROR, E_RECOVERABLE_ERROR)) && $error_is_enabled ) {
throw new
ErrorException($errstr, 0, $errno, $errfile, $errline);
}

// -- NON-FATAL ERROR/WARNING/NOTICE
// Log the error if it's enabled, otherwise just ignore it
else if( $error_is_enabled ) {
error_log( $string, 0 );
return
false; // Make sure this ends up in $php_errormsg, if appropriate
}
}
?>

Setting this function as the error handler will result in ErrorExceptions only being thrown for E_USER_ERROR and E_RECOVERABLE_ERROR, while other enabled error types will simply get error_log()'ed.

It's worth noting again that no matter what you do, "E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and most of E_STRICT" will never reach your custom error handler, and therefore will not be converted into ErrorExceptions. Plan accordingly.
up
7
luke at cywh dot com
15 years ago
To add to the comments made by chris AT cmbuckley DOT co DOT uk about the ErrorException problem with args:

I noticed that the problem is in the ErrorException class itself, not the Exception class. When using just the exception class, it's no longer an issue. Besides the args problem, the only difference between Exception and ErrorException in the stack trace is that the args are left out of the error handler exception function. I'm not sure if this was on purpose or not, but it shouldn't hurt to show this information anyway.

So instead of using this broken extended class, you can ignore it and make your own extended class and avoid the problem all together:

<?php

header
('Content-Type: text/plain');

class
ErrorHandler extends Exception {
protected
$severity;

public function
__construct($message, $code, $severity, $filename, $lineno) {
$this->message = $message;
$this->code = $code;
$this->severity = $severity;
$this->file = $filename;
$this->line = $lineno;
}

public function
getSeverity() {
return
$this->severity;
}
}

function
exception_error_handler($errno, $errstr, $errfile, $errline ) {
throw new
ErrorHandler($errstr, 0, $errno, $errfile, $errline);
}

set_error_handler("exception_error_handler", E_ALL);

function
A() {
$foo->bar; // Purposely cause error
}

function
B($c) {
A();
}

try {
B('foobar');
} catch (
Exception $e) {
var_dump($e->getTrace());
}

?>

The only thing I wish I could do was remove the entry for the error handler function because it's quite irrelevant. Maybe that's what they were trying to do with the ErrorException class? Either way, you can't change it because the trace functions are final, and the variable is private.
up
3
Christopher Marshall
3 years ago
Going on further from the point made by triplepoint at gmail dot com:

I'm using PHP 7.4.0 and trying to introduce error handling, exception handling and fatal exception handling into my application. A lot of the info all over the internet is now out of date in regards to handling errors with the new changes in PHP 7 and 8, which makes it difficult at the best of times to understand everything.

However what I've found is that by using register_shutdown_function to handle fatal exceptions, it works as expected. set_exception_handler also works perfectly in conjunction. The issue comes when you use set_error_handler as well, and you trigger a custom error (for example using trigger_error) - even if you're using E_ERROR or E_USER_ERROR.

This is because PHP is trying to handle the error before it shuts down and before the register_shutdown_function is actually involved. So it's very important to be mindful of this if you're using different methods for exceptions, errors and fatal exceptions. You will need to specifically catch the error like before and return out of your error handling function for the fatal exception handler to kick in properly.

You're welcome .....

<?php
/**
* We handle basic errors differently to everything else
*/
public static function errorHandler($errStatus, $errMsg = 'Unknown error', $errFile = 'unknown', $errLine = 0)
{
/**
* Because we're using set_error_handler, PHP tries to be
* clever and routes fatal errors and other "errors"
* (i.e. trigger_error) here before it goes to
* register_shutdown_function, so we need to be sure these
* are caught and dealt with in the correct way
*
* @See https://www.php.net/manual/en/class.errorexception.php#95415
*/
if (\in_array($errStatus, [E_ERROR, E_PARSE, E_CORE_ERROR, E_USER_ERROR, E_ERROR]))
{
return;
}

/* Handle everything else however you want */
}
up
-4
Kevin
7 years ago
I have been Googling all over the place for how to convert an E_NOTICE error into an exception, and I think I finally found a clean way to do it:

@include "errorcausingcode.php";

$lastError = error_get_last();
if ( !empty( $lastError ) ) {
throw new TemplateRenderingException($lastError['message'], $lastError['type']);
}

Basically, if it's something not system haulting it will likely hit this. Then you can throw whatever exception you want. Now, this is of course if you have a need. I did, because I wanted to clean up my output buffer if there was an error that skipped over it.
To Top