Bisakah saya mencoba / menangkap peringatan?


358

Saya perlu menangkap beberapa peringatan yang dilemparkan dari beberapa fungsi asli php dan kemudian menanganinya.

Secara khusus:

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

Itu melempar peringatan ketika permintaan DNS gagal.

trySaya catchtidak bekerja karena peringatan bukan pengecualian.

Saya sekarang memiliki 2 opsi:

  1. set_error_handler sepertinya berlebihan karena saya harus menggunakannya untuk menyaring setiap peringatan di halaman (apakah ini benar?);

  2. Sesuaikan pelaporan kesalahan / tampilan sehingga peringatan ini tidak disuarakan ke layar, lalu periksa nilai pengembalian; jika itu false, tidak ada catatan yang ditemukan untuk nama host.

Apa praktik terbaik di sini?


1
stackoverflow.com/questions/136899/… adalah diskusi yang bagus mengenai hal-hal seperti ini.
Mez

ada jawaban di bawah ini yang dihapus? baik oleh pemilik atau oleh seseorang?
user121196


@ user121196: Ya. Oleh pemilik.
Lightness Races dalam Orbit

Jawaban:


373

Setel dan pulihkan penangan kesalahan

Satu kemungkinan adalah untuk mengatur penangan kesalahan Anda sendiri sebelum panggilan dan mengembalikan penangan kesalahan sebelumnya dengan restore_error_handler().

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

Anda dapat membangun ide ini dan menulis penangan kesalahan yang dapat digunakan kembali yang mencatat kesalahan Anda.

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

Mengubah kesalahan menjadi pengecualian

Anda bisa menggunakan set_error_handler()dan ErrorExceptionkelas untuk mengubah semua kesalahan php menjadi pengecualian.

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) {
    // ...
}

Hal penting yang perlu diperhatikan ketika menggunakan penangan kesalahan Anda sendiri adalah bahwa ia akan melewati error_reportingpengaturan dan meneruskan semua kesalahan (pemberitahuan, peringatan, dll.) Ke penangan kesalahan Anda. Anda dapat mengaktifkan argumen kedua set_error_handler()untuk menentukan jenis kesalahan yang ingin Anda terima, atau mengakses pengaturan saat ini menggunakan ... = error_reporting()di dalam penangan kesalahan.

Menekan peringatan itu

Kemungkinan lain adalah untuk menekan panggilan dengan operator @ dan memeriksa nilai balik dns_get_record()sesudahnya. Tapi saya menyarankan hal ini karena kesalahan / peringatan dipicu untuk ditangani, bukan untuk ditekan.


3
apakah disarankan untuk mengatur penangan kesalahan saya sendiri tepat sebelum panggilan fungsi, kemudian restore_error_handler ketika pengecekan kesalahan dilakukan?
user121196

2
apakah ini thread aman jika ada banyak permintaan bersamaan, dan setiap permintaan 1.set_error_handler (). 2. lakukan 3.restore_error_handler?
user121196

4
Terima kasih; ini membantu. (Dan mereka mengatakan PHP bukan bencana.)
Aaron Miller

2
+1 untuk menghindari penggunaan @ untuk menekan kesalahan. E_WARNING sebenarnya adalah kesalahan non-fatal. Secara umum Anda harus selalu berusaha menangani kesalahan dengan tepat. Jika aplikasi Anda memerlukan penggunaan set_error_handler maka lakukanlah. Biasanya disarankan untuk mencatat kesalahan dan menonaktifkan tampilan mereka dalam lingkungan produksi. Setelah memeriksa log, Anda dapat melihat di mana membuat perubahan di lingkungan pengembangan Anda. Terlalu banyak contoh di mana saya melihat @ fopen / @ unlink dan bertanya-tanya mengapa pengembang tidak melakukan pemeriksaan untuk menghindari kesalahan atau menangani kesalahan menggunakan set_error_handler.
fyrye

5
Catatan tentang mengubah peringatan menjadi pengecualian: peringatan tidak akan membatalkan aplikasi Anda — pengecualian yang tidak dilakukan akan dilakukan!
Álvaro González

149

Solusi yang benar-benar berfungsi ternyata adalah pengaturan penangan kesalahan sederhana dengan E_WARNINGparameter, seperti:

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

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

4
juga anonim callabledapat digunakan di sini alih-alih string dengan deklarasi fungsi
vp_arth

Terima kasih, tetapi bagaimana saya bisa menghapus penangan kesalahan setelah blok kritis?
Yevgeniy Afanasyev

3
Luar biasa! Hanya trow new \Exception($errstr, $errno);di dalam warning_handlerfungsi. Terima kasih.
Vladimir Vukanac

Ini jawaban terbaik di sini!
lewis4u

28

Berhati-hatilah dengan @operator - saat menekan peringatan, ia juga menekan kesalahan fatal. Saya menghabiskan banyak waktu men-debug masalah dalam sistem di mana seseorang telah menulis @mysql_query( '...' )dan masalahnya adalah bahwa dukungan mysql tidak dimuat ke dalam PHP dan itu melemparkan kesalahan fatal yang sunyi. Ini akan aman untuk hal-hal yang merupakan bagian dari inti PHP tapi tolong gunakan dengan hati-hati.

bob@mypc:~$ php -a
Interactive shell

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

Tidak ada output lebih lanjut - semoga berhasil men-debug ini!

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:~$ 

Kali ini kita bisa melihat mengapa itu gagal.


5

Saya ingin mencoba / menangkap peringatan, tetapi pada saat yang sama tetap menggunakan peringatan / kesalahan yang biasa dicatat (misalnya di /var/log/apache2/error.log); yang harus dikembalikan oleh pawang false. Namun, karena pernyataan "throw new ..." pada dasarnya mengganggu eksekusi, kita harus melakukan trik "wrap in function", juga dibahas dalam:

Apakah ada cara statis untuk melempar pengecualian di php

Atau, secara singkat:

  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();
    // ...
  }

Suntingan: setelah diperiksa lebih dekat, ternyata itu tidak bekerja: " return false && throwErrorException ..." pada dasarnya tidak akan membuang pengecualian, dan hanya login di log kesalahan; menghapus bagian " false &&", seperti pada " return throwErrorException ...", akan membuat perkecualian melempar bekerja, tetapi kemudian tidak akan masuk ke error_log ... Namun, saya tetap membiarkan ini diposting, karena saya belum melihat perilaku ini didokumentasikan di tempat lain.


4

Anda mungkin harus mencoba untuk menghilangkan peringatan sepenuhnya, tetapi jika itu tidak mungkin, Anda dapat menambahkan panggilan dengan @ (yaitu @dns_get_record (...)) dan kemudian menggunakan informasi apa pun yang dapat Anda cari tahu jika peringatan itu terjadi atau tidak.


4

Anda seharusnya tidak pernah menggunakan @ kecuali jika ini adalah satu-satunya solusi. Dalam kasus khusus itu fungsi dns_check_record harus digunakan terlebih dahulu untuk mengetahui apakah catatan itu ada.


3

Menggabungkan baris kode ini di sekitar file_get_contents()panggilan ke url eksternal membantu saya menangani peringatan seperti " gagal membuka streaming: Koneksi habis waktu " jauh lebih baik:

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();

Solusi ini juga berfungsi dalam konteks objek. Anda bisa menggunakannya dalam suatu fungsi:

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

2

Jika dns_get_record()gagal, itu harus kembali FALSE, sehingga Anda dapat menekan peringatan dengan @dan kemudian memeriksa nilai kembali.


0

coba periksa apakah ia mengembalikan beberapa nilai boolean maka Anda cukup menempatkannya sebagai suatu syarat. Saya mengalami ini dengan oci_execute (...) yang mengembalikan beberapa pelanggaran dengan kunci unik saya.

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

0

Struktur Folder

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");

cukup sertakan file di atas ke dalam file skrip Anda seperti ini

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

Saya hanya akan merekomendasikan menggunakan @ untuk menekan peringatan ketika ini adalah operasi langsung (misalnya $ prop = @ ($ tinggi / ($ lebar - $ kedalaman)); untuk melewati pembagian dengan nol peringatan). Namun dalam kebanyakan kasus itu lebih baik ditangani.


2
Ini adalah satu kali Anda pasti tidak ingin menggunakan @ - Anda memiliki kendali atas operasi dan dapat memeriksa apakah itu pembagian dengan nol atau tidak sebelum melakukannya.
Eborbob
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.