PHP:异常vs错误?

时间:2009-05-08 19:50:19

标签: php exception exception-handling error-handling

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

12 个答案:

答案 0 :(得分:81)

例外是thrown - 它们是为了被抓住。错误通常是不可恢复的。让我们举个例子 - 你有一个代码块可以将一行插入数据库。此调用可能失败(重复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";

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

答案 1 :(得分:52)

我通常set_error_handler到一个接收错误并抛出异常的函数,这样无论发生什么事情,我都会有异常处理。没有更多@file_get_contents只是很好,整齐的尝试/捕获。

在调试情况下,我还有一个异常处理程序,可以输出类似asp.net的页面。我在路上发布这个,但如果要求我会稍后发布示例源。

编辑:

除了承诺之外,我还将我的一些代码剪切并粘贴在一起制作样本。我已将下面的文件保存到我的工作站you can NO LONGER see the results here上(因为链接已损坏)。

<?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' );

?>

答案 2 :(得分:14)

答案值得讨论房间里的大象

错误是在运行时处理错误条件的旧方法。通常,代码在执行某些代码之前会调用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的制造商实现了异常处理。但是可能支持旧代码,它们保持错误处理并提供了一种使错误处理看起来像异常处理的方法。除此之外,无法保证某些代码可能无法重置错误处理程序,这正是异常处理所要提供的。

最终答案

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

答案 3 :(得分:8)

这里要添加的一件事是处理异常和错误。出于应用程序开发人员的目的,错误和异常都是您要记录的“坏事”,以了解您的应用程序所遇到的问题 - 从而使您的客户从长远来看具有更好的体验。

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

答案 4 :(得分:6)

如其他答案中所述,将异常thrower的错误处理程序设置为处理PHP错误的最佳方法。我使用了一点简单的设置:

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

请注意error_reporting()检查以确保@运营商正常工作。此外,没有必要定义自定义异常,PHP有一个很好的类。

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

答案 5 :(得分:4)

Re:“但错误和异常之间到底有什么区别?”

这里的差异有很多好的答案。我只会添加一些尚未谈到的内容 - 性能。具体来说,这是针对抛出/处理异常和处理返回代码(成功或某些错误)之间的区别。通常,在php中,这意味着返回falsenull,但它们可以更详细,例如文件上传:http://php.net/manual/en/features.file-upload.errors.php您甚至可以返回Exception对象!

我已经在不同的语言/系统中完成了一些性能运行。一般来说,异常处理比检查错误返回代码慢大约10,000倍。

所以,如果它绝对正确,需要在它开始之前完成执行 - 好吧,你运气不好因为时间旅行不存在。没有时间旅行,返回代码是最快的选择。

修改

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

答案 6 :(得分:4)

我认为你正在寻找的是那个;

错误是你习惯的标准内容,比如回显一个不存在的$变量 例外情况仅从PHP 5开始,在处理对象时才会出现。

为了简单起见:

例外是处理对象时遇到的错误。 try / catch语句允许您对它们执行某些操作,并且与if / else语句非常相似。 尝试这样做,如果有问题,无所谓,做到这一点。

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

错误是php的基本错误,通常会停止你的脚本。

Try / catch通常用于建立像PDO这样的数据库连接,如果要重定向脚本或者在连接不起作用时执行其他操作,这很好。但是,如果您只是想显示错误消息并停止脚本然后您不需要它,未捕获的异常将变成致命错误。或者您也可以使用站点范围的错误处理设置。

希望有所帮助

答案 7 :(得分:1)

使用throw,错误的代码故意抛出异常......不是那么多。

错误是由于通常不处理的事情而产生的。 (IO错误,TCP / IP错误,空引用错误)

答案 8 :(得分:1)

我打算给你一个关于错误控制的最不寻常的讨论。

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

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

历史上,错误更老,更简单,异常更新,更复杂,更有能力。错误工作正常,直到你需要冒泡它们,这相当于将一个棘手的问题交给你的主管。

错误可以是数字,例如错误号,有时也可以是一个或多个相关字符串。例如,如果发生文件读取错误,您可能能够报告它是什么并且可能正常失败。 (干草,这就像过去那样崩溃了。)

关于异常的常见说法是异常是在特殊异常堆栈上分层的对象。它就像一个程序流的返回堆栈,但它只保留了错误trys和catch的返回状态。 (我曾经称他们为ePush和ePop,并且?Abort是一个有条件的投掷,它会ePop并恢复到那个级别,而Abort是一个完整的死亡或退出。)

在堆栈的底部是有关初始调用者的信息,该对象知道外部尝试启动时的状态,这通常是在程序启动时。最重要的是,或者堆栈中的下一层,作为子节点,而下面是父节点,是下一个内部try / catch块的异常对象。

如果您试一试,那么您将在外部尝试的顶部堆叠内部尝试。当内部try中发生错误并且内部catch无法处理它或将错误抛出到外部try时,则将控制权传递给外部catch块(对象)以查看它是否可以处理错误,即你的主管。

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

所以try / catch块排序设置一个状态,如果其他所有东西搞砸了就能返回。这就像父母一样。当我们的生活搞砸了,我们可以回到父母的膝盖上,他们会再次做好准备。

希望我没有让你失望。

答案 9 :(得分:1)

您可以添加此评论

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

答案 10 :(得分:0)

一旦定义了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
?>

答案 11 :(得分:0)

  

在PHP 7.1和更高版本中, catch 块可以指定多个异常   使用竖线(|)字符。这对于不同的时候很有用   来自不同类层次结构的异常的处理方式相同。

y + ((x-y)&1)