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

Nat*_*ate 35 php error-handling exception-handling exception try-catch

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

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
}
Run Code Online (Sandbox Code Playgroud)

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

我一直在阅读关于try/catch的内容,并且一直在考虑将我的所有代码放在try语句中,然后让代码按顺序运行而不使用任何if/else块,如果出现故障则抛出异常.根据我的阅读,这将停止执行并使其直接跳转到catch块(就像失败的if语句将转到else块),然后我可以输出错误消息.但这是可接受的还是标准的做法?

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

谢谢你的建议!

Bar*_*tak 34

有很多方法可以解决这个问题,坦率地说,没有一种方法可以解决这个问题.

你必须自己决定,哪种方法对你来说更"舒服" - 它始终是一个偏好的主体(虽然你应该避免使用某些技术并且有充分的理由).

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

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

现在只是为了清楚:

  • 非致命错误是您可以从中恢复的错误- 意味着即使出现问题,仍然可以执行一些代码并生成一些有价值的输出.例如,如果您想使用NTP协议获取当前时间,但服务器没有响应,您可以决定使用本地time功能并仍然向用户显示一些有价值的数据.
  • 致命错误是您无法恢复的错误- 意味着发生了一些非常糟糕的事情,您唯一能做的就是告诉您的用户该页面无法执行所要求的操作.例如,如果您从数据库中获取某些数据并获得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 ..
Run Code Online (Sandbox Code Playgroud)

实际上,函数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;
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

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();
}
Run Code Online (Sandbox Code Playgroud)

现在,如果你有一个可以执行所有其他代码的"顶级"脚本,这可能会派上用场.这意味着,例如,在你的index.php你将只有:

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

不要过度使用例外!

无论你做什么,都不要使用例外来检查你可以恢复的东西.阅读另一个问题(完全归功于Anton Gogolev对此以及其他答案者的非常好的解释)至于为什么会出现这种情况.

进一步阅读

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

希望这可以帮助 :)

  • @norfavrell您是否真的引用较慢的PHP*性能*作为避免异常的原因?:) 大声笑.我同意调试很痛苦,但我很少调试,因为我是PHPUnit的忠实粉丝.我不认为OP正在谈论正常的控制流程,只是错误.尽管如此,即使异常也会出错:JDBC:曾经编写过适当的三层嵌套try-catch-finally结构来快速读取? (2认同)

Tib*_*tan 6

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

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

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

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

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

 $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
Run Code Online (Sandbox Code Playgroud)

如果使用MySQL,您还需要一个错误,因为默认情况下不设置必填字段和许多其他错误/警告:

 $pdo->exec("SET sql_mode = 'STRICT_ALL_TABLES'");
Run Code Online (Sandbox Code Playgroud)

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

try
{
    echo $iAmAnUndefinedVariable;
}
catch(\Throwable $exception)
{
    /*[...]*/
}
Run Code Online (Sandbox Code Playgroud)

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

如果PHP有一天从遗留错误报告事件中彻底打破并且默认情况下抛出异常(弃用error_reporting()并更改默认值),那将是很好的.


Eds*_*ina 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();
}
Run Code Online (Sandbox Code Playgroud)