web-dev-qa-db-ja.com

PHP:例外対エラー?

多分PHPマニュアルで見逃していますが、エラーと例外の違いは何ですか?わかる唯一の違いは、エラーと例外の処理が異なることです。しかし、何が例外を引き起こし、何がエラーを引き起こしますか?

108
Jason Baker

例外は スロー -キャッチされることを意図しています。通常、エラーは回復不能です。たとえば、データベースに行を挿入するコードブロックがあるとします。この呼び出しが失敗する(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";

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

83
gnarf

私は通常 - 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' );

?>
53
Kris

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

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

最終回答

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

15

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

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

8
Alex Weinstein

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

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

_@_演算子が機能し続けるように、error_reporting()チェックに注意してください。また、カスタム例外を定義する必要はありません。PHPにはそのためのNiceクラスが1つあります。

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

6
Josef Kufner

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

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

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

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

編集:

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

4
evan

あなたが探しているアンカーはそれだと思います。

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

シンプルに保つには:

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

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

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

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

役立つことを願っています

4
Lan

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

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

1
cgp

エラー制御の最も珍しい議論を提供するつもりです。

私は何年も前に非常に優れたエラーハンドラを言語に組み込みました。名前の一部は変更されましたが、エラー処理の原則は今日と同じです。カスタムビルドマルチタスクOSがあり、メモリリーク、スタックの増加、クラッシュなしで、すべてのレベルでデータエラーから回復できる必要がありました。したがって、エラーと例外がどのように動作し、どのように異なるかについての私の理解は次のとおりです。 try catchの内部がどのように機能するのか理解していないので、何らかの尺度で推測しています。

エラー処理の裏で最初に起こることは、あるプログラム状態から別の状態にジャンプすることです。どうやって?それに行きます。

歴史的に、エラーはより古くて単純であり、例外はより新しく、もう少し複雑で機能的です。エラーはバブルアップするまで正常に機能します。これは、スーパーバイザーに難しい問題を渡すのと同じです。

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

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

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

Tryの内側にtryを配置すると、内側のtryを外側のtryの上にスタックします。内側のtryでエラーが発生し、内側のcatchがそれを処理できないか、エラーが外側のtryにスローされると、制御が外側のcatchブロック(オブジェクト)に渡され、エラーを処理できるかどうかが確認されます。あなたの上司。

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

したがって、try/catchブロックは、他のすべてが台無しになった場合に戻ることができるように状態を設定します。親のようなものです。私たちの生活が台無しになったら、私たちは親の膝に落ちて、彼らは再び大丈夫になります。

失望しなかったことを願っています。

1
Elliptical view

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
?>
0
N Zhang

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

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