31

我熟悉一些基础知识,但我想更多地了解的是何时以及为什么应该在 PHP 中使用错误处理(包括抛出异常),尤其是在实时站点或 Web 应用程序中。它是否可以被过度使用,如果是,过度使用是什么样的?有不应该使用的情况吗?此外,关于错误处理,有哪些常见的安全问题?

4

10 回答 10

32

添加到已经说过的内容的一件事是,将 Web 应用程序中的任何错误记录到日志中是至关重要的。这样,正如 Jeff “Coding Horror” Atwood 所建议的那样,您将知道您的用户何时遇到了您的应用程序的问题(而不是“问他们出了什么问题”)。

为此,我推荐以下类型的基础架构:

  • 在您的数据库中创建一个“崩溃”表和一组用于报告错误的包装类。我建议为崩溃设置类别(“阻塞”、“安全”、“PHP 错误/警告”(与异常)等)。
  • 在您的所有错误处理代码中,确保记录错误。始终如一地执行此操作取决于您构建 API 的程度(以上步骤) -如果操作正确,记录崩溃应该是微不足道的。

额外的功劳:有时,您的崩溃将是数据库级别的崩溃:即数据库服务器关闭等。如果是这种情况,您的错误记录基础设施(上面)将失败(您无法将崩溃记录到数据库,因为日志尝试写入数据库)。在这种情况下,我会在您的 Crash 包装器类中编写故障转移逻辑

  • 向管理员发送电子邮件,和/或
  • 将崩溃的详细信息记录到纯文本文件中

所有这些听起来都像是矫枉过正,但相信我,这会影响您的应用程序是否被接受为“稳定”或“不稳定”。这种差异来自这样一个事实,即所有应用程序一开始都是不稳定/崩溃的,但那些了解其应用程序所有问题的开发人员有机会实际修复它。

于 2008-09-25T18:43:38.020 回答
24

粗略地说,错误是 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());
}
于 2008-09-27T11:02:43.437 回答
3

恕我直言,最佳做法是使用以下方法: 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();

?>
于 2008-09-25T21:58:57.643 回答
2

未经处理的错误会停止脚本,仅此一项就是处理它们的一个很好的理由。

通常您可以使用 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 />');
于 2008-09-25T16:33:33.480 回答
2

在您无法明确控制脚本正在处理的数据的情况下,您应该使用错误处理。我倾向于经常使用它,例如在表单验证等地方。知道如何发现代码中容易出错的地方需要一些实践:一些常见的地方是在返回值的函数调用之后,或者在处理来自数据库查询的结果时。你永远不应该假设函数的返回会是你所期望的,你应该确保在预期中编写代码。您不必使用 try/catch 块,尽管它们很有用。很多时候,您可以通过简单的 if/else 检查来解决问题。

错误处理与安全编码实践密切相关,因为有很多“错误”不会导致您的脚本简单地崩溃。虽然不是严格的错误处理本身,但 addedbytes 有一篇很好的 4 篇文章系列,介绍了一些安全 PHP 编程的基础知识,您可以在这里找到。在 stackoverflow 上还有很多其他问题,例如mysql_real_escape_string正则表达式,它们在确认用户输入数据的内容方面非常强大。

于 2008-09-25T17:06:12.917 回答
1

您可以将其存储在日志中,而不是输出 mysql_error。这样您就可以跟踪错误(并且您不依赖用户报告它)并且您可以进入并删除问题。

最好的错误处理是对用户透明的那种,让你的代码解决问题,不需要那个用户伙伴。

于 2008-09-25T16:47:32.760 回答
1

除了立即在代码中处理错误之外,您还可以使用

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())并以“特殊”方式处理它:记录错误、数据库连接信息(主机、用户名、密码)等,甚至可能通过电子邮件通知开发团队可能真的有问题数据库

我用它来补充标准错误处理。我不建议过度使用这种方法

于 2008-09-25T17:55:18.963 回答
1

@ 错误抑制非常慢。

于 2008-09-27T14:38:10.627 回答
0

您还可以使用 Google 表单来捕获和分析异常,而无需维护数据库或可公开访问的服务器。这里有一个教程解释了这个过程。

于 2009-11-18T01:19:45.843 回答
0
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;
    }
}
于 2022-01-08T19:39:50.667 回答