2014-12-01 38 views
1

我在包含錯誤處理的實現中發現了很多嘗試,我想我可能會寫一個wiki樣式以希望提供一個完整的解決方案。如何在PHP中捕獲所有錯誤?

的問題是:

「怎麼可能一個陷阱,處理或截獲所有錯誤的PHP類型?」

現在 - 這可能被某些人認爲是'重寫' - 但我不知道有一個全面的解決方案。

回答

2

有許多PHP錯誤級別,其中一些需要設置單獨的錯誤處理程序,並且爲了捕獲PHP可能拋出的每一個錯誤 - 您必須編寫一些涵蓋所有這些「錯誤類型」的錯誤 - - 啓動,'運行時'和異常。

我的解決方案,以抓住每一個(只要我可以告訴),其歸結管道錯誤:

  • 幾個全局變量的
  • 一個初始化方法
  • 4「非面向對象」錯誤處理程序方法
  • 帶有'AppendError'方法的ErrorHandler類 - 可以修改錯誤輸出的方式(在這種情況下,錯誤只會從此方法轉儲到屏幕上的最小HTML中)

...

// Moved this line to the bottom of the 'file' for usability - 
// I keep each of the above mentioned 'pieces' in separate files. 
//$ErrorHandler = new ErrorHandler(); 

$ErrorCallback = "HandleRuntimeError"; 
$ExceptionCallback = "HandleException"; 
$FatalCallback = "HandleFatalError"; 

$EnableReporting = true; 
$ErrorLevel = E_ALL; 

function InitializeErrors() 
{ 
    if($GLOBALS["EnableReporting"]) 
    { 
     error_reporting($GLOBALS["ErrorLevel"]); 

     if(isset($GLOBALS["ErrorCallback"]) && strlen($GLOBALS["ErrorCallback"]) > 0) 
     { 
      set_error_handler($GLOBALS["ErrorCallback"]); 

      // Prevent the PHP engine from displaying runtime errors on its own 
      ini_set('display_errors',false); 
     } 
     else 
      ini_set('display_errors',true); 

     if(isset($GLOBALS["FatalCallback"]) && strlen($GLOBALS["FatalCallback"]) > 0) 
     { 
      register_shutdown_function($GLOBALS["FatalCallback"]); 

      // Prevent the PHP engine from displaying fatal errors on its own 
      ini_set('display_startup_errors',false); 
     } 
     else 
      ini_set('display_startup_errors',true); 

     if(isset($GLOBALS['ExceptionCallback']) && strlen($GLOBALS['ExceptionCallback']) > 0) 
      set_exception_handler($GLOBALS["ExceptionCallback"]); 
    } 
    else 
    { 
     ini_set('display_errors',0); 
     ini_set('display_startup_errors',0); 
     error_reporting(0); 
    } 
} 

function HandleRuntimeError($ErrorLevel,$ErrorMessage,$ErrorFile=null,$ErrorLine=null,$ErrorContext=null) 
{ 
    if(isset($GLOBALS['ErrorHandler'])) 
    { 
     // Pass errors up to the global ErrorHandler to be later inserted into 
     // final output at the appropriate time. 
     $GLOBALS['ErrorHandler']->AppendError($ErrorLevel,"Runtime Error: " . $ErrorMessage,$ErrorFile,$ErrorLine,$ErrorContext); 

     return true; 
    } 
    else 
    { 
     PrintError($ErrorLevel,$ErrorMessage,$ErrorFile,$ErrorLine,$ErrorContext); 
     return true; 
    } 
} 

function HandleException($Exception) 
{ 
    if(isset($GLOBALS['ErrorCallback'])) 
    { 
     // Parse and pass exceptions up to the standard error callback. 
     $GLOBALS['ErrorCallback']($Exception->getCode(), "Exception: " . $Exception->getMessage(), $Exception->getFile(), $Exception->getLine(), $Exception->getTrace()); 

     return true; 
    } 
    else 
    {  
     PrintError($Exception->getCode(), "Exception: " . $Exception->getMessage(), $Exception->getFile(), $Exception->getLine(), $Exception->getTrace()); 
     return true; 
    } 
} 

function HandleFatalError() 
{ 
    $Error = error_get_last(); 

    // Unset Error Type and Message implies a proper shutdown. 
    if(!isset($Error['type']) && !isset($Error['message'])) 
     exit(); 
    else if(isset($GLOBALS['ErrorCallback'])) 
    { 
     // Pass fatal errors up to the standard error callback. 
     $GLOBALS["ErrorCallback"]($Error['type'], "Fatal Error: " . $Error['message'],$Error['file'],$Error['line']); 

     return null; 
    } 
    else 
    { 
     PrintError($Error['type'], "Fatal Error: " . $Error['message'],$Error['file'],$Error['line']); 
     return null; 
    } 
} 

// In the event that our 'ErrorHandler' class is in fact the generator of the error, 
// we need a plain-Jane method that will still deliver the message. 
function PrintError($ErrorLevel,$ErrorMessage,$ErrorFile=null,$ErrorLine=null,$ErrorContext=null) 
{ 
    if(class_exists("ErrorHandler")) 
     $ErrorTypeString = ErrorHandler::ErrorTypeString($ErrorLevel); 
    else 
     $ErrorTypeString = "$ErrorLevel"; 

    if(isset($ErrorContext) && !is_array($ErrorContext) && strlen($ErrorContext) > 0) 
     $ErrorContext = str_replace("#", "<br/>\r\n#", $ErrorContext); 

    $ReturnValue = ""; 
    $ReturnValue .= "<div class=\"$ErrorTypeString\" style=\"margin: 10px;\">\r\n"; 

    $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Error Level:</span> <span class=\"ErrorValue\">$ErrorTypeString</span></p>\r\n"; 

    if(isset($ErrorFile) && strlen($ErrorFile) > 0) 
     $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">File:</span> <span class=\"ErrorValue\">'$ErrorFile'</span></p>\r\n"; 

    if(isset($ErrorLine) && strlen($ErrorLine) > 0) 
     $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Line:</span> <span class=\"ErrorValue\">$ErrorLine</span></p>\r\n"; 

    if(isset($ErrorContext) && is_array($ErrorContext)) 
     $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Context:</span><span class=\"ErrorValue\">" . var_export($ErrorContext,true) . "</span></p>\r\n"; 
    else if(isset($ErrorContext) && strlen($ErrorContext) > 0) 
     $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Context:</span><span class=\"ErrorValue\">$ErrorContext</span></p>\r\n"; 

    $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Message:</span> <span class=\"ErrorValue\">" . str_replace("\r\n","<br/>\r\n",$ErrorMessage) . "</span></p>\r\n"; 

    $ReturnValue .= "</div>\r\n"; 

    echo($ReturnValue); 
} 

class ErrorHandler 
{ 
    public function AppendError($ErrorLevel,$ErrorMessage,$ErrorFile=null,$ErrorLine=null,$ErrorContext=null) 
    { 
     // Perhaps evaluate the error level and respond accordingly 
     // 
     // In the event that this actually gets used, something that might 
     // determine if you're in a production environment or not, or that 
     // determines if you're an admin or not - or something - could belong here. 
     // Redirects or response messages accordingly. 
     $ErrorTypeString = ErrorHandler::ErrorTypeString($ErrorLevel); 

     if(isset($ErrorContext) && !is_array($ErrorContext) && strlen($ErrorContext) > 0) 
      $ErrorContext = str_replace("#", "<br/>\r\n#", $ErrorContext); 

     $ReturnValue = ""; 
     $ReturnValue .= "<div class=\"$ErrorTypeString\" style=\"margin: 10px;\">\r\n"; 

     $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Error Level:</span> <span class=\"ErrorValue\">$ErrorTypeString</span></p>\r\n"; 

     if(isset($ErrorFile) && strlen($ErrorFile) > 0) 
      $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">File:</span> <span class=\"ErrorValue\">'$ErrorFile'</span></p>\r\n"; 

     if(isset($ErrorLine) && strlen($ErrorLine) > 0) 
      $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Line:</span> <span class=\"ErrorValue\">$ErrorLine</span></p>\r\n"; 

     if(isset($ErrorContext) && is_array($ErrorContext)) 
      $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Context:</span><span class=\"ErrorValue\">" . var_export($ErrorContext,true) . "</span></p>\r\n"; 
     else if(isset($ErrorContext) && strlen($ErrorContext) > 0) 
      $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Context:</span><span class=\"ErrorValue\">$ErrorContext</span></p>\r\n"; 

     $ReturnValue .= "<p class=\"ErrorData\"><span class=\"ErrorKey\">Message:</span> <span class=\"ErrorValue\">" . str_replace("\r\n","<br/>\r\n",$ErrorMessage) . "</span></p>\r\n"; 

     $ReturnValue .= "</div>\r\n"; 

     echo($ReturnValue); 
    } 

    public static function ErrorTypeString($ErrorType) 
    { 
     $ReturnValue = ""; 

     switch($ErrorType) 
     { 
      default: 
       $ReturnValue = "E_UNSPECIFIED_ERROR"; 
       break; 
      case E_ERROR: // 1 // 
       $ReturnValue = 'E_ERROR'; 
       break; 
      case E_WARNING: // 2 // 
       $ReturnValue = 'E_WARNING'; 
       break; 
      case E_PARSE: // 4 // 
       $ReturnValue = 'E_PARSE'; 
       break; 
      case E_NOTICE: // 8 // 
       $ReturnValue = 'E_NOTICE'; 
       break; 
      case E_CORE_ERROR: // 16 // 
       $ReturnValue = 'E_CORE_ERROR'; 
       break; 
      case E_CORE_WARNING: // 32 // 
       $ReturnValue = 'E_CORE_WARNING'; 
       break; 
      case E_COMPILE_ERROR: // 64 // 
       $ReturnValue = 'E_COMPILE_ERROR'; 
       break; 
      case E_CORE_WARNING: // 128 // 
       $ReturnValue = 'E_COMPILE_WARNING'; 
       break; 
      case E_USER_ERROR: // 256 // 
       $ReturnValue = 'E_USER_ERROR'; 
       break; 
      case E_USER_WARNING: // 512 // 
       $ReturnValue = 'E_USER_WARNING'; 
       break; 
      case E_USER_NOTICE: // 1024 // 
       $ReturnValue = 'E_USER_NOTICE'; 
       break; 
      case E_STRICT: // 2048 // 
       $ReturnValue = 'E_STRICT'; 
       break; 
      case E_RECOVERABLE_ERROR: // 4096 // 
       $ReturnValue = 'E_RECOVERABLE_ERROR'; 
       break; 
      case E_DEPRECATED: // 8192 // 
       $ReturnValue = 'E_DEPRECATED'; 
       break; 
      case E_USER_DEPRECATED: // 16384 // 
       $ReturnValue = 'E_USER_DEPRECATED'; 
       break; 
     } 

     return $ReturnValue; 
    } 
} 

$ErrorHandler = new ErrorHandler(); 

現在 - 代碼閃開......

要實現這一點,這是因爲含有該文件,並執行 'InitializeErrors' 的方法一樣簡單。除此之外,這取決於你想要處理的錯誤;這僅僅是PHP可能生成的每個錯誤的一個包裝器,並且可以修改任何給定錯誤的處理方式,這基本上就像在AppendError方法中評估它一樣簡單,並相應地做出響應。我應該注意 - 由於我無法解釋的原因,我實現了返回值,我應該回顧一下自己在這方面的工作 - 但我並不十分確定它對結果有任何影響。

2

有三種錯誤處理程序,你需要:

  • set_exception_handler,捕獲任何其他方式捕獲的異常。

  • set_error_handler捕捉「標準」PHP錯誤。我想首先檢查它是否與error_reporting相對應,看它是否應該被處理或忽略(我通常忽略Notices - 可能是壞的,但這是我的選擇),並且拋出ErrorException,讓異常處理程序捕獲它以輸出。

  • register_shutdown_functionerror_get_last相結合。檢查['type']的值以查看它是否爲E_ERROR,E_PARSE或基本上任何您想要捕獲的致命錯誤類型。這些通常繞過set_error_handler,所以抓住它們讓你抓住它們。同樣,我傾向於只拋出ErrorException,以便所有錯誤最終由相同的異常處理程序處理。

至於你如何實現這些,完全取決於你的代碼是如何設置的。我通常做一個ob_end_clean()清除任何輸出,並提出一個很好的錯誤頁面,告訴他們錯誤已被報告。

+0

我想 - 對'問題'的有效答案 - 但記錄這裏的想法是提供一個實際的實施例。不管幹杯;所有的好消息。 – DigitalJedi805 2014-12-01 22:45:59