Może brakuje mi tego gdzieś w podręczniku PHP, ale jaka dokładnie jest różnica między błędem a wyjątkiem? Jedyną różnicą, jaką widzę, jest to, że błędy i wyjątki są obsługiwane inaczej. Ale co powoduje wyjątek, a co powoduje błąd?
Może brakuje mi tego gdzieś w podręczniku PHP, ale jaka dokładnie jest różnica między błędem a wyjątkiem? Jedyną różnicą, jaką widzę, jest to, że błędy i wyjątki są obsługiwane inaczej. Ale co powoduje wyjątek, a co powoduje błąd?
Odpowiedzi:
Wyjątki są wyrzucane - są one przeznaczone do złapany. Błędy są zazwyczaj nie do naprawienia. Powiedzmy na przykład - masz blok kodu, który wstawi wiersz do bazy danych. Możliwe, że to wywołanie się nie powiedzie (zduplikowany identyfikator) - będziesz chciał mieć „Błąd”, który w tym przypadku jest „Wyjątkiem”. Kiedy wstawiasz te wiersze, możesz zrobić coś takiego
try {
$row->insert();
$inserted = true;
} catch (Exception $e) {
echo "There was an error inserting the row - ".$e->getMessage();
$inserted = false;
}
echo "Some more stuff";
Wykonywanie programu będzie kontynuowane - ponieważ „złapałeś” wyjątek. Wyjątek zostanie potraktowany jako błąd, chyba że zostanie przechwycony. Pozwoli ci to kontynuować wykonywanie programu również po jego niepowodzeniu.
Throwable
interfejsu), dając znacznie bardziej wyrazisty i absolutny sposób na rozróżnienie i prawidłowe przekazanie obu rzeczy problemy i komunikaty doradcze
Error
VS a potomkami Exception
.
Zwykle korzystam set_error_handler
z funkcji, która przyjmuje błąd i rzuca wyjątek, więc cokolwiek się stanie, będę miał tylko wyjątki do załatwienia. Nie więcej @file_get_contents
po prostu ładny i schludny try / catch.
W sytuacjach debugowania mam również program obsługi wyjątków, który wyprowadza stronę podobną do asp.net. Publikuję to w drodze, ale na żądanie opublikuję później przykładowe źródło.
edytować:
Dodawanie zgodnie z obietnicą, wyciąłem i wkleiłem część kodu, aby utworzyć próbkę. Mam zapisane poniżej do pliku na mojej stacji roboczej, można JUŻ NIE zobaczyć wyniki tutaj (ponieważ link jest uszkodzony).
<?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' );
?>
Odpowiedź zasługuje na rozmowę o słoniu w pokoju
Błędy to stary sposób obsługi warunków błędu w czasie wykonywania. Zwykle kod wywołuje coś podobnego set_error_handler
przed wykonaniem jakiegoś kodu. Podążanie za tradycją asemblera przerywa. Oto jak wyglądałby kod w języku BASIC.
on error :divide_error
print 1/0
print "this won't print"
:divide_error
if errcode = X
print "divide by zero error"
Trudno było się upewnić, set_error_handler
że zostanie wywołana z odpowiednią wartością. Co gorsza, można by wywołać oddzielną procedurę, która zmieni procedurę obsługi błędów. Ponadto wiele razy połączenia były przeplatane set_error_handler
połączeniami i programami obsługi. Kod szybko wymknął się spod kontroli. Obsługa wyjątków przyszła na ratunek poprzez sformalizowanie składni i semantyki tego, co naprawdę robi dobry kod.
try {
print 1/0;
print "this won't print";
} catch (DivideByZeroException $e) {
print "divide by zero error";
}
Brak oddzielnej funkcji lub ryzyka wywołania niewłaściwego programu obsługi błędów. Gwarantujemy, że kod będzie teraz znajdował się w tym samym miejscu. Dodatkowo otrzymujemy lepsze komunikaty o błędach.
PHP miał tylko obsługę błędów, podczas gdy wiele innych języków już ewoluowało do preferowanego modelu obsługi wyjątków. Ostatecznie twórcy PHP zaimplementowali obsługę wyjątków. Ale prawdopodobnie obsługują stary kod, zachowali obsługę błędów i zapewnili sposób, aby obsługa błędów wyglądała jak obsługa wyjątków. Poza tym nie ma gwarancji, że jakiś kod nie zresetuje procedury obsługi błędów, co było dokładnie tym, do czego miała zapewnić obsługa wyjątków.
Ostatnia odpowiedź
Błędy, które zostały zakodowane przed wdrożeniem obsługi wyjątków, są prawdopodobnie nadal błędami. Nowe błędy są prawdopodobnie wyjątkami. Ale nie ma projektu ani logiki, do której są błędy i które są wyjątkami. Opiera się tylko na tym, co było dostępne w momencie kodowania, i preferencjach programisty, który to koduje.
Jedną rzeczą do dodania jest obsługa wyjątków i błędów. Dla twórców aplikacji, zarówno błędy, jak i wyjątki to „złe rzeczy”, które chcesz zarejestrować, aby dowiedzieć się o problemach, które ma Twoja aplikacja - aby klienci mieli lepsze wrażenia w dłuższej perspektywie.
Dlatego warto napisać procedurę obsługi błędów, która robi to samo, co w przypadku wyjątków.
Jak stwierdzono w innych odpowiedziach, ustawienie obsługi błędów na rzucający wyjątek jest najlepszym sposobem obsługi błędów w PHP. Używam nieco prostszej konfiguracji:
set_error_handler(function ($errno, $errstr, $errfile, $errline ) {
if (error_reporting()) {
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
}
});
Proszę zwrócić uwagę na error_reporting()
czek, który należy zachować@
operator pracować. Ponadto nie ma potrzeby definiowania niestandardowego wyjątku, PHP ma do tego jedną fajną klasę.
Ogromną zaletą rzucania wyjątków jest to, że wyjątek ma skojarzony z nim ślad stosu, więc łatwo jest znaleźć problem.
Re: "ale jaka dokładnie jest różnica między błędem a wyjątkiem?"
Istnieje wiele dobrych odpowiedzi na temat różnic. Dodam tylko coś, o czym jeszcze nie mówiono - wydajność. W szczególności dotyczy to różnicy między zgłaszaniem / obsługą wyjątków a obsługą kodu powrotu (powodzenie lub błąd). Zwykle w php oznacza to powrót false
lub null
, ale mogą być bardziej szczegółowe, na przykład przy przesyłaniu plików: http://php.net/manual/en/features.file-upload.errors.php Możesz nawet zwrócić obiekt wyjątku !
Wykonałem kilka testów wydajnościowych w różnych językach / systemach. Ogólnie rzecz biorąc, obsługa wyjątków jest około 10 000 razy wolniejsza niż sprawdzanie kodu powrotu błędu.
Tak więc, jeśli absolutnie, pozytywnie musi zakończyć wykonywanie, zanim jeszcze się zaczęło - cóż, nie masz szczęścia, ponieważ podróże w czasie nie istnieją. Bez podróży w czasie kody zwrotne są najszybszą dostępną opcją.
Edytować:
PHP jest wysoce zoptymalizowany pod kątem obsługi wyjątków. Testy w świecie rzeczywistym pokazują, że rzucanie wyjątku jest tylko 2-10x wolniejsze niż zwracanie wartości.
Myślę, że odpowiedź, której szukasz, jest taka;
Błędy to standardowe rzeczy, do których jesteś przyzwyczajony, na przykład wywoływanie echa zmiennej $, która nie istnieje.
Wyjątki występują tylko od PHP 5 i występują w przypadku obiektów.
Aby było to proste:
Wyjątkami są błędy, które otrzymujesz podczas pracy z obiektami. Instrukcja try / catch pozwala jednak coś z nimi zrobić i jest używana podobnie jak instrukcja if / else. Spróbuj to zrobić, jeśli problem nie ma znaczenia, zrób to.
Jeśli nie „złapiesz” wyjątku, zamieni się on w standardowy błąd.
Błędy to podstawowe błędy php, które zwykle powodują zatrzymanie skryptu.
Try / catch jest często używane do nawiązywania połączeń z bazą danych, takich jak PDO, co jest w porządku, jeśli chcesz przekierować skrypt lub zrobić coś innego, jeśli połączenie nie działa. Ale jeśli chcesz po prostu wyświetlić komunikat o błędzie i zatrzymać skrypt, nie potrzebujesz tego, nieprzechwycony wyjątek zamienia się w błąd krytyczny. Możesz też użyć ustawienia obsługi błędów w całej witrynie.
Mam nadzieję, że to pomoże
W PHP 7.1 i nowszych blok catch może określać wiele wyjątków za pomocą znaku potoku (|). Jest to przydatne, gdy różne wyjątki z różnych hierarchii klas są obsługiwane tak samo.
try {
// do something
} catch (Error | Exception $e) {
echo $e->getMessage();
}
Wyjątki są celowo generowane przez kod za pomocą rzutu, błędy ... nie tak bardzo.
Błędy pojawiają się w wyniku czegoś, co zwykle nie jest obsługiwane. (Błędy we / wy, błędy TCP / IP, błędy odniesienia zerowego)
Zamierzam przedstawić najbardziej niezwykłe omówienie kontroli błędów.
Wiele lat temu zbudowałem bardzo dobrą obsługę błędów w języku i chociaż niektóre nazwy się zmieniły, zasady przetwarzania błędów są dziś takie same. Miałem niestandardowy wielozadaniowy system operacyjny i musiałem móc odzyskać dane po błędach danych na wszystkich poziomach bez wycieków pamięci, wzrostu stosu lub awarii. Poniżej przedstawiam moje zrozumienie tego, jak muszą działać błędy i wyjątki i czym się różnią. Powiem tylko, że nie rozumiem, jak działają elementy wewnętrzne try catch, więc do pewnego stopnia zgaduję.
Pierwszą rzeczą, która dzieje się pod osłoną przetwarzania błędów, jest przeskakiwanie z jednego stanu programu do drugiego. Jak to się robi? Dojdę do tego.
Historycznie rzecz biorąc, błędy są starsze i prostsze, a wyjątki są nowsze, nieco bardziej złożone i wydajne. Błędy działają prawidłowo, dopóki nie trzeba ich rozwikłać, co jest równoznaczne z przekazaniem trudnego problemu przełożonemu.
Błędy mogą być liczbami, takimi jak numery błędów, a czasami z jednym lub większą liczbą skojarzonych ciągów. Na przykład, jeśli wystąpi błąd odczytu pliku, możesz być w stanie zgłosić, co to jest i prawdopodobnie nie powiedzie się. (Hay, to krok naprzód od zwykłego upadku, jak za dawnych czasów).
O wyjątkach nie mówi się często, że wyjątki to obiekty ułożone warstwowo na specjalnym stosie wyjątków. Jest jak stos zwrotny dla przepływu programu, ale przechowuje stan powrotu tylko dla prób i przechwyceń błędów. (Nazywałem je ePush i ePop, i? Przerwij był rzutem warunkowym, który będzie ePop i odzyskał do tego poziomu, podczas gdy Abort był pełną kostką lub wyjściem.)
Na dole stosu znajduje się informacja o początkowym obiekcie wywołującym, obiekcie, który wie o stanie, w którym została uruchomiona zewnętrzna próba, czyli często w momencie uruchomienia programu. Poza tym, kolejna warstwa na stosie, gdzie góra jest dziećmi, a dół to rodzice, jest obiektem wyjątku następnego wewnętrznego bloku try / catch.
Jeśli spróbujesz wewnątrz próby, umieszczasz wewnętrzną próbę na zewnętrznej próbie. Gdy wystąpi błąd w wewnętrznej try i albo wewnętrzny catch nie może go obsłużyć, albo błąd zostanie zgłoszony do zewnętrznej try, wówczas kontrola jest przekazywana do zewnętrznego bloku catch (obiekt), aby sprawdzić, czy może obsłużyć błąd, tj. twój przełożony.
Więc to, co naprawdę robi ten stos błędów, to możliwość oznaczania i przywracania przepływu programu i stanu systemu, innymi słowy, pozwala programowi nie zawiesić stosu zwrotnego i zepsuć rzeczy innym (dane), gdy coś pójdzie nie tak. W ten sposób zapisuje również stan wszystkich innych zasobów, takich jak pule alokacji pamięci, i może je wyczyścić po zakończeniu przechwytywania. Ogólnie rzecz biorąc, może to być bardzo skomplikowana sprawa i dlatego obsługa wyjątków jest często powolna. Ogólnie rzecz biorąc, do tych bloków wyjątków należy wprowadzić sporo stanu.
Tak więc blok try / catch ustawia stan, do którego można powrócić, jeśli wszystko inne zostanie pomieszane. To jest jak rodzic. Kiedy nasze życie się popsuje, możemy wrócić na kolana rodziców, a oni znowu wszystko będzie dobrze.
Mam nadzieję, że cię nie zawiodłem.
Po zdefiniowaniu set_error_handler () procedura obsługi błędów jest podobna do procedury wyjątków. Zobacz kod poniżej:
<?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
?>
Errors are generally unrecoverable
<- właściwie to nie jest prawda.E_ERROR
iE_PARSE
to dwa najczęstsze błędy nienaprawialne (istnieje kilka innych), ale zdecydowana większość błędów będziesz widzieć w dev podlegają zwrotowi (E_NOTICE
,E_WARNING
et al). Niestety obsługa błędów PHP jest kompletnym bałaganem - wszelkiego rodzaju rzeczy niepotrzebnie powodują błędy (na przykład ogromna większość funkcji systemu plików). Generalnie wyjątki to „sposób OOP”, ale niestety niektóre natywne API OOP PHP używają błędów zamiast wyjątków :-(