使用 PDO 处理错误的最佳实践

Posted

技术标签:

【中文标题】使用 PDO 处理错误的最佳实践【英文标题】:Best practice for error handling using PDO 【发布时间】:2015-12-02 00:11:28 【问题描述】:

问题:

寻找。我在网站、SO、书籍等上找到的选项。

    很多网站说您应该在catch 块中回显错误消息。 SO 上的大量用户表示,出于安全风险,您永远不应回显错误消息。 其他人建议将其记录到文档根目录之外的日志文件中。 有些使用错误处理将其记录到 SQL 表中。

有了众多选项,您很容易沉迷于应该使用的选项。当然,您可以使用 MVC 框架并让它为您处理错误日志记录,但如果您不使用 MVC,它会是什么样子。

据我了解,开发环境中的错误处理应如下所示:

display_errors = On
display_startup_errors = On
error_reporting = -1
log_errors = On

或者如果无法访问 php.ini 文件:

error_reporting(-1);
ini_set("display_errors", 1);

而在生产环境

display_errors = Off
display_startup_errors = Off
error_reporting = E_ALL
log_errors = On

或者如果无法访问 php.ini 文件:

error_reporting(0);

生产环境中的数据库连接为例。

代码:

<?php
  // Error handling
  error_reporting(0);

  // Get credentials from outside document root
  require_once('../settings.php');

  // Tests connection to database
  try 
    $dbh = new PDO(
            sprintf(
              'mysql:host=%s;dbname=%s;port=%s;charset=%s',
              $settings['host'],
              $settings['name'],
              $settings['port'],
              $settings['charset']
            ),
            $settings['username'],
            $settings['password']
    );
    // Prevents emulated prepares and activates error handling
    // PDO::ERRMODE_EXCEPTION
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  
  // Catches errors raised by PDO
  catch (PDOException $e) 
    // Prints error messages to file
    file_put_contents('/home/ubuntu/errors.log', 'Error: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
    // Shows generic error message to user
    header('Location: 404.php');
    exit;
  
?>

问题:

在 PHP 中处理一般错误的最佳做法是什么? 在 catch-block 中处理错误的最佳做法是什么?

【问题讨论】:

【参考方案1】:

这是一个非常好的问题,但一开始就有一个错误的前提:您将 PDO 的错误报告与站点范围的错误报告分开。这几乎没有什么意义:PDO 错误在各个方面都与其他错误相同——文件系统错误、HTTP 错误等等。因此,没有理由建立仅 PDO 的错误报告。您只需要正确设置站点范围的错误报告即可。

关于 php.ini 不可访问性还有一个错误假设:您始终可以使用 ini_set() 函数设置任何配置指令。因此,将 error_reporting 设置为灾难性级别 0 的原因不止一个。

要回答您的其余问题,您只需要一点常识即可。

很多网站都说你应该在你的 catch 块中回显你的错误消息。 SO 上的大量用户表示,出于安全风险,您永远不应回显错误消息。

你自己怎么看?向用户显示系统错误消息有什么好处吗?向恶意用户展示系统内部有什么好处?

其他人建议将其记录到文档根目录之外的日志文件中。

您对此有异议吗?

有些使用错误处理将其记录到 SQL 表中。

您不认为将数据库错误记录到数据库中很矛盾吗?

在 PHP 中处理一般错误的最佳实践是什么?

您已经展示过了:在 dev 中显示并登录 prod。通过几个简单的配置选项,所有这些都在站点范围内进行控制。

在 catch-block 中处理错误的最佳做法是什么?

根本不使用 try-catch 块进行错误报告。您不会为应用中的每个查询编写带有友好错误消息的 catch 块强>,正如另一个答案中所建议的那样,是吗?

因此你的代码必须是

<?php
  // Error handling
  error_reporting(-1);
  ini_set('display_errors',0);
  ini_set('log_errors',1);

  // Get credentials from outside document root
  require_once('../settings.php');

  // Tests connection to database
    $dbh = new PDO(
            sprintf(
              'mysql:host=%s;dbname=%s;port=%s;charset=%s',
              $settings['host'],
              $settings['name'],
              $settings['port'],
              $settings['charset']
            ),
            $settings['username'],
            $settings['password']
    );
    // Prevents emulated prepares and activates error handling
    // PDO::ERRMODE_EXCEPTION
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

现在回答您在评论中提出的问题。

自定义错误屏幕是完全不同的事情,您的代码使用它尤其糟糕。既不应该是 404 错误,也不应该使用 HTTP 重定向(这对 SEO 非常不利)。

要创建自定义错误页面,您必须使用 Web 服务器功能(首选)或 PHP 脚本中的错误处理程序。

当遇到致命错误(未捕获的异常是一个)时,PHP 响应的不是 200 OK HTTP 状态,而是 5xx 状态。每个网络服务器都可以捕捉到这种状态并显示相应的错误页面。例如。对于 Apache 它会是

ErrorDocument 503 server_error.html

你可以写任何你想要的借口。

或者您可以在 PHP 中设置一个自定义错误处理程序,它也可以处理所有 PHP 错误,可以在我写的文章中看到一个示例:The (im)proper use of try..catch.

【讨论】:

不向用户显示错误消息听起来不太优雅。我的意思是,即使是 Facebook 在发生错误时也会显示一般错误。在您的示例中,没有显示此类通用错误。这真的是一种处理错误的优雅方式吗? 感谢您的更新!您能否举例说明自定义错误处理程序的外观,以便了解应该如何实施?如果没有,您能否提供讨论此问题的文章的链接?只是试图充实一种合理的错误处理方法。我对这样的处理程序如何处理显示自定义错误消息特别感兴趣。 感谢您的链接,信息量很大。我沿着set_error_handler()set_exception_handler() 的路径前进,它把我带到了一个有趣的世界。但是有一件事让我很困惑,为什么你在一个处理程序中使用 include() 而在另一个处理程序中使用 readfile()? 这只是一个复制粘贴错误。在原始代码中包含一个 php 文件,即使在错误页面上也能显示广告。 感谢您的澄清。我采纳了您的所有建议并编写了三个函数,希望您在有时间时继续考虑审查:codereview.stackexchange.com/questions/103954/…

以上是关于使用 PDO 处理错误的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spring JMS 进行错误处理的最佳实践

使用 NSURLSession 处理 HTTP 错误的最佳实践?

JavaScript 错误处理的最佳实践是啥?

Invoke-AzVMRunCommand - 错误处理的最佳实践?

CodeIgniter 中处理数据库错误的最佳实践

爆肝3天只为Golang 错误处理最佳实践