PHP:异常与错误?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/841500/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 00:03:06  来源:igfitidea点击:

PHP: exceptions vs errors?

phpexceptionexception-handlingerror-handling

提问by Jason Baker

Maybe I'm missing it somewhere in the PHP manual, but what exactly is the difference between an error and an exception? The only difference that I can see is that errors and exceptions are handled differently. But what causes an exception and what causes an error?

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

采纳答案by gnarf

Exceptions are thrown- they are intended to be caught. Errors are generally unrecoverable. Lets say for instance - you have a block of code that will insert a row into a database. It is possible that this call fails (duplicate ID) - you will want to have a "Error" which in this case is an "Exception". When you are inserting these rows, you can do something like this

异常抛出-它们的目的是捕获。错误通常是不可恢复的。比方说 - 您有一个代码块,可以将一行插入到数据库中。此调用可能会失败(重复 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";

Program execution will continue - because you 'caught' the exception. An exception will be treated as an error unless it is caught. It will allow you to continue program execution after it fails as well.

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

回答by Kris

I usually set_error_handlerto a function that takes the error and throws an exception so that whatever happens i'll just have exceptions to deal with. No more @file_get_contentsjust nice and neat try/catch.

我通常set_error_handler使用一个接受错误并抛出异常的函数,这样无论发生什么,我都会有异常来处理。不再@file_get_contents只是漂亮整洁的 try/catch。

In debug situations i also have an exception handler that outputs an asp.net like page. I'm posting this on the road but if requested I will post example source later.

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

edit:

编辑:

Addition as promised, I've cut and pasted some of my code together to make a sample. I've saved the below to file on my workstation, you can NO LONGERsee the results here(because the link is broken).

此外,我已经将我的一些代码剪切并粘贴在一起以制作示例。我一直保存在下面的文件我的工作站上,可以不再看到结果这里(因为链路断开)。

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

?>

回答by Arturo Hernandez

The answer deserves talking about the elephant in the room

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

Errors is the old way of handling an error condition at run-time. Typically the code would make a call to something like set_error_handlerbefore executing some code. Following the tradition of assembly language interrupts. Here is how some BASIC code would look.

错误是在运行时处理错误情况的旧方法。通常,代码会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"

It was hard to make sure that set_error_handlerwould be called with the right value. And even worse, a call could be made to a separate procedure that would change the error handler. Plus many times calls were interspersed with set_error_handlercalls and handlers. It was easy for code to quickly get out of control. Exception handling came to the rescue by formalizing syntax and semantics of what good code was really doing.

很难确保set_error_handler以正确的值调用它。更糟糕的是,可以调用一个单独的过程来更改错误处理程序。加上很多时候,调用中穿插着set_error_handler调用和处理程序。代码很容易很快失控。异常处理通过将好的代码真正在做什么的语法和语义形式化来解决问题。

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

No separate function or risk of calling the wrong error handler. The code now is guaranteed to be in the same place. Plus we get better error messages.

没有单独的函数或调用错误错误处理程序的风险。现在保证代码在同一个地方。此外,我们会收到更好的错误消息。

PHP used to only have error handling, when many other languages already had evolved to the preferable exception handling model. Eventually the makers of PHP implemented exception handling. But likely to support old code, they kept error handling and provided a way to make error handling look like exception handling. Except that, there is no guarantee that some code may not reset the error handler which was precisely what exception handling was meant to provide.

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

Final answer

最终答案

Errors that were coded before exception handling was implemented, are likely still errors. New errors are likely exceptions. But there is no design or logic to which are errors and which are exceptions. It's just based in what was available at the time it was coded, and the preference of the programmer coding it.

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

回答by Alex Weinstein

One thing to add here is about handling exceptions and errors. For the purpose of the application developer, both errors and exceptions are "bad things" that you want to record to learn about the problems that your application has - so that your customers have a better experience in the long run.

这里要添加的一件事是处理异常和错误。对于应用程序开发人员而言,错误和异常都是“坏事”,您希望记录它们以了解您的应用程序存在的问题——以便您的客户从长远来看有更好的体验。

So it makes sense to write an error handler that does the same thing as what you do for exceptions.

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

回答by Josef Kufner

As stated in other answers, setting error handler to exception thrower is the best way to handle errors in PHP. I use a bit simpler setup:

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

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

Please note the error_reporting()check to keep @operator working. Also, there is no need to define custom exception, PHP has one nice class for that.

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

Great benefit of throwing exceptions is that exception has stack trace associated with them, so it is easy to find where is the problem.

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

回答by evan

Re: "but what exactly is the difference between an error and an exception?"

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

There are a lot of good answers about the differences here. I'll just add in something that hasn't yet been talked about - performance. Specifically, this is for the difference between throwing/handling exceptions and handling a return code (either success or some error). Usually, in php, this means returning falseor null, but they can be more detailed such as with file uploading: http://php.net/manual/en/features.file-upload.errors.phpYou could even return an Exception object!

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

I've done a few performance runs in different languages/systems. Generally speaking, exception handling is about 10,000x slower than checking for an error return code.

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

So, if it absolutely, positively needs to finish executing before it even started - well, you're out of luck because time travel doesn't exist. Without time travel, return codes are the fastest option available.

所以,如果它绝对、肯定需要在它开始之前完成执行——好吧,你运气不好,因为时间旅行不存在。如果没有时间旅行,返回代码是可用的最快选项。

Edit:

编辑:

PHP is highly optimized for exception handling. Real world tests show that throwing an exception is only 2-10x slower than returning a value.

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

回答by Lan

I think the anwser you're looking for is that;

我认为您正在寻找的答案是;

Errors are the standard stuff you're used to, like echoing a $variable that doesnt exist.
Exceptions are only from PHP 5 onwards and come when dealing with objects.

错误是您习惯的标准内容,例如回显不存在的 $variable。
异常仅从 PHP 5 开始出现,并且在处理对象时出现。

To keep it simple:

为简单起见:

Exceptions are the errors you get when dealing with objects. The try/catch statement lets you do something about them though, and is used much like the if/else statement. Try to do this, if problem, doesnt matter, do this.

异常是您在处理对象时遇到的错误。不过,try/catch 语句可以让您对它们做一些事情,它的使用方式与 if/else 语句非常相似。尝试这样做,如果问题无关紧要,请执行此操作。

If you dont "catch" an exception, then it turns into a standard error.

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

Errors are the php fundemental errors which usually halt your script.

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

Try/catch is often used for establishing database connections like PDO, which is fine if you want to redirect the script or do something else if the connection doesnt work. But if you just want to display the error message and stop the script then you dont need it, the uncaught exception turns into a fatal error. Or you can use a site-wide error handling setting as well.

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

Hope that helps

希望有帮助

回答by Elliptical view

I intend to give you a most unusual discussion of error control.

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

I built a very good error handler into a language years ago, and though some of the names have changed, the principles of error processing are the same today. I had a custom built multi-tasking OS and had to be able to recover from data errors at all levels with no memory leaks, stack growth or crashes. So what follows is my understanding of how errors and exceptions must operate and how they differ. I will just say I do not have an understanding of how the internals of try catch works, so am guessing to some measure.

几年前我在一种语言中构建了一个非常好的错误处理程序,虽然一些名称已经改变,但今天错误处理的原则是相同的。我有一个定制的多任务操作系统,并且必须能够从所有级别的数据错误中恢复,而不会出现内存泄漏、堆栈增长或崩溃。所以接下来是我对错误和异常必须如何运作以及它们有何不同的理解。我只想说我不了解 try catch 的内部原理是如何工作的,所以我在某种程度上猜测。

The first thing that happens under the covers for error processing is jumping from one program state to another. How is that done? I'll get to that.

错误处理背后发生的第一件事是从一个程序状态跳转到另一个程序状态。这是怎么做的?我会做到这一点。

Historically, errors are older and simpler, and exceptions are newer and a bit more complex and capable. Errors work fine until you need to bubble them up, which is the equivalent of handing a difficult problem to your supervisor.

从历史上看,错误更老更简单,异常更新,更复杂和更强大。错误工作正常,直到您需要将它们冒泡出来,这相当于将难题交给您的主管。

Errors can be numbers, like error numbers, and sometimes with one or more associated strings. For example if a file-read error occurs you might be able to report what it is and possibly gracefully fail. (Hay, it's a step up from just crashing like in the old days.)

错误可以是数字,如错误编号,有时还带有一个或多个相关联的字符串。例如,如果发生文件读取错误,您可能能够报告它是什么并且可能正常失败。(嘿,这比过去的崩溃更上一层楼了。)

What is not often said about exceptions is that exceptions are objects layered on a special exception stack. It's like a return stack for program flow, but it holds a return state just for error trys and catches. (I used to call them ePush and ePop, and ?Abort was a conditional throw which would ePop and recover to that level, while Abort was a full die or exit.)

不常说的异常是异常是分层在特殊异常堆栈上的对象。它就像程序流的返回堆栈,但它只为错误尝试和捕获保存返回状态。(我曾经称它们为 ePush 和 ePop,而 ?Abort 是一个有条件的抛出,它将 ePop 并恢复到那个水平,而 Abort 是一个完整的死亡或退出。)

On the bottom of the stack is the information about the initial caller, the object that knows about the state when the outer try was started, which is often when your program was started. On top that, or the next layer on the stack, with up being the children, and down being the parents, is the exception object of the next inner try/catch block.

堆栈底部是有关初始调用者的信息,该对象知道外部 try 开始时的状态,通常是您的程序启动时。在最上面,或者堆栈上的下一层,上层是孩子,下层是父母,是下一个内部 try/catch 块的异常对象。

If you put a try inside a try you are stacking the inner try on top of the outer try. When an error occurs in the inner try and either the inner catch can't handle it or the error is thrown to the outer try, then control is passed to the outer catch block (object) to see if it can handle the error, i.e. your supervisor.

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

So what this error stack really does is to be able to mark and restore program flow and system state, in other words, it allows a program to not crash the return stack and mess up things for others (data) when things go wrong. So it also saves the state of any other resources like memory allocation pools and so it can clean them up when catch is done. In general this can be a very complicated thing, and that is why exception handling is often slow. In general quite a bit of state needs to go into these exception blocks.

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

So a try/catch block sort of sets a state to be able to return to if all else gets messed up. It's like a parent. When our lives get messed up we can fall back into our parent's lap and they will make it all right again.

因此,try/catch 块设置了一个状态,以便在其他所有事情都搞砸时能够返回。这就像父母一样。当我们的生活一团糟时,我们可以回到父母的怀抱,他们会让一切重新好起来。

Hope I didn't disappoint you.

希望我没有让你失望。

回答by cgp

Exceptions are thrown intentionally by code using a throw, errors... not so much.

异常是由使用抛出、错误的代码有意抛出的......不是那么多。

Errors come about as a result of something which isn't handled typically. (IO errors, TCP/IP errors, null reference errors)

错误是由于一些通常不处理的事情引起的。(IO 错误、TCP/IP 错误、空引用错误)

回答by Jehong Ahn

In PHP 7.1 and later, a catchblock may specify multiple exceptions using the pipe (|) character. This is useful for when different exceptions from different class hierarchies are handled the same.

在 PHP 7.1 及更高版本中,catch块可以使用管道 (|) 字符指定多个异常。当来自不同类层次结构的不同异常被相同处理时,这很有用。

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