PHP: 异常与错误?

也许我在 PHP 手册的某个地方遗漏了它,但是错误和异常之间到底有什么区别呢?我能看到的唯一区别是错误和异常的处理方式不同。但是什么导致异常和什么导致错误呢?

48993 次浏览

异常是由代码使用抛出(throw)、错误(error)有意抛出的,但不是这样。

错误是由一些不常处理的错误(IO 错误、 TCP/IP 错误、空引用错误)造成的

例外情况是 抛出-它们是为了被捕获。错误通常是不可恢复的。让我们举个例子-你有一个代码块,将插入一行到数据库。有可能这个调用失败(重复的 ID)-您将希望有一个“错误”,在这种情况下是一个“异常”。插入这些行时,可以执行下面的操作

try {
$row->insert();
$inserted = true;
} catch (Exception $e) {
echo "There was an error inserting the row - ".$e->getMessage();
$inserted = false;
}


echo "Some more stuff";

程序执行将继续-因为您“捕获”了异常。除非捕获到异常,否则将视为错误。它将允许您在程序失败后继续执行程序。

这里要补充的一点是关于处理异常和错误。对于应用程序开发人员来说,错误和异常都是“坏事”,您需要记录这些错误来了解应用程序存在的问题,这样客户才能获得更好的长期体验。

因此,编写一个与处理异常相同的错误处理程序是有意义的。

我通常将 set_error_handler转换为一个接受错误并抛出异常的函数,这样无论发生什么情况,我都只需要处理异常。没有更多的 @file_get_contents只是漂亮和整洁的尝试/接住。

在调试的情况下,我还有一个异常处理程序,它输出一个 asp.net 样的页面。我张贴这在道路上,但如果要求我将后期的例子来源。

编辑:

此外,正如所承诺的那样,我已经剪切和粘贴了一些代码,以制作一个示例。

<?php


define( 'DEBUG', true );


class ErrorOrWarningException extends Exception
{
protected $_Context = null;
public function getContext()
{
return $this->_Context;
}
public function setContext( $value )
{
$this->_Context = $value;
}
    

public function __construct( $code, $message, $file, $line, $context )
{
parent::__construct( $message, $code );


$this->file = $file;
$this->line = $line;
$this->setContext( $context );
}
}


/**
* Inspire to write perfect code. everything is an exception, even minor warnings.
**/
function error_to_exception( $code, $message, $file, $line, $context )
{
throw new ErrorOrWarningException( $code, $message, $file, $line, $context );
}
set_error_handler( 'error_to_exception' );


function global_exception_handler( $ex )
{
ob_start();
dump_exception( $ex );
$dump = ob_get_clean();
// send email of dump to administrator?...


// if we are in debug mode we are allowed to dump exceptions to the browser.
if ( defined( 'DEBUG' ) && DEBUG == true )
{
echo $dump;
}
else // if we are in production we give our visitor a nice message without all the details.
{
echo file_get_contents( 'static/errors/fatalexception.html' );
}
exit;
}


function dump_exception( Exception $ex )
{
$file = $ex->getFile();
$line = $ex->getLine();


if ( file_exists( $file ) )
{
$lines = file( $file );
}
    

?><html>
<head>
<title><?= $ex->getMessage(); ?></title>
<style type="text/css">
body {
width : 800px;
margin : auto;
}
        

ul.code {
border : inset 1px;
}
ul.code li {
white-space: pre ;
list-style-type : none;
font-family : monospace;
}
ul.code li.line {
color : red;
}
            

table.trace {
width : 100%;
border-collapse : collapse;
border : solid 1px black;
}
table.thead tr {
background : rgb(240,240,240);
}
table.trace tr.odd {
background : white;
}
table.trace tr.even {
background : rgb(250,250,250);
}
table.trace td {
padding : 2px 4px 2px 4px;
}
</style>
</head>
<body>
<h1>Uncaught <?= get_class( $ex ); ?></h1>
<h2><?= $ex->getMessage(); ?></h2>
<p>
An uncaught <?= get_class( $ex ); ?> was thrown on line <?= $line; ?> of file <?= basename( $file ); ?> that prevented further execution of this request.
</p>
<h2>Where it happened:</h2>
<? if ( isset($lines) ) : ?>
<code><?= $file; ?></code>
<ul class="code">
<? for( $i = $line - 3; $i < $line + 3; $i ++ ) : ?>
<? if ( $i > 0 && $i < count( $lines ) ) : ?>
<? if ( $i == $line-1 ) : ?>
<li class="line"><?= str_replace( "\n", "", $lines[$i] ); ?></li>
<? else : ?>
<li><?= str_replace( "\n", "", $lines[$i] ); ?></li>
<? endif; ?>
<? endif; ?>
<? endfor; ?>
</ul>
<? endif; ?>


<? if ( is_array( $ex->getTrace() ) ) : ?>
<h2>Stack trace:</h2>
<table class="trace">
<thead>
<tr>
<td>File</td>
<td>Line</td>
<td>Class</td>
<td>Function</td>
<td>Arguments</td>
</tr>
</thead>
<tbody>
<? foreach ( $ex->getTrace() as $i => $trace ) : ?>
<tr class="<?= $i % 2 == 0 ? 'even' : 'odd'; ?>">
<td><?= isset($trace[ 'file' ]) ? basename($trace[ 'file' ]) : ''; ?></td>
<td><?= isset($trace[ 'line' ]) ? $trace[ 'line' ] : ''; ?></td>
<td><?= isset($trace[ 'class' ]) ? $trace[ 'class' ] : ''; ?></td>
<td><?= isset($trace[ 'function' ]) ? $trace[ 'function' ] : ''; ?></td>
<td>
<? if( isset($trace[ 'args' ]) ) : ?>
<? foreach ( $trace[ 'args' ] as $i => $arg ) : ?>
<span title="<?= var_export( $arg, true ); ?>"><?= gettype( $arg ); ?></span>
<?= $i < count( $trace['args'] ) -1 ? ',' : ''; ?>
<? endforeach; ?>
<? else : ?>
NULL
<? endif; ?>
</td>
</tr>
<? endforeach;?>
</tbody>
</table>
<? else : ?>
<pre><?= $ex->getTraceAsString(); ?></pre>
<? endif; ?>
</body>
</html><? // back in php
}
set_exception_handler( 'global_exception_handler' );


class X
{
function __construct()
{
trigger_error( 'Whoops!', E_USER_NOTICE );
}
}


$x = new X();


throw new Exception( 'Execution will never get here' );


?>

回复: “但是错误和异常之间到底有什么区别呢?”

这里有很多关于差异的好的答案。我还要补充一些还没有被讨论过的东西——性能。具体来说,这是为了处理抛出/处理异常与处理返回代码(成功或某个错误)之间的区别。通常,在 php 中,这意味着返回 falsenull,但是它们可以更加详细,例如文件上传: http://php.net/manual/en/features.file-upload.errors.php您甚至可以返回一个 Exception 对象!

我用不同的语言/系统执行了一些性能测试。一般来说,异常处理比检查错误返回代码要慢10,000倍。

所以,如果它绝对,肯定需要在它开始之前就完成执行-那么,你就不走运了,因为时间旅行并不存在。没有时间旅行,回程代码是最快的选择。

编辑:

PHP 对异常处理进行了高度优化。实际测试表明,抛出异常只比返回值慢2-10倍。

一旦定义了 set _ error _ handler () ,错误处理程序就类似于 Exception 的错误处理程序:

 <?php
function handleErrors( $e_code ) {
echo "error code: " . $e_code . "<br>";
}


set_error_handler( "handleErrors" );


trigger_error( "trigger a fatal error", E_USER_ERROR);
echo "after error."; //it would run if set_error_handler is defined, otherwise, it wouldn't show
?>

我想你要找的答案是

错误是您习惯使用的标准内容,比如回显不存在的 $变量。
只有从 PHP5开始并在处理对象时才会出现例外。

简单来说:

异常是在处理对象时出现的错误。 Try/catch 语句允许您对它们进行一些处理,它的用法与 if/else 语句非常相似。 尝试这样做,如果问题,没有关系,这样做。

如果您没有“捕获”一个异常,那么它就会变成一个标准错误。

错误是 php 的基本错误,通常会导致脚本中断。

Try/catch 通常用于建立数据库连接(如 PDO) ,如果您希望重定向脚本或者在连接无法工作时执行其他操作,这样做是可以的。但是如果您只想显示错误消息并停止脚本,那么您就不需要它了,未捕获的异常将变成一个致命错误。或者也可以使用站点范围的错误处理设置。

希望能帮上忙

我打算给你们一个非同寻常的关于错误控制的讨论。

几年前,我在一种语言中构建了一个非常好的错误处理程序,尽管一些名称已经改变,但错误处理的原则仍然是相同的。我有一个自定义的多任务操作系统,必须能够从所有级别的数据错误恢复,没有内存泄漏,堆栈增长或崩溃。因此,下面是我对错误和异常必须如何操作以及它们如何不同的理解。我只想说我不了解 try catch 的内部工作原理,所以我猜测一些度量。

在错误处理的掩护下发生的第一件事是从一个程序状态跳转到另一个程序状态。怎么做到的?我会说到的。

从历史上看,错误比较古老和简单,异常比较新,比较复杂和有能力。错误可以很好地发挥作用,直到你需要把它们冒出来,这相当于把一个难题交给你的主管。

错误可以是数字,比如错误数字,有时还可以包含一个或多个关联的字符串。例如,如果发生了文件读取错误,您可以报告它是什么,并且可能会优雅地失败。(嘿,比起像以前那样直接崩溃,这是一个进步。)

有关异常的常见说法是,异常是位于特殊异常堆栈上的对象分层。它类似于程序流的返回堆栈,但它保持一个返回状态,只用于尝试和捕获错误。(我以前叫它们 ePush 和 ePop,然后呢?Abort 是一个条件抛出,它将 ePop 并恢复到该级别,而 Abort 是一个完整的骰子或退出。)

堆栈底部是关于初始调用方的信息,初始调用方是知道外部尝试启动时(通常是程序启动时)状态的对象。在那上面,或者堆栈的下一层,上面是子层,下面是父层,是下一个内部 try/catch 块的异常对象。

如果你把一个尝试放在一个尝试里面,你就是把内部的尝试堆叠在外部的尝试之上。当内部 try 中出现错误时,内部 catch 无法处理错误,或者错误被抛给外部 try,然后控制被传递给外部 catch 块(对象) ,看看它是否能处理错误,也就是说,你的主管。

因此,这个错误堆栈真正做的是能够标记和恢复程序流和系统状态,换句话说,它允许程序在出错时不会崩溃返回堆栈,也不会搞乱其他程序(数据)。因此,它还可以保存任何其他资源的状态,比如内存分配池,这样当 catch 完成时,它就可以清理这些资源。一般来说,这可能是一件非常复杂的事情,这就是异常处理通常很慢的原因。一般来说,需要进入这些异常块的状态很多。

因此,try/catch 块可以设置一种状态,以便在其他一切都搞砸时能够返回到该状态。就像父母一样。当我们的生活变得一团糟时,我们可以回到父母的怀抱,他们会让一切重新回到正轨。

希望我没让你失望。

这个问题的答案值得我们谈谈房间里的大象

错误是在运行时处理错误条件的旧方法。通常,代码在执行某些代码之前会调用类似 set_error_handler的内容。遵循汇编语言中断的传统。下面是一些 BASIC 代码的外观。

on error :divide_error


print 1/0
print "this won't print"


:divide_error


if errcode = X
print "divide by zero error"

很难确保使用正确的值调用 set_error_handler。更糟糕的是,可以调用一个单独的过程来更改错误处理程序。另外,很多时候调用都穿插有 set_error_handler调用和处理程序。代码很容易很快失去控制。异常处理通过规范化优秀代码实际执行的语法和语义得到了拯救。

try {
print 1/0;
print "this won't print";
} catch (DivideByZeroException $e) {
print "divide by zero error";
}

没有单独的函数或调用错误处理程序的风险。现在可以保证代码位于相同的位置。另外,我们得到了更好的错误消息。

PHP 过去只有错误处理,而许多其他语言已经演变成了更好的异常处理模型。最终 PHP 的制造商实现了异常处理。但可能支持旧代码,它们保留了错误处理,并提供了一种方法,使错误处理看起来像异常处理。除此之外,不能保证某些代码不会重置错误处理程序,而这正是异常处理应该提供的。

最终答案

在实现异常处理之前编写的错误可能仍然是错误。新的错误很可能是例外。但是没有哪种设计或逻辑是错误的,哪种是例外的。它只是基于编码时可用的内容,以及编码程序员的偏好。

如其他答案所述,在 PHP 中将错误处理程序设置为异常抛出程序是处理错误的最佳方法。我使用更简单的设置:

set_error_handler(function ($errno, $errstr, $errfile, $errline ) {
if (error_reporting()) {
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
}
});

请注意 error_reporting()检查,以保持 @操作员的工作。另外,没有必要定义自定义异常,PHP 有一个很好的类。

抛出异常的最大好处是异常具有与它们相关联的堆栈跟踪,因此很容易找到问题所在。

来自 PHP: 例外-手动:

从 PHP 7.1.0开始,catch 块可以使用管道(|)字符指定多个异常。这对于同时处理来自不同类层次结构的不同异常非常有用。

try {
// do something
} catch (Error | Exception $e) {
echo $e->getMessage();
}

您可以添加此注释

function doSomething()
{
/** @noinspection PhpUnhandledExceptionInspection */
throw new Exception();
}