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

问题:

寻找使用 PDO 处理错误的最佳实践。我在网站、SO、书籍等上找到的选项。

  1. 很多网站说您应该在catch 块中回显错误消息。
  2. SO 上的大量用户表示,出于安全风险,您永远不应回显错误消息。
  3. 其他人建议将其记录到文档根目录之外的日志文件中。
  4. 有些使用错误处理将其记录到 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 中处理错误的最佳做法是什么?

【问题讨论】:

    标签: php mysql pdo error-handling database-connection


    【解决方案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/…
    猜你喜欢
    • 1970-01-01
    • 2018-09-12
    • 2017-11-26
    • 1970-01-01
    • 2016-06-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多