PHP:例外vsエラー?


116

多分私はそれをPHPマニュアルのどこかに欠けていますが、エラーと例外の正確な違いは何ですか?私が見ることができる唯一の違いは、エラーと例外が異なる方法で処理されることです。しかし、何が例外を引き起こし、何がエラーを引き起こしますか?

回答:


87

例外がスローされます -キャッチすることを目的としています。エラーは通常回復不能です。たとえば、データベースに行を挿入するコードブロックがあるとします。この呼び出しが失敗する可能性があります(IDの重複)-この場合は「例外」である「エラー」が必要になるでしょう。これらの行を挿入するときは、次のようなことができます

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

echo "Some more stuff";

プログラムの実行は続行されます-例外を「キャッチ」したためです。キャッチされない限り、例外はエラーとして扱われます。失敗した後もプログラムの実行を継続できます。


29
Errors are generally unrecoverable<-実際、これは本当ではありません。E_ERRORE_PARSEは、最も一般的な2つの回復不可能なエラーです(他にもいくつかあります)が、devに表示されるエラーの大部分は回復可能です(E_NOTICEなどE_WARNING)。残念ながら、PHPのエラー処理は完全に混乱しています。あらゆる種類のものが不必要にエラーをトリガーします(ファイルシステム関数の大部分など)。一般的な例外は「OOP方式」ですが、残念ながら、PHPのネイティブOOP APIの一部は例外ではなくエラーを使用します:-(
DaveRandom

1
@DaveRandom E_NOTICE、E_WARNINGは定義上「エラー」ではありませんか?私はいつもそれらを「メッセージ」PHPがプログラマーに彼/彼女が書いたコードに問題があるかもしれないことを通知するために表示するものと思っていました。
slhsen

2
@slhsen問題は本当にひどい用語です。これらのメッセージのすべての形式は、PHPの「エラー処理システム」を通過します。意味論的には、これらのイベントはすべて「エラー」です。エラー」を参照してください。ありがたいことに、今後のPHP7では、これらの問題の大部分を(新しいThrowableインターフェイスを使用して)キャッチ可能な例外に変換することで、この混乱を解決する方法が用意されました。問題と助言メッセージ
DaveRandom 2015

「プログラムの実行は継続します」は変わったと思いますか?PHPは(「例外がスローされた場合は、ステートメントの次のコードが実行されることはありません」と言うのでphp.net/manual/en/language.exceptions.php
ロバート・シンクレア

1
OPが意味したのは、ErrorVSの子孫との子孫の違いの方が多かったと思いますException
XedinUnknown 2018年

55

私は通常set_error_handler、エラーを受け取り、例外をスローする関数に行きます。これにより、何が起こっても、処理する例外があります。@file_get_contents素敵できちんとしたtry / catchは不要です。

デバッグ状況では、ページのようなasp.netを出力する例外ハンドラーもあります。私はこれを道路に投稿していますが、要求された場合は後でサンプルソースを投稿します。

編集:

さらに、約束どおり、サンプルを作成するためにコードの一部を切り取って貼り付けました。私は私のワークステーション上のファイルに以下の保存した、次のことができNO LONGERここで結果を見ない(リンクが壊れているため)。

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

?>

それは役に立ちます。私がPHPに対処するために作られている時間を緩和するための何かが役立ちます。:-)
Jason Baker、

いいコード、ありがとう。クラスXの出所はわかりませんが、その目的は何ですか?
アレック

「set_exception_handler( 'global_exception_handler');」以下のすべて これは単なるデモであり、必要はありません。通常は例外ではないエラー状況で何が起こるかを示すためだけです。
クリス

標準のPHPでは、一般的なエラーハンドラーからスローされるErrorExceptionが具体的に定義されています。投稿の編集と更新を許可しますか?
Tiberiu-Ionuțスタン2013

@ Tiberiu-IonuțStan:もちろんですが、実際の例は同期していません。また、最近は私はおそらくに人々を指したいgithub.com/theredhead/red.web/blob/master/src/lib/bootstrap.phpからprivate-void.com代わりに。
クリス

21

答えは部屋の中の象について話すに値する

エラーは、実行時にエラー条件を処理する古い方法です。通常、コードは、コードを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のメーカーが例外処理を実装しました。しかし、古いコードをサポートする可能性が高いため、エラー処理を維持し、エラー処理を例外処理のようにする方法を提供しました。それを除いて、一部のコードがエラーハンドラーをリセットしないという保証はありません。これは、例外処理が提供することを意図していたものでした。

最終回答

例外処理が実装される前にコーディングされたエラーは、おそらくまだエラーです。新しいエラーはおそらく例外です。しかし、エラーや例外となるデザインやロジックはありません。それは、それがコーディングされたときに利用可能であったもの、およびそれをコーディングするプログラマーの好みに基づいています。


3
これが、例外とエラーが共存する本当の理由です。ゼロから設計する場合、phpにはどちらか一方のみを含める必要があります。
Tomas Zubiri 16

1
これは最も詳細で説明的なものなので、私の意見では最良の答えです。
ロバートKusznier

8

ここで追加することの1つは、例外とエラーの処理についてです。アプリケーション開発者にとって、エラーと例外はどちらも「悪いこと」であり、アプリケーションが抱える問題について学習するために記録する必要があります。これにより、長期的に顧客のエクスペリエンスが向上します。

したがって、例外に対して行うのと同じことを行うエラーハンドラを記述することは理にかなっています。


リンクを提供してくれてありがとう!
マイクムーア

@Alex Weinstein:リンクが壊れています
Marco Demaio、

7

他の回答で述べたように、エラーハンドラーを例外スローアーに設定することは、PHPでエラーを処理する最良の方法です。私は少し簡単な設定を使用します:

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

オペレーターの作業error_reporting()を続けるためのチェックに注意してください@。また、カスタム例外を定義する必要はありません。PHPにはそのための素晴らしいクラスが1つあります。

例外をスローすることの大きな利点は、例外にスタックトレースが関連付けられているため、問題の場所を簡単に見つけることができることです。


5

再:「エラーと例外の違いは何ですか?」

ここに違いについて多くの良い答えがあります。私はまだ話されていない何かを追加します-パフォーマンス。具体的には、これは、例外のスロー/処理と戻りコード(成功または何らかのエラー)の処理の違いです。通常、PHPで、この手段は返却falseまたはnull、彼らはそれ以上のそのようなファイルアップロードのように詳述することができます。http://php.net/manual/en/features.file-upload.errors.phpあなたも例外オブジェクトを返すことができます!

さまざまな言語/システムでいくつかのパフォーマンスを実行しました。一般的に言えば、例外処理はエラー戻りコードのチェックよりも約10,000倍遅くなります。

したがって、絶対に、それが開始する前に確実に実行を終了する必要がある場合、まあ、タイムトラベルが存在しないため、運が悪かったことになります。タイムトラベルがない場合、リターンコードは利用可能な最速のオプションです。

編集:

PHPは、例外処理用に高度に最適化されています。実際のテストでは、例外のスローは値を返すよりも2〜10倍遅いだけです。


3
もちろん、例外をスローすることによって失われるサイクルの量は、例外で得られる追加の記述力によって補われた以上のものです。特定の種類の例外をスローしたり、例外にデータを追加してエラーコードを含めることもできます。私もあなたの10,000 *の主張を真剣に疑っています。時間差が正しいとしても、実際のシナリオでreturn&if対new Execption、throw、catchを実行するのに費やした時間は、実行されたコードに比べて非常に少ないため、これは明らかに時期尚早の最適化です。例外をスローします。90%の時間を処理するのに適しています。
gnarf 2012

1
1. 10,000xは正確です-言語およびコンパイラオプションに基づいて多少の差異があります。2. null / falseを返す必要はありません。数値を返すことができます-すぐに最大MAX_ULONGの戻りコード。あるいは、失敗した文字列を返し、成功した文字列またはintまたはnullを確認することもできます。3.実際のシナリオでは、すべてのクロックサイクルがカウントされます。Facebookの毎日のアクティブユーザー数は5億5,200万人です。例外が2倍であり、ユーザー/パスのチェックに.001秒かかると仮定すると、毎日153時間の処理時間を節約できます。10,000xで175年節約できます。ログイン試行をチェックするためだけ-毎日。
evan

@evan:参考までに、ここでは例外を除いてコードをテストしましたが、遅くなることはないと思われます:stackoverflow.com/a/445094/260080
Marco Demaio

@MarcoDemaioその質問は、例外をスローせずに、try / catchブロックのみをカバーしています。より良いテストは、noexcept()で値を返し、except()で例外をスローすることです。また、複数の機能を介してバブルアップする必要があります。 stackoverflow.com/a/104375/505172によると、PHPの違いは実際には54倍です。私はリアルタイムで自分のテストを実行しましたが、2-10倍遅いようです。これは予想よりずっと良いです。
エヴァン2013年

@evan:私は心配しません。例外を使用して、予期しない/回復不可能なエラーを追跡するだけなので、100倍遅くても気になりません。私の心配は、try / catchブロックを追加するだけでコードを遅くすることでした。
Marco Demaio 2013年

4

あなたが探しているanwserはそれだと思います。

エラーは、存在しない$ variableをエコーするなど、慣れている標準的なものです。
例外はPHP 5以降のみで、オブジェクトを処理するときに発生します。

シンプルに保つには:

例外は、オブジェクトを処理するときに発生するエラーです。ただし、try / catchステートメントを使用すると、それらに対して何かを行うことができ、if / elseステートメントと同じように使用されます。これを試してみてください。問題がある場合は問題ありません。

例外を「キャッチ」しないと、標準エラーになります。

エラーは、通常スクリプトを停止させるphpの根本的なエラーです。

Try / catchは、PDOのようなデータベース接続を確立するためによく使用されます。これは、スクリプトをリダイレクトしたい場合や、接続が機能しない場合に何かを行う場合に適しています。ただし、エラーメッセージを表示してスクリプトを停止するだけの場合は、それを必要としないと、キャッチされなかった例外が致命的なエラーになります。または、サイト全体のエラー処理設定を使用することもできます。

それが役に立てば幸い


3
例外は、PHPの手続き型コードでも使用できます。
Tiberiu-Ionuțスタン2013

2

PHP 7.1以降では、catchブロックでパイプ(|)文字を使用して複数の例外を指定できます。これは、異なるクラス階層からの異なる例外が同じように処理される場合に役立ちます。

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

1

例外は、throw、errors ...を使用するコードによって意図的にスローされます。

エラーは、通常は処理されないものの結果として発生します。(IOエラー、TCP / IPエラー、null参照エラー)


1
これは必ずしも本当ではありません。多くの場合、エラーがチェックされ、リターンコードは必要に応じて意図的に送り返されます。実際、それはすべての非オブジェクト指向言語に当てはまります。例外は、それだけでなく、ルールの例外でもあります。どちらの場合も、何かがうまくいかず、気づかれ、対処する必要があります。PHPファイルのアップロードは、戻りコードによる意図的なエラー処理の一例です-php.net/manual/en/features.file-upload.errors.php
evan

1

私はあなたにエラー制御の最も変わった議論をするつもりです。

非常に優れたエラーハンドラーを数年前に言語に組み込みましたが、一部の名前は変更されましたが、エラー処理の原則は現在も同じです。私はカスタムビルドのマルチタスクOSを使用しており、メモリリーク、スタックの増加、クラッシュを発生させることなく、すべてのレベルのデータエラーから回復できなければなりませんでした。したがって、エラーと例外がどのように動作する必要があるか、およびそれらがどのように異なるかについての私の理解は次のとおりです。トライキャッチの内部がどのように機能するのか私にはわからないので、ある程度は推測しています。

エラー処理のために最初に発生するのは、プログラムのある状態から別の状態にジャンプすることです。それはどのように行われますか?私はそれに行きます。

歴史的に、エラーはより古くて単純であり、例外はより新しく、もう少し複雑で機能的です。エラーは、それらをバブルアップする必要があるまで正常に機能します。これは、上司に困難な問題を渡すことと同じです。

エラーは、エラー番号のような数値である場合があり、1つ以上の関連する文字列を伴う場合もあります。たとえば、ファイル読み取りエラーが発生した場合、それが何であるかを報告でき、おそらく正常に失敗する可能性があります。(ああ、それは昔のようにクラッシュするだけのステップアップです。)

例外についてよく言われないのは、例外が特別な例外スタックに階層化されたオブジェクトであることです。これはプログラムフローのリターンスタックのようなものですが、エラーの試行とキャッチのためだけに戻り状態を保持します。(私は以前それらをePushとePopと呼んでいましたが、?AbortはePopとそのレベルに回復する条件付きスローでしたが、Abortは完全なダイまたは出口でした。)

スタックの一番下には、最初の呼び出し元に関する情報があります。これは、外側の試行が開始されたときの状態を知っているオブジェクトです。これは、多くの場合、プログラムが開始されたときです。その上、またはスタックの次のレイヤーは、upが子で、downが親で、次の内側のtry / catchブロックの例外オブジェクトです。

トライの内側にトライを入れると、内側のトライを外側のトライの上に重ねます。内部トライでエラーが発生し、内部キャッチがそれを処理できないか、エラーが外部トライにスローされると、制御が外部キャッチブロック(オブジェクト)に渡され、エラーを処理できるかどうかが確認されます。あなたの上司。

したがって、このエラースタックが実際に行うことは、プログラムフローとシステム状態をマークおよび復元できることです。つまり、問題が発生したときに、プログラムがリターンスタックをクラッシュさせたり、他のもの(データ)を壊したりすることがないようにします。したがって、メモリ割り当てプールなどの他のリソースの状態も保存されるため、キャッチが行われたときにそれらをクリーンアップできます。一般に、これは非常に複雑なことになる可能性があるため、例外処理が遅くなることがよくあります。一般に、これらの例外ブロックに入るにはかなりの状態が必要です。

したがって、try / catchブロックの種類は、他のすべてがめちゃくちゃになった場合に戻ることができるように状態を設定します。それは親のようなものです。私たちの生活がめちゃくちゃになったとき、私たちは親の膝の上にフォールバックすることができ、彼らは再び大丈夫にします。

私があなたを失望させなかったことを願っています。


1

このコメントを追加できます

function doSomething()
{
   /** @noinspection PhpUnhandledExceptionInspection */
   throw new Exception();
}

0

set_error_handler()が定義されると、エラーハンドラーは例外のハンドラーに似ています。以下のコードを参照してください。

 <?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
?>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.