我熟悉一些基础知识,但我想更多地了解的是何时以及为什么应该在 PHP 中使用错误处理(包括抛出异常),尤其是在实时站点或 Web 应用程序中。它是否可以被过度使用,如果是,过度使用是什么样的?有不应该使用的情况吗?此外,关于错误处理,有哪些常见的安全问题?
10 回答
添加到已经说过的内容的一件事是,将 Web 应用程序中的任何错误记录到日志中是至关重要的。这样,正如 Jeff “Coding Horror” Atwood 所建议的那样,您将知道您的用户何时遇到了您的应用程序的问题(而不是“问他们出了什么问题”)。
为此,我推荐以下类型的基础架构:
- 在您的数据库中创建一个“崩溃”表和一组用于报告错误的包装类。我建议为崩溃设置类别(“阻塞”、“安全”、“PHP 错误/警告”(与异常)等)。
- 在您的所有错误处理代码中,确保记录错误。始终如一地执行此操作取决于您构建 API 的程度(以上步骤) -如果操作正确,记录崩溃应该是微不足道的。
额外的功劳:有时,您的崩溃将是数据库级别的崩溃:即数据库服务器关闭等。如果是这种情况,您的错误记录基础设施(上面)将失败(您无法将崩溃记录到数据库,因为日志尝试写入数据库)。在这种情况下,我会在您的 Crash 包装器类中编写故障转移逻辑
- 向管理员发送电子邮件,和/或
- 将崩溃的详细信息记录到纯文本文件中
所有这些听起来都像是矫枉过正,但相信我,这会影响您的应用程序是否被接受为“稳定”或“不稳定”。这种差异来自这样一个事实,即所有应用程序一开始都是不稳定/崩溃的,但那些了解其应用程序所有问题的开发人员有机会实际修复它。
粗略地说,错误是 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);
不过,在少数情况下,代码是专门为处理错误而设计的。例如,在验证文档时引发警告的schemaValidate
方法。DomDocument
如果将错误转换为异常,它将在第一次失败后停止验证。有时这是您想要的,但在验证文档时,您实际上可能想要所有失败。在这种情况下,您可以临时安装一个错误处理程序来收集错误。这是一个小片段,我已用于此目的:
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());
}
恕我直言,最佳做法是使用以下方法: 1. 创建错误/异常处理程序 2. 在应用程序启动时启动它 3. 从内部处理所有错误
<?php
class 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();
?>
未经处理的错误会停止脚本,仅此一项就是处理它们的一个很好的理由。
通常您可以使用 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 />');
在您无法明确控制脚本正在处理的数据的情况下,您应该使用错误处理。我倾向于经常使用它,例如在表单验证等地方。知道如何发现代码中容易出错的地方需要一些实践:一些常见的地方是在返回值的函数调用之后,或者在处理来自数据库查询的结果时。你永远不应该假设函数的返回会是你所期望的,你应该确保在预期中编写代码。您不必使用 try/catch 块,尽管它们很有用。很多时候,您可以通过简单的 if/else 检查来解决问题。
错误处理与安全编码实践密切相关,因为有很多“错误”不会导致您的脚本简单地崩溃。虽然不是严格的错误处理本身,但 addedbytes 有一篇很好的 4 篇文章系列,介绍了一些安全 PHP 编程的基础知识,您可以在这里找到。在 stackoverflow 上还有很多其他问题,例如mysql_real_escape_string和正则表达式,它们在确认用户输入数据的内容方面非常强大。
您可以将其存储在日志中,而不是输出 mysql_error。这样您就可以跟踪错误(并且您不依赖用户报告它)并且您可以进入并删除问题。
最好的错误处理是对用户透明的那种,让你的代码解决问题,不需要那个用户伙伴。
除了立即在代码中处理错误之外,您还可以使用
http://us.php.net/manual/en/function.set-exception-handler.php
和
http://us.php.net/manual/en/function.set-error-handler.php
我发现设置自己的异常处理程序特别有用。当发生异常时,您可以根据异常类型执行不同的操作。
例如:当mysql_connet
呼叫返回时,FALSE
我会抛出一个new DBConnectionException(mysql_error())
并以“特殊”方式处理它:记录错误、数据库连接信息(主机、用户名、密码)等,甚至可能通过电子邮件通知开发团队可能真的有问题数据库
我用它来补充标准错误处理。我不建议过度使用这种方法
@ 错误抑制非常慢。
您还可以使用 Google 表单来捕获和分析异常,而无需维护数据库或可公开访问的服务器。这里有一个教程解释了这个过程。
public $error=array();
public function Errors($Err)
{
------ how to use -------
$Err = array("func" => "constr", "ref" => "constrac","context" =>
"2222222ت","state" => 3,);
$ResultErr=$this->Errors($Err);
$context=(array_filter(explode(',', $ResultErr['context'])));
$func=(array_filter(explode(',', $ResultErr['func'])));
$ref=(array_filter(explode(',', $ResultErr['ref'])));
$state=($ResultErr['state']);
$errors=array_merge(["context"=>$context], ["func"=>$func],
["ref"=>$ref], ["state"=>$state]);
var_dump($errors);
---------------begine ------------------------
global $error;
if (!is_array($Err)) {
return $error;
} else {
if (!(isset($error['state']))) {
$error['state']="";
}
if (!(isset($error['func']))) {
$error['func']="";
}
if (!(isset($error['ref']))) {
$error['ref']="";
}
if (!(isset($error['context']))) {
$error['context']="";
}
$error['state']=$error['state'];
$error['func']=$error['func'].= $Err["func"].",";
$error['ref']=$error['ref'].= $Err["ref"].",";
$error['context']=$error['context'].= $Err["context"].",";
$error["state"]=$Err["state"];
return $error;
}
}