PHP-Errors zu Exceptions konvertieren

Wie wir ja alle wissen, ist der @-Fehlerunterdrückungs-Operator gemeinhin böse. Was aber nun, wenn wir mit PHP-eigenen Funktionen arbeiten müssen, die im Fehlerfall eine Warning produzieren?
Was ich meine ist z.B. file_get_contents. Wenn die aufzurufende URL nicht erreichbar ist, heißt es

Warning: file_get_contents() [function.file-get-contents]: php_network_getaddresses: getaddrinfo failed: Der angegebene Host ist unbekannt

Unschön. Glücklicherweise kann man mittels eines eigenen Errorhandlers einen Workaround basteln, indem der Fehler zu einer Exception konvertiert wird:

function errorhandler($code, $error, $file, $line)
{
	throw new ErrorException($error, $code, 0, $file, $line);
}

set_error_handler("errorhandler");

try
{
	echo file_get_contents("http://www.this-is-not-a-real-url.org");
}
catch (ErrorException $ex)
{
	echo "Die Webseite ist gerade nicht erreichbar";
}

Im Produktivbetrieb sollten die Fehler bekanntlich ohnehin nur geloggt und nicht angezeigt werden, trotz allem fühle ich mich wohler mit solch einer Lösung. Leider greift diese „Konvertierung“ nicht bei fatal errors und parser errors – Aber die sind ja ohnehin auszumerzen.

Edit 10.10.2012

Basierend auf PHP 5.3 (Closures) und diesem Blogpost hier ein recht vollständiges Error-Handling-Konzept, welches testweise die Funktion error_log verwendet:

<?php
class Logger
{
	public static function addError($message, $content = array())
	{
		error_log("Error: " . $message . " - " . print_r($content, true), 3, "D:/xampp/htdocs/error.log");
	}
	
	public static function addAlert($message, $content = array())
	{
		error_log("Warning: " . $message . " - " . print_r($content, true), 3, "D:/xampp/htdocs/error.log");
	}
}

/**
 * Create a closure to handle uncaught exceptions
 */
set_exception_handler($handler = function(Exception $e) use (&$handler) {
    $message = sprintf(
        'Uncaught exception of type %s thrown in file %s at line %s%s.',
        get_class($e),
        $e->getFile(),
        $e->getLine(),
        $e->getMessage() ? sprintf(' with message "%s"', $e->getMessage()) : ''
    );
    Logger::addError($message, array(
        'Exception file'  => $e->getFile(),
        'Exception line'  => $e->getLine(),
        'Exception trace' => $e->getTraceAsString()
    ));
    /**
     * If there was a previous nested exception call this function recursively
     * to log that too.
     */
    if ($prev = $e->getPrevious()) {
        $handler($prev);
    }
});

/**
 * Set a custom error handler to make sure that errors are logged to Graylog.
 * Allows any non-fatal errors to be logged to the Graylog2 server.
 */
set_error_handler(function($errno, $errstr, $errfile, $errline, array $errcontext){
	$message = 'Error of level ';
	switch ($errno) {
		case E_USER_ERROR:
			$message .= 'E_USER_ERROR';
			break;
		case E_USER_WARNING:
			$message .= 'E_USER_WARNING';
			break;
		case E_USER_NOTICE:
			$message .= 'E_USER_NOTICE';
			break;
		case E_STRICT:
			$message .= 'E_STRICT';
			break;
		case E_RECOVERABLE_ERROR:
			$message .= 'E_RECOVERABLE_ERROR';
			break;
		case E_DEPRECATED:
			$message .= 'E_DEPRECATED';
			break;
		case E_USER_DEPRECATED:
			$message .= 'E_USER_DEPRECATED';
			break;
		case E_NOTICE:
			$message .= 'E_NOTICE';
			break;
		case E_WARNING:
			$message .= 'E_WARNING';
			break;
		default:
			$message .= sprintf('Unknown error level, code of %d passed', $errno);
	}
	$message .= sprintf(
		'. Error message was "%s" in file %s at line %d.',
		$errstr,
		$errfile,
		$errline
	);
	Logger::addError($message, $errcontext);

	return false;//Returning false will mean that PHP's error handling mechanism will not be bypassed.
});

/**
 * This function will be called before the script exits.
 * This allows us to catch and log any fatal errors in the Graylog2 server.
 * This is needed as the set_error_handler function cannot be used to handle
 * any of the errors in the array below.
 */
register_shutdown_function(function(){
    $codes = array(
        1   => 'E_ERROR',
        4   => 'E_PARSE',
        16  => 'E_CORE_ERROR',
        32  => 'E_CORE_WARNING',
        64  => 'E_COMPILE_ERROR',
        128 => 'E_COMPILE_WARNING'
    );
    $error = error_get_last();
    if (is_array($error) && array_key_exists($error['type'], $codes)) {
        $message = sprintf(
            'Error of type %s raised in file %s at line %d with message "%s"',
            $codes[$error['type']],
            $error['file'],
            $error['line'],
            $error['message']
        );

        if (in_array($error['type'], array(32, 128))) {
            //These errors are warnings and should be logged at a lower level.
            Logger::addError($message);
        } else {
           Logger::addAlert($message);
        }
    }
});

//throw new Exception("foobar");
//echo 1/0;
asd::Xxx();

Macht mir einen sehr vernünftigen Eindruck, ein eigener Errorhandler, ein eigener Exception-Handler und eine Shutdown-Funktion. So entwischt kein Fehler mehr ;).

Weitere Posts:

Dieser Beitrag wurde unter php, Quicktips, webdev veröffentlicht. Setze ein Lesezeichen auf den Permalink.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.