ฉันสามารถลอง / จับคำเตือนได้หรือไม่?


358

ฉันต้องการที่จะจับคำเตือนบางอย่างที่ถูกโยนออกมาจากฟังก์ชั่นพื้นเมือง php แล้วจัดการพวกเขา

โดยเฉพาะ:

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

มันจะส่งคำเตือนเมื่อแบบสอบถาม DNS ล้มเหลว

try/ catchไม่ทำงานเนื่องจากคำเตือนไม่ใช่ข้อยกเว้น

ตอนนี้ฉันมี 2 ตัวเลือก:

  1. set_error_handler ดูเหมือนว่า overkill เพราะฉันต้องใช้มันเพื่อกรองคำเตือนทุกอย่างในหน้า (นี่เป็นเรื่องจริงหรือ?);

  2. ปรับการรายงานข้อผิดพลาด / การแสดงผลเพื่อให้คำเตือนเหล่านี้ไม่ได้รับการสะท้อนไปยังหน้าจอจากนั้นตรวจสอบค่าตอบแทน; ถ้าเป็นเช่นนั้นจะfalseไม่พบระเบียนสำหรับชื่อโฮสต์

การปฏิบัติที่ดีที่สุดที่นี่คืออะไร


1
stackoverflow.com/questions/136899/…เป็นการสนทนาที่ดีเกี่ยวกับสิ่งต่างๆเช่นนี้
Mez

มีคำตอบด้านล่างที่ถูกลบไหม ไม่ว่าจะโดยเจ้าของหรือโดยใครบางคน?
user121196

ดูเพิ่มเติมที่: stackoverflow.com/questions/1087365
dreftymac

@ user121196: ใช่ โดยเจ้าของ
การแข่งขัน Lightness ใน Orbit

คำตอบ:


373

ตั้งค่าและเรียกคืนตัวจัดการข้อผิดพลาด

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()และErrorExceptionclass เพื่อเปลี่ยนข้อผิดพลาด 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ตั้งค่าและส่งข้อผิดพลาดทั้งหมด (การแจ้งเตือนคำเตือน ฯลฯ ) ไปยังตัวจัดการข้อผิดพลาดของคุณ คุณสามารถตั้งค่าอาร์กิวเมนต์ที่สองset_error_handler()เพื่อกำหนดประเภทข้อผิดพลาดที่คุณต้องการรับหรือเข้าถึงการตั้งค่าปัจจุบันโดยใช้... = error_reporting()ภายในตัวจัดการข้อผิดพลาด

ระงับการเตือน

ความเป็นไปได้อีกอย่างคือการระงับการโทรด้วยตัวดำเนินการ @ และตรวจสอบค่าส่งคืนdns_get_record()หลังจากนั้น แต่ฉันขอแนะนำเรื่องนี้เนื่องจากข้อผิดพลาด / คำเตือนถูกกระตุ้นให้จัดการไม่ถูกระงับ


3
จะแนะนำให้ตั้งค่าตัวจัดการข้อผิดพลาดของฉันเองก่อนการเรียกใช้ฟังก์ชันแล้ว restore_error_handler เมื่อตรวจสอบข้อผิดพลาดเสร็จแล้วหรือไม่
user121196

2
สิ่งนี้จะเป็นเธรดที่ปลอดภัยหรือไม่หากมีคำขอที่เกิดขึ้นพร้อมกันจำนวนมากและแต่ละคำขอจะทำ 1.set_error_handler () 2. ทำ 3.restore_error_handler หรือไม่
user121196

4
ขอบคุณ; สิ่งนี้ช่วย (และพวกเขาบอกว่า PHP ไม่ใช่ภัยพิบัติ)
Aaron Miller

2
+1 เพื่อหลีกเลี่ยงการใช้ @ เพื่อระงับข้อผิดพลาด E_WARNING เป็นข้อผิดพลาดที่ไม่ร้ายแรง โดยทั่วไปคุณควรพยายามจัดการข้อผิดพลาดอย่างเหมาะสม หากแอปพลิเคชันของคุณต้องการการใช้งาน set_error_handler ให้ทำเช่นนั้น โดยทั่วไปจะแนะนำให้บันทึกข้อผิดพลาดและปิดใช้งานการแสดงผลในสภาพแวดล้อมการใช้งานจริง เมื่อตรวจสอบบันทึกคุณสามารถดูว่าจะทำการเปลี่ยนแปลงในสภาพแวดล้อมการพัฒนาของคุณได้ที่ไหน มีหลายครั้งที่ฉันเห็น @ fopen / @ unlink และสงสัยว่าทำไมนักพัฒนาไม่ทำการตรวจสอบเพื่อหลีกเลี่ยงข้อผิดพลาดหรือจัดการข้อผิดพลาดโดยใช้ set_error_handler
fyrye

5
หมายเหตุเกี่ยวกับการเปลี่ยนคำเตือนเข้าข้อยกเว้น: เตือนจะไม่ยกเลิกแอปสำหรับผู้ใช้ของคุณไม่ถูกตรวจจับข้อยกเว้นจะทำ!
ÁlvaroGonzález

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

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ซึ่งการจัดการที่มีผลตอบแทน อย่างไรก็ตามเนื่องจากคำสั่ง "โยนใหม่ ... " โดยทั่วไปขัดจังหวะการดำเนินการอย่างใดอย่างหนึ่งจึงต้องทำเคล็ดลับ "ห่อในฟังก์ชั่น" ฟังก์ชั่นยังกล่าวถึงใน:

มีวิธีคงที่จะโยนข้อยกเว้นใน 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

Normaly คุณไม่ควรใช้ @ ยกเว้นว่านี่เป็นทางออกเดียว ในกรณีเฉพาะนั้นควรใช้ฟังก์ชัน 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;
}


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 = @ ($ สูง / ($ กว้าง - $ ลึก)); เพื่อข้ามการหารด้วยคำเตือนเป็นศูนย์) อย่างไรก็ตามในกรณีส่วนใหญ่จะดีกว่าที่จะจัดการ


2
นี่เป็นครั้งเดียวที่คุณไม่ต้องการใช้ @ - คุณสามารถควบคุมการปฏิบัติงานและสามารถตรวจสอบได้ว่าการหารด้วยศูนย์หรือไม่ก่อนที่จะทำ
Eborbob
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.