PHP中的错误处理
我熟悉一些基本知识,但是我想了解的更多信息是何时以及为什么在PHP中使用错误处理(包括引发异常),尤其是在现场站点或者Web应用程序中。它是可以被过度使用的东西吗?如果可以的话,过度使用是什么样子?在某些情况下不应该使用它吗?此外,关于错误处理的一些常见安全问题是什么?
解决方案
未经处理的错误会停止脚本,仅凭这是处理它们的一个很好的理由。
通常,我们可以使用Try-Catch块来处理错误
try { // Code that may error } catch (Exception $e) { // Do other stuff if there's an error }
如果要停止页面上显示的错误或者警告消息,则可以在呼叫之前添加@符号作为前缀。
@mysql_query($query);
但是,对于查询,通常最好执行这样的操作,以便我们对发生的事情有更好的了解。
@mysql_query($query) or die('Invalid query: ' . mysql_error() . '<br />Line: ' . __LINE__ . '<br />File: ' . __FILE__ . '<br /><br />');
我们可以将其存储在日志中,而不是输出mysql_error。这样,我们就可以跟踪错误(并且不依赖用户报告错误),并且可以排除故障。
最好的错误处理是对用户透明的类型,让代码找出问题,而无需让该用户同伴。
如果我们无法明确控制脚本正在处理的数据,则应使用错误处理。我倾向于在例如表单验证之类的地方频繁使用它。知道如何发现代码中容易出错的地方需要一些实践:一些常见的问题是在返回值的函数调用之后,或者在处理数据库查询的结果时。我们永远不要以为函数的返回值将是期望,并且我们应该确保在预期的情况下进行编码。尽管它们很有用,但我们不必使用try / catch块。很多时候,我们可以通过简单的if / else检查来解决问题。
错误处理与安全的编码实践紧密结合,因为有许多"错误"不会导致脚本简单崩溃。尽管不仅仅严格地讨论错误处理本身,但是addbytes在关于安全PHP编程的一些基础知识上有一篇不错的文章(共4篇),我们可以在这里找到。在stackoverflow上还有很多其他问题,例如mysql_real_escape_string和正则表达式,它们在确认用户输入数据的内容方面非常有力。
除了立即处理代码中的错误外,我们还可以利用
http://us.php.net/manual/zh/function.set-exception-handler.php
和
http://us.php.net/manual/zh/function.set-error-handler.php
我发现设置自己的异常处理程序特别有用。发生异常时,我们可以根据异常的类型执行不同的操作。
例如:当mysql_connet调用返回FALSE时,我抛出新的DBConnectionException(mysql_error())并以"特殊"方式处理它:记录错误,数据库连接信息(主机,用户名,密码)等,并甚至可以通过电子邮件发送给开发团队,通知他们数据库可能确实存在问题
我用它来补充标准错误处理。我不建议过度使用这种方法
要增加已经说过的一件事是,将Web应用程序中的任何错误记录到日志中是至关重要的。这样,就像Jeff" Coding Horror" Atwood所建议的那样,我们将知道用户何时遇到应用程序问题(而不是"向他们提出问题")。
为此,我建议使用以下类型的基础结构:
- 在数据库中创建一个"崩溃"表,并创建一组用于报告错误的包装器类。我建议为崩溃设置类别("阻止","安全"," PHP错误/警告"(与例外)等)。
- 在所有错误处理代码中,请确保记录错误。持续执行此操作取决于我们构建API的能力(上述步骤)-如果操作正确,记录崩溃应该很简单。
额外的功劳:有时,崩溃将是数据库级的崩溃:例如,数据库服务器关闭等。如果是这种情况,错误日志记录基础结构(上述)将失败(我们无法将崩溃记录到数据库,因为日志会尝试写入数据库)。在那种情况下,我会在崩溃包装器类中将故障转移逻辑写入
- 发送电子邮件给管理员,和/或者
- 将崩溃的详细信息记录到纯文本文件中
所有这些听起来都有些过分,但是请相信我,这对应用程序被接受为"稳定"还是"不稳定"有所不同。造成这种差异的原因是,所有应用程序始终都以片状/崩溃开头,但那些知道其应用程序所有问题的开发人员就有机会进行实际修复。
恕我直言的最佳实践是使用以下方法:
1.创建一个错误/异常处理程序
2.在应用启动时启动
3.从那里处理所有错误
<?php
类Debug {
public static setAsErrorHandler() { set_error_handler(array(__CLASS__, '__error_handler')); } public static function __error_handler($errcode, $errmsg, $errfile, $errline) { if (IN DEV) { print on screen } else if (IN PRO) { log and mail } }
}
Debug :: setAsErrorHandler();
?>
粗略地说,错误是PHP的传统,而异常是处理错误的现代方法。然后,最简单的事情是设置一个引发异常的错误处理程序。这样,所有错误都将转换为异常,然后我们可以简单地处理一种错误处理方案。以下代码将为我们将错误转换为异常:
function exceptions_error_handler($severity, $message, $filename, $lineno) { if (error_reporting() == 0) { return; } if (error_reporting() & $severity) { throw new ErrorException($message, 0, $severity, $filename, $lineno); } } set_error_handler('exceptions_error_handler'); error_reporting(E_ALL ^ E_STRICT);
但是,在某些情况下,代码是专门为处理错误而设计的。例如,DomDocument的schemaValidate方法会在验证文档时发出警告。如果将错误转换为异常,它将在第一次失败后停止验证。有时这是我们想要的,但是在验证文档时,我们实际上可能希望所有失败。在这种情况下,我们可以临时安装错误处理程序,以收集错误。这是我使用的一小段代码:
class errorhandler_LoggingCaller { protected $errors = array(); function call($callback, $arguments = array()) { set_error_handler(array($this, "onError")); $orig_error_reporting = error_reporting(E_ALL); try { $result = call_user_func_array($callback, $arguments); } catch (Exception $ex) { restore_error_handler(); error_reporting($orig_error_reporting); throw $ex; } restore_error_handler(); error_reporting($orig_error_reporting); return $result; } function onError($severity, $message, $file = null, $line = null) { $this->errors[] = $message; } function getErrors() { return $this->errors; } function hasErrors() { return count($this->errors) > 0; } }
和一个用例:
$doc = new DomDocument(); $doc->load($xml_filename); $validation = new errorhandler_LoggingCaller(); $validation->call( array($doc, 'schemaValidate'), array($xsd_filename)); if ($validation->hasErrors()) { var_dump($validation->getErrors()); }
@的错误抑制非常慢。
我们还可以使用Google表单来捕获和分析异常,而无需维护数据库或者可公开访问的服务器。这里有一个教程来解释该过程。