webentwicklung-frage-antwort-db.com.de

PHP: Ausnahmen vs Fehler?

Vielleicht fehlt es mir irgendwo im PHP Handbuch, aber was genau ist der Unterschied zwischen einem Fehler und einer Ausnahme? Der einzige Unterschied, den ich sehen kann, ist, dass Fehler und Ausnahmen unterschiedlich behandelt werden. Aber was verursacht eine Ausnahme und was verursacht einen Fehler?

108
Jason Baker

Ausnahmen sind geworfen - sie sollen gefangen werden. Fehler sind im Allgemeinen nicht behebbar. Nehmen wir zum Beispiel an, Sie haben einen Codeblock, der eine Zeile in eine Datenbank einfügt. Es ist möglich, dass dieser Aufruf fehlschlägt (doppelte ID) - es wird ein "Fehler" angezeigt, der in diesem Fall eine "Ausnahme" darstellt. Wenn Sie diese Zeilen einfügen, können Sie so etwas tun

try {
  $row->insert();
  $inserted = true;
} catch (Exception $e) {
  echo "There was an error inserting the row - ".$e->getMessage();
  $inserted = false;
}

echo "Some more stuff";

Die Programmausführung wird fortgesetzt, da Sie die Ausnahme "abgefangen" haben. Eine Ausnahme wird als Fehler behandelt, sofern sie nicht abgefangen wird. Damit können Sie die Programmausführung fortsetzen, nachdem dies ebenfalls fehlgeschlagen ist.

83
gnarf

Ich für gewöhnlich set_error_handler zu einer Funktion, die den Fehler aufnimmt und eine Ausnahme auslöst, sodass ich, was auch immer passiert, nur Ausnahmen habe, mit denen ich mich befassen muss. Nicht mehr @file_get_contents einfach nett und ordentlich versuchen/fangen.

In Debug-Situationen habe ich auch einen Exception-Handler, der eine asp.net-ähnliche Seite ausgibt. Ich poste dies auf der Straße, aber auf Anfrage werde ich später eine Beispielquelle posten.

bearbeiten:

Zusätzlich habe ich, wie versprochen, einen Teil meines Codes ausgeschnitten und eingefügt, um ein Beispiel zu erstellen. Ich habe das Folgende in einer Datei auf meiner Workstation gespeichert. Sie können NICHT MEHR die Ergebnisse anzeigen hier (weil die Verbindung unterbrochen ist).

<?php

define( 'DEBUG', true );

class ErrorOrWarningException extends Exception
{
    protected $_Context = null;
    public function getContext()
    {
        return $this->_Context;
    }
    public function setContext( $value )
    {
        $this->_Context = $value;
    }

    public function __construct( $code, $message, $file, $line, $context )
    {
        parent::__construct( $message, $code );

        $this->file = $file;
        $this->line = $line;
        $this->setContext( $context );
    }
}

/**
 * Inspire to write perfect code. everything is an exception, even minor warnings.
 **/
function error_to_exception( $code, $message, $file, $line, $context )
{
    throw new ErrorOrWarningException( $code, $message, $file, $line, $context );
}
set_error_handler( 'error_to_exception' );

function global_exception_handler( $ex )
{
    ob_start();
    dump_exception( $ex );
    $dump = ob_get_clean();
    // send email of dump to administrator?...

    // if we are in debug mode we are allowed to dump exceptions to the browser.
    if ( defined( 'DEBUG' ) && DEBUG == true )
    {
        echo $dump;
    }
    else // if we are in production we give our visitor a Nice message without all the details.
    {
        echo file_get_contents( 'static/errors/fatalexception.html' );
    }
    exit;
}

function dump_exception( Exception $ex )
{
    $file = $ex->getFile();
    $line = $ex->getLine();

    if ( file_exists( $file ) )
    {
        $lines = file( $file );
    }

?><html>
    <head>
        <title><?= $ex->getMessage(); ?></title>
        <style type="text/css">
            body {
                width : 800px;
                margin : auto;
            }

            ul.code {
                border : inset 1px;
            }
            ul.code li {
                white-space: pre ;
                list-style-type : none;
                font-family : monospace;
            }
            ul.code li.line {
                color : red;
            }

            table.trace {
                width : 100%;
                border-collapse : collapse;
                border : solid 1px black;
            }
            table.thead tr {
                background : rgb(240,240,240);
            }
            table.trace tr.odd {
                background : white;
            }
            table.trace tr.even {
                background : rgb(250,250,250);
            }
            table.trace td {
                padding : 2px 4px 2px 4px;
            }
        </style>
    </head>
    <body>
        <h1>Uncaught <?= get_class( $ex ); ?></h1>
        <h2><?= $ex->getMessage(); ?></h2>
        <p>
            An uncaught <?= get_class( $ex ); ?> was thrown on line <?= $line; ?> of file <?= basename( $file ); ?> that prevented further execution of this request.
        </p>
        <h2>Where it happened:</h2>
        <? if ( isset($lines) ) : ?>
        <code><?= $file; ?></code>
        <ul class="code">
            <? for( $i = $line - 3; $i < $line + 3; $i ++ ) : ?>
                <? if ( $i > 0 && $i < count( $lines ) ) : ?>
                    <? if ( $i == $line-1 ) : ?>
                        <li class="line"><?= str_replace( "\n", "", $lines[$i] ); ?></li>
                    <? else : ?>
                        <li><?= str_replace( "\n", "", $lines[$i] ); ?></li>
                    <? endif; ?>
                <? endif; ?>
            <? endfor; ?>
        </ul>
        <? endif; ?>

        <? if ( is_array( $ex->getTrace() ) ) : ?>
        <h2>Stack trace:</h2>
            <table class="trace">
                <thead>
                    <tr>
                        <td>File</td>
                        <td>Line</td>
                        <td>Class</td>
                        <td>Function</td>
                        <td>Arguments</td>
                    </tr>
                </thead>
                <tbody>
                <? foreach ( $ex->getTrace() as $i => $trace ) : ?>
                    <tr class="<?= $i % 2 == 0 ? 'even' : 'odd'; ?>">
                        <td><?= isset($trace[ 'file' ]) ? basename($trace[ 'file' ]) : ''; ?></td>
                        <td><?= isset($trace[ 'line' ]) ? $trace[ 'line' ] : ''; ?></td>
                        <td><?= isset($trace[ 'class' ]) ? $trace[ 'class' ] : ''; ?></td>
                        <td><?= isset($trace[ 'function' ]) ? $trace[ 'function' ] : ''; ?></td>
                        <td>
                            <? if( isset($trace[ 'args' ]) ) : ?>
                                <? foreach ( $trace[ 'args' ] as $i => $arg ) : ?>
                                    <span title="<?= var_export( $arg, true ); ?>"><?= gettype( $arg ); ?></span>
                                    <?= $i < count( $trace['args'] ) -1 ? ',' : ''; ?> 
                                <? endforeach; ?>
                            <? else : ?>
                            NULL
                            <? endif; ?>
                        </td>
                    </tr>
                <? endforeach;?>
                </tbody>
            </table>
        <? else : ?>
            <pre><?= $ex->getTraceAsString(); ?></pre>
        <? endif; ?>
    </body>
</html><? // back in php
}
set_exception_handler( 'global_exception_handler' );

class X
{
    function __construct()
    {
        trigger_error( 'Whoops!', E_USER_NOTICE );      
    }
}

$x = new X();

throw new Exception( 'Execution will never get here' );

?>
53
Kris

Die Antwort verdient es, über den Elefanten im Raum zu sprechen

Errors ist die alte Methode zur Behandlung eines Fehlerzustands zur Laufzeit. In der Regel ruft der Code vor dem Ausführen von Code so etwas wie set_error_handler Auf. In Anlehnung an die Tradition der Assembler-Interrupts. So würde ein BASIC-Code aussehen.

on error :divide_error

print 1/0
print "this won't print"

:divide_error

if errcode = X
   print "divide by zero error"

Es war schwer sicherzustellen, dass set_error_handler Mit dem richtigen Wert aufgerufen wurde. Und noch schlimmer, es könnte ein Aufruf an eine separate Prozedur erfolgen, die den Fehlerbehandler ändern würde. Außerdem wurden Aufrufe häufig mit set_error_handler - Aufrufen und -Handlern durchsetzt. Code geriet schnell außer Kontrolle. Die Ausnahmebehandlung wurde durch die Formalisierung der Syntax und der Semantik dessen, was guter Code wirklich tat, unterstützt.

try {
   print 1/0;
   print "this won't print";
} catch (DivideByZeroException $e) {
   print "divide by zero error";
}

Keine separate Funktion oder das Risiko, den falschen Fehlerbehandler aufzurufen. Der Code befindet sich jetzt garantiert an derselben Stelle. Außerdem erhalten wir bessere Fehlermeldungen.

PHP hatte früher nur eine Fehlerbehandlung, als sich viele andere Sprachen bereits zum bevorzugten Ausnahmebehandlungsmodell entwickelt hatten. Irgendwann haben die Entwickler von PHP=) die Ausnahmebehandlung implementiert. Da sie wahrscheinlich alten Code unterstützen, behielten sie die Fehlerbehandlung bei und boten eine Möglichkeit, die Fehlerbehandlung wie eine Ausnahmebehandlung aussehen zu lassen. Abgesehen davon gibt es keine Garantie dafür Einige Codes setzen den Fehlerbehandler möglicherweise nicht zurück, was genau das war, was die Ausnahmebehandlung bieten sollte.

Endgültige Antwort

Fehler, die vor der Implementierung der Ausnahmebehandlung codiert wurden, sind wahrscheinlich immer noch Fehler. Neue Fehler sind wahrscheinlich Ausnahmen. Aber es gibt kein Design und keine Logik, bei denen es sich um Fehler und Ausnahmen handelt. Es basiert nur auf dem, was zum Zeitpunkt der Codierung verfügbar war, und auf der Präferenz des Programmierers, der es codiert.

15

Eine Sache, die hier hinzugefügt werden muss, ist die Behandlung von Ausnahmen und Fehlern. Für den Anwendungsentwickler sind sowohl Fehler als auch Ausnahmen "schlechte Dinge", die Sie aufzeichnen möchten, um zu erfahren, welche Probleme Ihre Anwendung hat, damit Ihre Kunden auf lange Sicht eine bessere Erfahrung haben.

Aus diesem Grund ist es sinnvoll, einen Fehlerbehandler zu schreiben, der das Gleiche tut, wie Sie es für Ausnahmen tun.

8
Alex Weinstein

Wie in anderen Antworten angegeben, ist das Setzen des Error Handlers auf Exception Thrower die beste Möglichkeit, um Fehler in PHP zu behandeln. Ich benutze ein etwas einfacheres Setup:

set_error_handler(function ($errno, $errstr, $errfile, $errline ) {
        if (error_reporting()) {
                throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
        }
});

Bitte beachten Sie das error_reporting() Häkchen, um den Operator @ Am Laufen zu halten. Außerdem muss keine benutzerdefinierte Ausnahme definiert werden. PHP hat dafür eine Nice-Klasse.

Das Auslösen von Ausnahmen hat den großen Vorteil, dass mit der Ausnahme ein Stack-Trace verknüpft ist, sodass leicht ermittelt werden kann, wo das Problem liegt.

6
Josef Kufner

Betreff: "aber was genau ist der Unterschied zwischen einem Fehler und einer Ausnahme?"

Hier gibt es viele gute Antworten auf die Unterschiede. Ich füge nur etwas hinzu, worüber noch nicht gesprochen wurde - Leistung. Dies gilt insbesondere für den Unterschied zwischen dem Auslösen/Behandeln von Ausnahmen und dem Behandeln eines Rückkehrcodes (entweder Erfolg oder ein Fehler). In der Regel bedeutet dies in PHP, dass false oder null zurückgegeben werden. Sie können jedoch detaillierter sein, z. B. beim Hochladen von Dateien: http://php.net/manual/en/ features.file-upload.errors.php Sie könnten sogar ein Exception-Objekt zurückgeben!

Ich habe einige Performanceläufe in verschiedenen Sprachen/Systemen durchgeführt. Im Allgemeinen ist die Ausnahmebehandlung etwa 10.000x langsamer als das Überprüfen auf einen Fehlerrückgabecode.

Wenn es also unbedingt fertig ausgeführt werden muss, bevor es überhaupt begonnen hat, haben Sie Pech, weil es keine Zeitreisen gibt. Ohne Zeitreise sind Rückkehrcodes die schnellste verfügbare Option.

Bearbeiten:

PHP ist stark für die Ausnahmebehandlung optimiert. Tests in der Praxis zeigen, dass das Auslösen einer Ausnahme nur 2-10x langsamer ist als das Zurückgeben eines Werts.

4
evan

Ich denke, die Antwort, die Sie suchen, ist die;

Fehler sind das Standardmaterial, an das Sie gewöhnt sind, wie das Echo einer nicht existierenden $ -Variablen.
Ausnahmen gibt es erst ab PHP 5) und wenn es um Objekte geht.

Um es einfach zu halten:

Eine Ausnahme bilden die Fehler, die beim Umgang mit Objekten auftreten. Die try/catch-Anweisung lässt Sie jedoch etwas dagegen unternehmen und wird ähnlich wie die if/else-Anweisung verwendet. Versuchen Sie dies zu tun, wenn das Problem keine Rolle spielt, tun Sie dies.

Wenn Sie keine Ausnahme "abfangen", wird dies zu einem Standardfehler.

Fehler sind die grundlegenden PHP-Fehler, die normalerweise Ihr Skript anhalten.

Try/Catch wird häufig zum Herstellen von Datenbankverbindungen wie PDO verwendet. Dies ist in Ordnung, wenn Sie das Skript umleiten möchten oder wenn die Verbindung nicht funktioniert. Wenn Sie jedoch nur die Fehlermeldung anzeigen und das Skript beenden möchten, brauchen Sie sie nicht. Die nicht abgefangene Ausnahme wird zu einem schwerwiegenden Fehler. Sie können auch eine standortweite Fehlerbehandlungseinstellung verwenden.

Ich hoffe, das hilft

4
Lan

Ausnahmen werden absichtlich durch Code ausgelöst, der einen Throw verwendet, Fehler ... nicht so sehr.

Fehler entstehen durch etwas, das normalerweise nicht behandelt wird. (E/A-Fehler, TCP/IP-Fehler, Nullreferenzfehler)

1
cgp

Ich habe vor, Ihnen eine höchst ungewöhnliche Diskussion über die Fehlerkontrolle zu geben.

Ich habe vor Jahren einen sehr guten Fehlerbehandler in eine Sprache eingebaut, und obwohl sich einige Namen geändert haben, sind die Prinzipien der Fehlerverarbeitung heute dieselben. Ich hatte ein speziell entwickeltes Multitasking-Betriebssystem und musste in der Lage sein, Datenfehler auf allen Ebenen ohne Speicherlecks, Stapelwachstum oder Abstürze zu beheben. Was folgt, ist mein Verständnis, wie Fehler und Ausnahmen funktionieren müssen und wie sie sich unterscheiden. Ich werde nur sagen, ich habe kein Verständnis dafür, wie die Interna von try catch funktionieren, also rate ich zu einem gewissen Grad.

Das erste, was unter dem Deckmantel der Fehlerverarbeitung passiert, ist, von einem Programmzustand in einen anderen zu springen. Wie geht das? Ich komme darauf zurück.

Historisch gesehen sind Fehler älter und einfacher, und Ausnahmen sind neuer und etwas komplexer und leistungsfähiger. Fehler funktionieren einwandfrei, bis Sie sie in die Luft sprudeln müssen. Dies entspricht der Übergabe eines schwierigen Problems an Ihren Vorgesetzten.

Fehler können Zahlen sein, wie Fehlernummern, und manchmal mit einer oder mehreren verknüpften Zeichenfolgen. Wenn beispielsweise ein Dateilesefehler auftritt, können Sie möglicherweise angeben, um was es sich handelt, und möglicherweise ordnungsgemäß fehlschlagen. (Hay, es ist ein Schritt weiter als früher.)

Über Ausnahmen wird nicht oft gesagt, dass Ausnahmen Objekte sind, die auf einem speziellen Ausnahmestapel liegen. Es ist wie ein Rückgabestapel für den Programmablauf, enthält jedoch nur einen Rückgabestatus für Fehlerversuche und -abfragen. (Früher nannte ich sie ePush und ePop, und? Abbruch war ein bedingter Wurf, der ePop auslöste und auf dieses Niveau zurückkehrte, während Abbruch ein voller Würfel oder Ausstieg war.)

Am unteren Rand des Stapels befinden sich die Informationen über den ersten Aufrufer, das Objekt, das über den Status informiert ist, in dem der äußere Versuch gestartet wurde. Dies ist häufig der Zeitpunkt, zu dem Ihr Programm gestartet wurde. Darüber hinaus ist die nächste Ebene auf dem Stapel, wobei up die untergeordneten und down die übergeordneten Ebenen sind, das Ausnahmeobjekt des nächsten inneren Try/Catch-Blocks.

Wenn Sie einen Versuch in einen Versuch stecken, stapeln Sie den inneren Versuch über den äußeren Versuch. Wenn im inneren Versuch ein Fehler auftritt und entweder der innere Fang ihn nicht verarbeiten kann oder der Fehler an den äußeren Versuch weitergeleitet wird, wird die Steuerung an den äußeren Fangblock (Objekt) übergeben, um zu prüfen, ob er den Fehler verarbeiten kann, d. H Ihr Vorgesetzter.

Was dieser Fehlerstapel also wirklich tut, ist, in der Lage zu sein, Programmfluss und Systemstatus zu markieren und wiederherzustellen, mit anderen Worten, es ermöglicht einem Programm, den Rückgabestapel nicht zum Absturz zu bringen und Dinge für andere (Daten) durcheinander zu bringen, wenn Dinge schief gehen. Dadurch wird auch der Status aller anderen Ressourcen wie Speicherzuordnungspools gespeichert, und die Ressourcen können nach Abschluss des Abfangs bereinigt werden. Im Allgemeinen kann dies eine sehr komplizierte Angelegenheit sein, weshalb die Ausnahmebehandlung häufig langsam ist. Im Allgemeinen muss einiges an Status in diese Ausnahmeblöcke gehen.

Ein Try/Catch-Block legt also einen Zustand fest, in den zurückgekehrt werden kann, wenn alles andere durcheinander gerät. Es ist wie bei einem Elternteil. Wenn unser Leben durcheinander gerät, können wir auf den Schoß unserer Eltern zurückfallen und sie werden es wieder gut machen.

Hoffe, ich habe dich nicht enttäuscht.

1
Elliptical view

Sobald set_error_handler () definiert ist, ähnelt der Fehlerhandler dem von Exception. Siehe Code unten:

 <?php
 function handleErrors( $e_code ) {
   echo "error code: " . $e_code . "<br>";
 }

 set_error_handler( "handleErrors" ); 

 trigger_error( "trigger a fatal error", E_USER_ERROR);
 echo "after error."; //it would run if set_error_handler is defined, otherwise, it wouldn't show
?>
0
N Zhang

In PHP 7.1 und höher kann ein catch - Block mehrere Ausnahmen mit dem Pipe-Zeichen (|) angeben. Dies ist nützlich, wenn verschiedene Ausnahmen aus verschiedenen Klassenhierarchien behandelt werden das Gleiche.

try {
  // do something
} catch (Error | Exception $e) {
  echo $e->getMessage();
}
0
Jehong Ahn