处理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 }
我做事情的方式是有效的,但是对于可能很明显的事情来说,这是非常麻烦和乏味的:假设我在代码的中间调用某个函数,或者想要检查一个variables的值,或者validation一个DB查询返回一个有效的结果,如果失败,我想输出一个错误? 我将不得不做另一个if / else块,并移动新的if块中的所有代码。 这似乎不是一个聪明的做事方式。
我一直在阅读关于try / catch的内容,并且一直在考虑将所有代码放在try语句中,然后让代码顺序地运行,而不用任何if / else块,如果某个失败就抛出一个exception。 从我读过的,这将停止执行,并使其直接跳到catch块(就像一个失败的if语句将去到else块),然后我可以输出错误消息。 但这是一个可以接受的标准做法吗?
在构build和输出HTML页面的PHP应用程序中,处理错误的最好方法是什么? 我不想只是因为一个空白的屏幕而死,因为这将是非常不友好的用户,而是希望在页面的主体中输出消息,仍然允许显示页眉和页脚。
谢谢你的build议!
有很多方法可以解决这个问题,坦率地说,他们中没有一个本质上是“正确的”。
你必须自己决定,哪一种方法对你来说更“舒适” – 它始终是一种优先select(虽然有一些技巧你应该避免,并且有很好的理由)。
它将高度依赖于你如何分解你的逻辑,但是我倾向于把所有能够返回非致命错误的代码包含在一个函数中,并且使用所述函数的返回值来指示有错误。
对于致命错误,我倾向于使用exception(使用try-catch
块)。
现在要清楚:
- 一个非致命的错误是一个错误,你可以从中恢复 – 这意味着即使出了问题,仍然有一些代码可以被执行并产生一些有价值的输出。 例如,如果您想使用
NTP
协议获取当前时间,但服务器没有响应,则可以决定使用本地time
function,并向用户显示一些有价值的数据。 - 一个致命的错误是一个你无法恢复的错误 – 这意味着发生了一件非常糟糕的事情,你唯一能做的就是告诉你的用户页面不能完成被请求的内容。 例如,如果您从数据库中获取某些数据并获取了
SQL Exception
,则不会显示有价值的数据,只能通知用户这一点。
非致命错误(使用函数返回)
使用函数返回作为处理非致命问题的一个很好的例子是尝试在页面上显示某些文件的内容时,这不是页面的主要目标 (例如,一个显示徽章的function,从文本文件中提取,在每一个页面上 – 我知道这是遥不可及的,但忍受着我)。
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。
致命错误(使用例外 – 尝试捕获)
当你有一些代码需要被执行,因为它正是用户想要的(例如从数据库中读取所有新闻并显示给用户),你可以使用exception。 我们举一个简单的例子 – 用户访问他的个人资料,并希望看到他所有的消息(现在让我们假设他们以纯文本forms存储)。 你可能有一个像这样的function:
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(); }
不要过度使用例外!
无论您做什么,都不要将exception用作检查可以从中恢复的方法。 读一下另外一个问题 (关于这一点,以及其他答案,请充分说明Anton Gogolev的一个很好的解释),为什么会出现这种情况。
进一步阅读
现在没有更好的方法来学习如何处理错误,而不是尝试几件事,看看对你有什么好处。 你可能会发现下面的有用的:
- 关于PHPexception处理的W3School
- error handling简短教程 (类似于我的函数返回方法)
- 关于PHPerror handling的广泛教程 – 包括使用
trigger_error()
函数,我没有提到,因为我没有使用它,并且不太了解它,但是显然它非常有用。 这是一个特别好的阅读。
希望这可以帮助 :)
这是更优雅和可读性。
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(); }
PHP有一个内置的类ErrorException ,用于将PHP错误转换为exception,如果未处理,自然会停止执行。
exception改进了error handling机制(try catch)和更好的debugging信息(堆栈跟踪)。
将其包含在执行path的顶部(config,或者包含所有代码的东西):
set_error_handler(function($nNumber, $strMessage, $strFilePath, $nLineNumber){ throw new \ErrorException($strMessage, 0, $nNumber, $strFilePath, $nLineNumber); }, /*E_ALL*/ -1);
尽pipePDO支持抛出exception,但默认情况下是closures的,您必须启用它:
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
如果使用MySQL,您还希望在设置默认值时不设置必填字段和许多其他错误/警告,
$pdo->exec("SET sql_mode = 'STRICT_ALL_TABLES'");
终于可以像使用try catch一样在许多其他编程语言中处理exception:
try { echo $iAmAnUndefinedVariable; } catch(\Throwable $exception) { /*[...]*/ }
当validation的东西,只是抛出exception: throw new Exception("Missing URL variable userId!");
如果PHP有一天从传统的错误报告中彻底清除,并且默认抛出exception(弃用error_reporting()并更改默认值),那将会很不错。
使用try-catch
是您可以使用的最干净的解决scheme之一。
我做了一个例子,当出现错误时,使用转换为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>
参考文献:
- PHP:例外 – 手动
- PHP:CATCH – 手册
查询的PDO错误exception处理,实际上所有的代码都应该运行:
try{ } catch{ } finally{ }
这样做的原因是,当您可以精确地查明在冗长的脚本中发生错误的位置时,它会使debugging更容易
更多信息在这里: http : //php.net/manual/en/language.exceptions.php
创builderror handling程序(set_error_handler)并在其中引发exception。
这将有助于function,不支持例外。
通过使用error handling函数正确处理PHP错误和警告。 (请看这里的例子)
PHP中error handling的最佳方法是,您可以通过在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 handling例程
- error_reporting – 设置报告哪些PHP错误
- restore_error_handler – 恢复以前的error handling函数
- restore_exception_handler – 恢复先前定义的exception处理函数
- set_error_handler – 设置用户定义的error handling函数
- set_exception_handler – 设置用户定义的exception处理函数
- trigger_error – 生成用户级错误/警告/通知消息
- user_error – trigger_error的别名
上面列出的所有函数都用于PHP中的error handling。
如果你正在寻找一个漂亮的代码结构,你可以使用我总是使用的whitelist
方法。 例如 – validation一个$_GET
variables:
$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
块,没有嵌套