警告を試す/キャッチすることはできますか?


358

一部のphpネイティブ関数からスローされる警告をキャッチして処理する必要があります。

具体的には:

array dns_get_record  ( string $hostname  [, int $type= DNS_ANY  [, array &$authns  [, array &$addtl  ]]] )

DNSクエリが失敗すると、警告がスローされます。

try/catchは警告が例外ではないため機能しません。

私には2つのオプションがあります:

  1. set_error_handler 私はそれを使用してページ内のすべての警告をフィルタリングする必要があるため、やり過ぎのように見えます(これは本当ですか?)。

  2. エラー報告/表示を調整して、これらの警告が画面にエコーされないようにしてから、戻り値を確認してください。の場合false、ホスト名のレコードは見つかりません。

ここでのベストプラクティスは何ですか?


1
stackoverflow.com/questions/136899/…は、このようなことについての良い議論です。
Mez、

削除された以下の回答がありましたか?所有者か誰かのどちらか?
user121196 2009

次も参照してください:stackoverflow.com/questions/1087365
dreftymac

@ user121196:はい。所有者による。
オービットのライトネスレース

回答:


373

エラーハンドラーの設定と復元

1つの可能性は、呼び出しの前に独自のエラーハンドラーを設定し、後で以前のエラーハンドラーを復元することrestore_error_handler()です。

set_error_handler(function() { /* ignore errors */ });
dns_get_record();
restore_error_handler();

このアイデアに基づいて、エラーをログに記録する再利用可能なエラーハンドラを作成できます。

set_error_handler([$logger, 'onSilencedError']);
dns_get_record();
restore_error_handler();

エラーを例外に変える

set_error_handler()ErrorExceptionクラスを使用して、すべてのphpエラーを例外に変換できます。

set_error_handler(function($errno, $errstr, $errfile, $errline, $errcontext) {
    // error was suppressed with the @-operator
    if (0 === error_reporting()) {
        return false;
    }

    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});

try {
    dns_get_record();
} catch (ErrorException $e) {
    // ...
}

独自のエラーハンドラーを使用するときに注意する重要な点は、error_reporting設定をバイパスし、すべてのエラー(通知、警告など)をエラーハンドラーに渡すことです。2番目の引数をon set_error_handler()に設定し... = error_reporting()て、受信するエラータイプを定義するか、エラーハンドラー内を使用して現在の設定にアクセスできます。

警告を抑制する

もう1つの可能性は、@演算子を使用して呼び出しを抑制し、dns_get_record()後で戻り値を確認することです。しかし、エラー/警告は抑制されるのではなく処理されるようにトリガーされるので、これはお勧めしません。


3
関数呼び出しの直前に独自のエラーハンドラーを設定し、エラーチェックが完了したらrestore_error_handlerを設定することをお勧めしますか?
user121196 2009

2
同時リクエストが多く、各リクエストが1.set_error_handler()を実行する場合、これはスレッドセーフになります。2.doit 3.restore_error_handler?
user121196 2009

4
ありがとう。これは役立ちます。(そして、彼らはPHPは災害ではないと言っています。)
アーロンミラー

2
エラーを抑制するために@を使用しないようにするための+1。E_WARNINGは実際には致命的でないエラーです。一般に、常に適切にエラーを処理するように努める必要があります。アプリケーションでset_error_handlerの使用が必要な場合は、そうしてください。通常、運用環境ではエラーをログに記録し、エラーの表示を無効にすることをお勧めします。ログを確認すると、開発環境で変更を加える場所を確認できます。@ fopen / @ unlinkを見て、開発者がエラーを回避したり、set_error_handlerを使用してエラーを処理したりするためのチェックを実行しなかったのはなぜか疑問に思った事例が多すぎます。
fyrye 2014年

5
警告を例外に変換することに関する注意:警告によってアプリが強制終了されることはありません。キャッチされなかった例外は実行されます!
アルバロ・ゴンサレス

149

実際に機能する解決策はE_WARNING、次のように、パラメーターを使用して単純なエラーハンドラーを設定することです。

set_error_handler("warning_handler", E_WARNING);
dns_get_record(...)
restore_error_handler();

function warning_handler($errno, $errstr) { 
// do something
}

4
callable関数宣言のある文字列の代わりに匿名も使用できます
vp_arth

ありがとう、しかしどうすればクリティカルブロックの後にエラーハンドラを削除できますか?
Yevgeniy Afanasyev 2015

3
優秀な!ただ、trow new \Exception($errstr, $errno);内部のwarning_handler機能。ありがとう。
Vladimir Vukanac

これがここでの最良の答えです!
lewis4u

28

@オペレーターには注意してください。警告は抑制されますが、致命的なエラーも抑制されます。誰かが書いたシステムで問題をデバッグするのに多くの時間を費やしましたが@mysql_query( '...' )、問題は、mysqlサポートがPHPにロードされておらず、サイレントフェイタルエラーをスローしたことでした。PHPコアの一部であるものに対しては安全ですが、注意して使用してください

bob@mypc:~$ php -a
Interactive shell

php > echo @something(); // this will just silently die...

これ以上の出力はありません-これをデバッグして頑張ってください!

bob@mypc:~$ php -a
Interactive shell

php > echo something(); // lets try it again but don't suppress the error
PHP Fatal error:  Call to undefined function something() in php shell code on line 1
PHP Stack trace:
PHP   1. {main}() php shell code:0
bob@mypc:~$ 

今回はそれが失敗した理由がわかります。


5

警告を試したりキャッチしたりしたかったのですが、同時に通常の警告/エラーのログを残しておきます(例:)/var/log/apache2/error.log。ハンドラが返す必要があるものfalse。ただし、「throw new ...」ステートメントは基本的に実行を中断するため、次に説明する「wrap in function」のトリックを実行する必要があります。

PHPで例外をスローする静的な方法はありますか

または、簡単に言うと:

  function throwErrorException($errstr = null,$code = null, $errno = null, $errfile = null, $errline = null) {
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
  }
  function warning_handler($errno, $errstr, $errfile, $errline, array $errcontext) {
    return false && throwErrorException($errstr, 0, $errno, $errfile, $errline);
    # error_log("AAA"); # will never run after throw
    /* Do execute PHP internal error handler */
    # return false; # will never run after throw
  }
  ...
  set_error_handler('warning_handler', E_WARNING);
  ...
  try {
    mkdir($path, 0777, true);
  } catch (Exception $e) {
    echo $e->getMessage();
    // ...
  }

編集:よく調べた結果、機能しないことがわかりました。「return false && throwErrorException ...」は基本的に例外をスローせ、エラーログに記録します。「false &&」のように「」の部分を削除return throwErrorException ...すると、例外のスローは機能しますが、error_logにはログインしません。


4

おそらく警告を完全に取り除こうとする必要がありますが、それが不可能な場合は、呼び出しを@で付加して(つまり、@ dns_get_record(...))、警告が発生したかどうかを把握できる情報を使用できます。か否か。


4

通常、これが唯一の解決策でない限り、@を使用しないでください。その特定のケースでは、レコードが存在するかどうかを知るために、最初に関数dns_check_recordを使用する必要があります。


3

これらのコード行をfile_get_contents()外部URLの呼び出しの前後に組み合わせると、「ストリームを開けませんでした:接続がタイムアウトしました」などの警告を処理するのに役立ちました:

set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line, array $err_context)
{
    throw new ErrorException( $err_msg, 0, $err_severity, $err_file, $err_line );
}, E_WARNING);
try {
    $iResult = file_get_contents($sUrl);
} catch (Exception $e) {
    $this->sErrorMsg = $e->getMessage();
}
restore_error_handler();

このソリューションはオブジェクトコンテキスト内でも機能します。あなたはそれを関数で使うことができます:

public function myContentGetter($sUrl)
{
  ... code above ...
  return $iResult;
}

2

dns_get_record()失敗した場合はを返すはずなFALSEので、で警告を抑制して@から戻り値を確認できます。


0

ブール値を返すかどうかを確認してみてください。そうすれば、単純にそれを条件として置くことができます。oci_execute(...)でこれに遭遇しました。これは、私の固有のキーでいくつかの違反を返していました。

ex.
oci_parse($res, "[oracle pl/sql]");
if(oci_execute){
...do something
}

0

FolderStructure

index.php //Script File
logs //Folder for log Every warning and Errors
CustomException.php //Custom exception File

CustomException.php

/**
* Custom error handler
*/
function handleError($code, $description, $file = null, $line = null, $context = null) {
    $displayErrors = ini_get("display_errors");;
    $displayErrors = strtolower($displayErrors);
    if (error_reporting() === 0 || $displayErrors === "on") {
        return false;
    }
    list($error, $log) = mapErrorCode($code);
    $data = array(
        'timestamp' => date("Y-m-d H:i:s:u", time()),
        'level' => $log,
        'code' => $code,
        'type' => $error,
        'description' => $description,
        'file' => $file,
        'line' => $line,
        'context' => $context,
        'path' => $file,
        'message' => $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']'
    );
    $data = array_map('htmlentities',$data);
    return fileLog(json_encode($data));
}

/**
* This method is used to write data in file
* @param mixed $logData
* @param string $fileName
* @return boolean
*/
function fileLog($logData, $fileName = ERROR_LOG_FILE) {
    $fh = fopen($fileName, 'a+');
    if (is_array($logData)) {
        $logData = print_r($logData, 1);
    }
    $status = fwrite($fh, $logData . "\n");
    fclose($fh);
//    $file = file_get_contents($filename);
//    $content = '[' . $file .']';
//    file_put_contents($content); 
    return ($status) ? true : false;
}

/**
* Map an error code into an Error word, and log location.
*
* @param int $code Error code to map
* @return array Array of error word, and log location.
*/
function mapErrorCode($code) {
    $error = $log = null;
    switch ($code) {
        case E_PARSE:
        case E_ERROR:
        case E_CORE_ERROR:
        case E_COMPILE_ERROR:
        case E_USER_ERROR:
            $error = 'Fatal Error';
            $log = LOG_ERR;
            break;
        case E_WARNING:
        case E_USER_WARNING:
        case E_COMPILE_WARNING:
        case E_RECOVERABLE_ERROR:
            $error = 'Warning';
            $log = LOG_WARNING;
            break;
        case E_NOTICE:
        case E_USER_NOTICE:
            $error = 'Notice';
            $log = LOG_NOTICE;
            break;
        case E_STRICT:
            $error = 'Strict';
            $log = LOG_NOTICE;
            break;
        case E_DEPRECATED:
        case E_USER_DEPRECATED:
            $error = 'Deprecated';
            $log = LOG_NOTICE;
            break;
        default :
            break;
    }
    return array($error, $log);
}
//calling custom error handler
set_error_handler("handleError");

このようなスクリプトファイルに上記のファイルを含めるだけです

index.php

error_reporting(E_ALL);
ini_set('display_errors', 'off');
define('ERROR_LOG_FILE', 'logs/app_errors.log');

include_once 'CustomException.php';
echo $a; // here undefined variable warning will be logged into logs/app_errors.log

-2

単純な操作である場合にのみ警告を抑制するために@を使用することをお勧めします(例:$ prop = @($ high /($ width-$ depth));ゼロ警告による除算をスキップします)。ただし、ほとんどの場合、処理することをお勧めします。


2
これは、@を絶対に使用したくないときです。これは、操作を制御でき、それを実行する前に、ゼロによる除算かどうかを確認できます。
エボルボブ2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.