处理 php 页面上错误的最佳方法?

Best way to handle errors on a php page?

提问人:Nate 提问时间:8/10/2012 最后编辑:Nate 更新时间:7/23/2016 访问量:21055

问:

现在我的页面看起来像这样:

if($_GET['something'] == 'somevalue')
{
    $output .= 'somecode';

    // make a DB query, fetch a row
    //...
    $row = $stmt->Fetch(PDO::ASSOC);

    if($row != null)
    {
        $output .= 'morecode';

        if(somethingIsOK())
        {
            $output .= 'yet more page output';
        }
        else
        {
            $error = 'something is most definitely not OK.';
        }
    }
    else
    {
        $error = 'the row does not exist.';
    }
}
else
{
    $error = 'something is not a valid value';
}

if($error == '') // no error
{
    //display $output on page
}
else // an error
{
    // display whatever error occurred on the page
}

我做事的方式是有效的,但它非常繁琐和乏味,因为可能是显而易见的:假设我在代码中间的某个地方调用了一个函数,或者想要检查变量的值,或者验证数据库查询返回了有效的结果,如果它失败了,我想输出一个错误?我必须创建另一个 if/else 块并将所有代码移动到新的 if 块中。这似乎不是一种聪明的做事方式。

我一直在阅读有关try/catch的文章,并一直在考虑将我的所有代码放在try语句中,然后让代码按顺序运行,没有任何if/else块,如果出现问题,则抛出异常。从我所读到的内容来看,这将停止执行并使其直接跳转到 catch 块(就像失败的 if 语句将转到 else 块一样),然后我可以在那里输出错误消息。但这是一种可接受或标准的做法吗?

在构建和输出 HTML 页面的 php 应用程序中处理错误(无论是否致命)的最佳方法是什么?我不想死在空白屏幕上,因为这会对用户非常不友好,而是想在页面正文中输出一条消息,仍然允许显示页眉和页脚。

感谢您的建议!

php 异常 错误处理 try-catch

评论

0赞 Abdulaziz 8/10/2012
我用自己,比如:希望这不是一个坏习惯。exit($error)if($some_error == TRUE){exit('Error.');}
1赞 Nate 8/10/2012
@AzizAG - 这不是一个坏主意,但我的页面上有一个包装器,其中包括页眉和页脚,如果有错误,我希望它以图形方式显示。(即我不想死在空白的白页上,并附上错误消息)。
0赞 Abdulaziz 8/10/2012
@Nate我撒谎,我从不直接使用。我有一个名为:(1)显示页眉(2)回显,(3)显示页脚(4),最后是应用程序。exitexitApp($error)$errorexit
0赞 oxygen 8/14/2012
@AzizAG这就是未捕获的异常的作用,因此您可以直接切换到异常。看我的回答,它告诉你如何使PDO抛出异常,以及如何将所有PHP错误转换为异常(PHP有ErrorException类专门用于此目的)。

答:

38赞 Bart Platak 8/12/2012 #1

有很多方法可以解决这个问题,坦率地说,它们本质上都不是“正确的”。

您必须自己决定哪种方法对您来说更“舒适”——它始终是偏好的集合体(尽管您应该避免某些技术并且有充分的理由)。

这在很大程度上取决于你如何拆分你的逻辑,但是我倾向于将所有可以在函数中返回非致命错误的代码括起来,并使用该函数的返回值来指示存在错误。

对于致命错误,我倾向于使用异常(带块)。try-catch

现在要明确一点:

  • 非致命错误是您可以从中恢复的错误 - 这意味着即使出了问题,仍然有一些代码可以执行并生成一些有价值的输出。例如,如果你想使用协议获取当前时间,但服务器没有响应,你可以决定使用本地函数,并且仍然向用户显示一些有价值的数据。NTPtime
  • 致命错误是您无法从中恢复的错误 - 这意味着发生了非常糟糕的事情,您唯一能做的就是告诉用户该页面无法执行要求的操作。例如,如果您从数据库中获取一些数据并得到 - 没有有价值的数据可以显示,您只能通知用户。SQL Exception

非致命错误(使用函数返回)

使用函数返回作为处理非致命问题的一个很好的例子是,当这不是页面的主要目标时,它试图在页面上显示某个文件的内容(例如,您将有一个函数,在每个页面上显示从文本文件中获取的徽章 - 我知道这很牵强,但请耐心等待)。

function getBadge($file){
    $f = fopen($file,'r');
    if(!$f){
        return null;
    }
    .. do some processing ..
    return $badges;
}

$badges = getBadges('badges.txt');
if(!$badges){
    echo "Cannot display badges.";
} else {
    echo $badges;
}
.. carry on doing whatever page should be doing ..

事实上,函数本身就是一个例子 - 它会返回fopen

成功时返回文件指针资源,错误时返回 FALSE。


致命错误(使用异常 - try-catch)

当你有一些代码需要执行时,因为它正是用户想要的(例如,从数据库中读取所有新闻并将它们显示给用户),你可以使用异常。让我们举一个简单的例子 - 一个用户访问了他的个人资料,并希望查看他收到的所有消息(现在,我们假设它们以纯文本形式存储)。您可能有一个类似这样的函数:

function getMessages($user){
    $messages = array();
    $f = fopen("messages_$user.txt","r");
    if(!$f){
        throw new Exception("Could not read messages!");
    }
    ... do some processing ...
    return $messages;
}

并像这样使用它:

try{
    ..do some stuff..
    $messages = getMessages($_SESSION['user'])); //assuming you store username in $_SESSION
    foreach($messages as $msg){
        echo $msg."<br/>";
    }
} catch(Exception $e){
    echo "Sorry, there was an error: ".$e->getMessage();
}

现在,如果您有一个可以执行所有其他代码的“顶级”脚本,这可能会派上用场。这意味着,例如,在您的 You 中,您只需要:index.php

try{
    .. execute some code, perform some functions ..
} catch(Exception $e){
    echo "Sorry, there was an error: ".$e->getMessage();
}

不要过度使用异常!

无论您做什么,都不要使用异常来检查可以从中恢复的内容。阅读另一个问题(完全归功于安东·戈戈列夫(Anton Gogolev)对此的很好的解释,以及其他回答者)为什么会这样。

延伸阅读

现在,没有比尝试几件事更好的方法来学习如何处理错误,看看什么对你有好处。您可能会发现以下内容很有用:

希望这对:)有所帮助

评论

0赞 uınbɐɥs 8/19/2012
顺便说一句,我猜你的意思不是?badges.txtbages.txt
0赞 Bart Platak 8/19/2012
@ShaquinTrifonoff:啊,是的,确实是错别字:)感谢您的编辑!:)
0赞 pestilence669 8/19/2012
同意其中的大部分内容,但对过度使用异常的警告除外。例如,Java 在任何地方都使用它们。Python 使用了 StopIteration 异常,这应该会严重激怒反异常人群。:)
0赞 Bart Platak 8/19/2012
@Pestilence我喜欢 Java 的原因之一。可悲的是,我不认为PHP被设计为在非特殊情况下使用异常,或者作为正常的流量控制。尝试调试使用异常作为正常流控制的长 PHP 代码 - 这很痛苦。性能也会受到影响。
2赞 pestilence669 8/19/2012
@norfavrell 你真的把较慢的PHP性能作为避免异常的理由吗?:)哈哈。我同意调试是一件痛苦的事情,但我很少调试,因为我是 PHPUnit 的忠实粉丝。我不认为 OP 是在谈论正常的控制流,只是错误。尽管如此,即使是例外也会出错:JDBC:是否曾经编写过正确的三嵌套 try-catch-finally 结构以进行快速阅读??
1赞 CodeTalk 8/12/2012 #2

查询的 PDO 错误异常处理,实际上所有代码都应该运行:

try{

}

catch{


}

finally{

}

这样做的原因是,当您可以粗略地确定冗长脚本中发生错误的位置时,它使调试变得更加容易

更多信息在这里: http://php.net/manual/en/language.exceptions.php

评论

4赞 Erty Seidohl 8/17/2012
我相信 finally{} 现在只在 RFC 土地上,实际上无法使用。
0赞 CodeTalk 8/20/2012
它供将来使用。OP 了解未来的潜力是件好事。不过好点子。
3赞 uınbɐɥs 8/13/2012 #3

使用是您可以使用的最干净的解决方案之一。try-catch

我做了一个示例,当发生错误时仍然显示页眉和页脚,使用转换为以下格式的代码:try-catch

PHP格式:

<?php
try {
    $output = array();
    if($_GET['something'] != 'somevalue') throw new Exception('something does not have a valid value.');
    $output[] = 'Some Code';
    $row = mt_rand(0, 10) < 5 ? null : mt_rand(0, 100);
    if($row === null) throw new Exception('The row does not exist.');
    $output[] = $row;
    if(!somethingIsOK()) throw new Exception('Something is most definitely not OK.');
    $output[] = 'Yet more page output';
} catch(Exception $e) {
    $output[] = 'Error: ' . $e->getMessage(); // To show output and error
    $output = array('Error: ' . $e->getMessage()); // To only show error
}
function somethingIsOK() {
    return mt_rand(0, 10) < 5;
}
?>

HTML格式:

<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta charset="UTF-8" />
    <title>PHP Error test</title>
    <style type="text/css">
body {
    background: #eee;
    text-align: center
}
#content {
    padding: 60px
}
#header {
    padding: 30px;
    background: #fff
}
#footer {
    padding: 10px;
    background: #ddd
}
    </style>
</head>
<body>
    <div id="header">Header</div>
    <div id="content">
<?php echo implode('<br />', $output); ?>

    </div>
    <div id="footer">Footer</div>
</body>
</html>

引用:

6赞 oxygen 8/13/2012 #4

PHP 有一个内置的类 ErrorException,用于将 PHP 错误转换为异常,如果不加以处理,自然会停止执行。

异常改进了错误处理机制(try catch)和更好的调试信息(堆栈跟踪)。

将其包含在执行路径的顶部(配置,或所有代码中首先包含的内容):

 set_error_handler(function($nNumber, $strMessage, $strFilePath, $nLineNumber){
      throw new \ErrorException($strMessage, 0, $nNumber, $strFilePath, $nLineNumber);
 }, /*E_ALL*/ -1);

虽然 PDO 支持抛出异常,但默认情况下它是关闭的,您必须启用它:

 $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

如果使用 MySQL,您还需要一个未设置必填字段的错误以及默认情况下原谅的许多其他错误/警告:

 $pdo->exec("SET sql_mode = 'STRICT_ALL_TABLES'");

可以像在许多其他编程语言中使用 try catch finally 一样处理异常:

try
{
    echo $iAmAnUndefinedVariable;
}
catch(\Throwable $exception)
{
    /*[...]*/
}

在验证内容时,只需抛出异常:throw new Exception("Missing URL variable userId!");

如果 PHP 有一天从遗留的错误报告中彻底脱离出来,并且默认抛出异常(弃用 error_reporting() 并更改默认值),那就太好了。

6赞 Edson Medina 8/14/2012 #5

这更加优雅和可读。

try
{

    if($_GET['something'] != 'somevalue') 
    {
        throw new Exception ('something is not a valid value');
    }


    $output .= 'somecode';

    // make a DB query, fetch a row
    //...
    $row = $stmt->Fetch(PDO::ASSOC);

    if($row == null)
    {
        throw new Exception ('the row does not exist.');
    }


    $output .= 'morecode';


    if(somethingIsOK())
    {
        $output .= 'yet more page output';
    }
    else
    {
        throw new Exception ('something is most definitely not OK.');
    }


    echo $output;

}
catch (Exception $e)
{
    echo $e->getMessage();
}

评论

1赞 Nate 8/14/2012
这就是我提出问题时所设想的,但我不确定这是否是 try/catch 的正确使用。
-3赞 Noobie 8/17/2012 #6

如果你正在寻找一个看起来很漂亮并且会起作用的代码结构 - 你可以使用我一直使用的方法。例如,验证变量:whitelist$_GET

$error = false;

if(!isset($_GET['var'])) 
{
    $error = 'Please enter var\'s value';
}
elseif(empty($_GET['var'])) 
{
    $error = 'Var shouldn\'t be empty';
}
elseif(!ctype_alnum($_GET['var'])) 
{
    $error = 'Var should be alphanumeric';
}

//if we have no errors -> proceed to db part
if(!$error) 
{
    //inserting var into database table
}

所以,就是这样,只有 2 个块,没有嵌套if/elseif

评论

0赞 uınbɐɥs 8/19/2012
我没有投反对票,但 Nate 正在寻找没有语句的东西,因此如果要添加功能,代码需要添加最少的代码。if
0赞 Noobie 8/30/2012
这就是 if/else 块的用途 - 用于检查变量和值
0赞 uınbɐɥs 8/31/2012
诚然,它们是,但这不是处理错误的有效方法。此外,这基本上是@Nate已经拥有的。
0赞 Noobie 9/2/2012
@ShaquinTrifonoff,@Nate使用嵌套块,这不利于错误检查,这就是我提出这种方法的原因;)干杯if/else
0赞 uınbɐɥs 9/3/2012
@Nate不想要块。如果您使用 ,随着项目变得越来越复杂,不可避免地需要嵌套块。因此,需要一种更清洁的解决方案。请参阅接受的答案和我的答案。干净多了。if/elseif/else
0赞 Dmitry 8/19/2012 #7

创建错误处理程序 (set_error_handler) 并在其中抛出异常。
这将对不支持异常的函数有所帮助。

0赞 Steve 11/27/2015 #8

使用错误处理函数正确处理PHP错误和警告。( 请看这里的例子 )

在 PHP 中处理错误的最佳方法是, 您可以通过在 php 文件顶部添加此行来停止所有错误报告 -

error_reporting(0);

//OR

error_reporting('E_ALL');

// Predefined Constant

PHP 中使用函数进行错误处理:

  • debug_backtrace — 生成回溯
  • debug_print_backtrace — 打印回溯
  • error_clear_last — 清除最近的错误
  • error_get_last — 获取上次发生的错误
  • error_log — 向定义的错误处理发送错误消息 例 程
  • error_reporting — 设置报告的 PHP 错误
  • restore_error_handler — 恢复以前的错误处理程序函数
  • restore_exception_handler — 恢复先前定义的异常 handler 函数
  • set_error_handler — 设置用户定义的错误处理程序函数
  • set_exception_handler — 设置用户定义的异常处理程序 功能
  • trigger_error — 生成用户级错误/警告/通知消息
  • user_error — trigger_error的别名

上面列出的所有函数都用于 PHP 中的错误处理。