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

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

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应用程序中处理错误(无论是否致命)的最佳方法是什么?我不想只是死在空白屏幕上,因为那将是非常不友好的用户,而是想在页面正文中输出一条消息,仍然允许页眉和页脚显示。

感谢您的建议!


答案 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。


致命错误(使用异常 - 尝试捕获)

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

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();
}

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

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

不要过度使用异常!

无论你做什么,永远不要使用异常作为检查可以从中恢复的内容的一种方式。阅读另一个问题(完全归功于Anton Gogolev对此的很好的解释,以及其他回答者)为什么会这样。

进一步阅读

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

希望这有助于:)


答案 2

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

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

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

 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 final 来处理

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

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

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


推荐