PHP: исключения против ошибок?

Возможно, я пропустил его где-то в руководстве по PHP, но в чем же разница между ошибкой и исключением? Единственное отличие, которое я вижу, это то, что ошибки и исключения обрабатываются по-разному. Но что вызывает исключение и что вызывает ошибку?

Ответ 1

Исключения thrown - они должны быть пойманы. Ошибки обычно невосстанавливаются. Скажем, например, у вас есть блок кода, который будет вставлять строку в базу данных. Возможно, этот вызов не удался (дублированный идентификатор) - вы захотите иметь "ошибку", которая в этом случае является "Исключением". Когда вы вставляете эти строки, вы можете сделать что-то вроде этого

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

echo "Some more stuff";

Выполнение программы будет продолжаться - потому что вы "поймали" исключение. Исключение будет считаться ошибкой, если оно не будет обнаружено. Это позволит вам продолжить выполнение программы после ее срыва.

Ответ 2

Я обычно set_error_handler к функции, которая принимает ошибку и генерирует исключение, так что, что бы ни случилось, у меня будут только исключения с. Не более @file_get_contents просто приятный и аккуратный try/catch.

В ситуациях отладки у меня также есть обработчик исключений, который выводит страницу asp.net как. Я размещаю это на дороге, но по запросу я отправлю пример источника позже.

изменить:

Дополнение, как и было обещано, я вырезал и вставлял часть моего кода вместе, чтобы сделать образец. Я сохранил файл ниже на моей рабочей станции, вы можете увидеть результаты здесь.

<?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' );

?>

Ответ 3

Одна вещь, которую нужно добавить здесь, - это обрабатывать исключения и ошибки. Для разработчиков приложений, как ошибки, так и исключения - это "плохие вещи", которые вы хотите записать, чтобы узнать о проблемах, которые имеет ваше приложение, - чтобы ваши клиенты имели лучший опыт в долгосрочной перспективе.

Поэтому имеет смысл написать обработчик ошибок, который делает то же, что и для исключений.

Ответ 4

Ответ заслуживает того, чтобы говорить о слоне в комнате

Ошибки - это старый способ обработки условия ошибки во время выполнения. Как правило, код выполняет вызов с чем-то вроде set_error_handler перед выполнением некоторого кода. Следуя традиции прерывания языка ассемблера. Вот как выглядит код BASIC.

on error :divide_error

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

:divide_error

if errcode = X
   print "divide by zero error"

Было трудно убедиться, что set_error_handler будет вызываться с правильным значением. И что еще хуже, вызов может быть сделан для отдельной процедуры, которая изменит обработчик ошибок. Плюс много раз звонки были перемежаны с помощью вызовов set_error_handler и обработчиков. Кодексу было легко быстро выйти из-под контроля. Обработка исключений пришла на помощь путем формализации синтаксиса и семантики того, что действительно делал хороший код.

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

Нет отдельной функции или риска вызова неправильного обработчика ошибок. Теперь код гарантированно находится в одном месте. Плюс мы получаем лучшие сообщения об ошибках.

PHP использовал только обработку ошибок, когда многие другие языки уже превратились в предпочтительную модель обработки исключений. В конечном итоге разработчики PHP реализовали обработку исключений. Но, вероятно, поддержка старого кода, они сохраняли обработку ошибок и предоставляли способ заставить обработку ошибок выглядеть как обработка исключений. Кроме того, нет никакой гарантии, что какой-то код не может reset обработчика ошибок, который должен был обеспечить именно обработка исключений.

Окончательный ответ

Ошибки, которые были закодированы до обработки исключений, были реализованы, вероятно, все еще ошибки. Новые ошибки, вероятно, являются исключениями. Но нет никакой конструкции или логики, к которой относятся ошибки и которые являются исключениями. Он просто основывался на том, что было доступно в то время, когда оно было закодировано, и предпочтении программиста, кодирующего его.

Ответ 5

Re: "но в чем же разница между ошибкой и исключением?"

Здесь есть много хороших ответов о различиях. Я просто добавлю то, о чем еще не говорили - производительность. В частности, это разница между исключениями броска/обработки и обработкой кода возврата (либо успеха, либо некоторой ошибки). Обычно в php это означает возврат false или null, но они могут быть более подробными, например, при загрузке файла: http://php.net/manual/en/features.file-upload.errors.php Вы даже можете вернуть объект Exception!

Я сделал несколько прогонов производительности на разных языках/системах. Вообще говоря, обработка исключений примерно на 10 000 раз медленнее, чем проверка кода возврата ошибки.

Итак, если это абсолютно необходимо, нужно закончить выполнение, прежде чем он даже начнется - ну, вам не повезло, потому что путешествия во времени не существует. Без временного путешествия наиболее эффективными являются коды возврата.

Edit:

PHP оптимизирован для обработки исключений. Тесты реального мира показывают, что выброс исключения составляет всего 2-10x медленнее, чем возврат значения.

Ответ 6

Как указано в других ответах, установка обработчика ошибок для метаданных исключений - лучший способ обработки ошибок в PHP. Я использую немного более простую настройку:

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

Обратите внимание на проверку error_reporting(), чтобы оператор @ работал. Кроме того, нет необходимости определять настраиваемое исключение, PHP имеет для этого один класс.

Большим преимуществом исключений бросания является то, что исключение имеет связанную с ним трассировку стека, поэтому легко найти, где проблема.

Ответ 7

Я думаю, что anwser, которого вы ищете, это;

Ошибки - это стандартный материал, к которому вы привыкли, например, повторение переменной $, которая не существует.
Исключения выполняются только с PHP 5 и далее при работе с объектами.

Чтобы это было просто:

Исключения - это ошибки, возникающие при работе с объектами. Операция try/catch позволяет вам что-то делать с ними, и используется так же, как и оператор if/else. Попытайтесь сделать это, если проблема, не имеет значения, сделайте это.

Если вы не "поймаете" исключение, то оно превратится в стандартную ошибку.

Ошибки - это фундаментальные ошибки php, которые обычно останавливают ваш script.

Try/catch часто используется для установления соединений с базой данных, таких как PDO, что прекрасно, если вы хотите перенаправить script или сделать что-то еще, если соединение не работает. Но если вы просто хотите отобразить сообщение об ошибке и остановить script, тогда вам это не понадобится, неперехваченное исключение превратится в фатальную ошибку. Или вы также можете использовать настройку обработки ошибок на уровне сайта.

Надеюсь, что поможет

Ответ 8

Исключения умышленно забрасываются кодом с помощью броска, ошибок... не так много.

Ошибки возникают в результате чего-то, что обычно не обрабатывается. (Ошибки ввода-вывода, ошибки TCP/IP, ошибки нулевой ссылки)

Ответ 9

Я намерен дать вам самое необычное обсуждение контроля ошибок.

Я построил очень хороший обработчик ошибок на языке много лет назад, и хотя некоторые из имен были изменены, принципы обработки ошибок сегодня одинаковы. У меня была специально построенная многозадачная ОС, и мне приходилось восстанавливать ошибки данных на всех уровнях без утечек памяти, роста стека или сбоев. Итак, что следует за моим пониманием того, как должны действовать ошибки и исключения и как они отличаются. Я просто скажу, что у меня нет понимания того, как работают внутренние попытки try catch, поэтому я догадываюсь в какой-то мере.

Первое, что происходит под обложками для обработки ошибок, - это переход из одного состояния программы в другое. Как это делается? Я доберусь до этого.

Исторически, ошибки более старые и более простые, а исключения новее и немного сложнее и способны. Ошибки работают нормально, пока вам не понадобится их пузырь, что эквивалентно передаче сложной проблемы вашему руководителю.

Ошибки могут быть числами, такими как номера ошибок, а иногда и с одной или несколькими связанными строками. Например, если возникает ошибка чтения файла, вы можете сообщить, что это такое и, возможно, изящно выйти из строя. (Хей, это шаг от просто грохота, как в старые времена.)

То, что нередко говорят об исключениях, состоит в том, что исключения - это объекты, наложенные на специальный стек исключений. Это похоже на стек возврата для потока программы, но он содержит состояние возврата только для попыток ошибок и уловов. (Я называл их ePush и ePop, и? Abort был условным броском, который бы ePop и восстанавливался до этого уровня, в то время как Abort был полной смертью или выходом.)

В нижней части стека находится информация о начальном вызывающем объекте, который знает о состоянии при запуске внешней попытки, что часто происходит при запуске вашей программы. Вдобавок к этому, или следующий слой в стеке, с тем, чтобы быть дочерними, а не являющимися родителями, является объектом исключения следующего внутреннего блока try/catch.

Если вы попробуете попробовать попробовать, вы укладываете внутреннюю попытку поверх внешней попытки. Когда во внутренней попытке возникает ошибка, и либо внутренний catch не может ее обработать, либо ошибка передается во внешнюю попытку, тогда управление передается внешнему блоку catch (object), чтобы увидеть, может ли он обрабатывать ошибку, т.е. ваш руководитель.

То, что действительно делает этот стек ошибок, - это возможность маркировать и восстанавливать поток программы и состояние системы, другими словами, она позволяет программе не сбрасывать стек возврата и беспорядок для других (данных), когда что-то происходит неправильно. Таким образом, он также сохраняет состояние любых других ресурсов, таких как пулы распределения памяти, и поэтому может очищать их при завершении catch. В общем, это может быть очень сложной задачей, и поэтому обработка исключений часто медленная. В общем, довольно немного состояний нужно входить в эти блоки исключений.

Таким образом, тип try/catch блокирует состояние, чтобы иметь возможность вернуться, если все остальное перепуталось. Это как родитель. Когда наши жизни перепутались, мы можем вернуться на родительский круг, и они снова сделают все правильно.

Надеюсь, я тебя не разочаровал.

Ответ 10

Когда задан параметр set_error_handler(), обработчик ошибок похож на Exception. См. Следующий код:

 <?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
?>