PHP: exceptions vs errors?

2019-01-01 10:04发布

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?

10条回答
听够珍惜
2楼-- · 2019-01-01 10:48

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.

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.)

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.

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.

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.

Hope I didn't disappoint you.

查看更多
墨雨无痕
3楼-- · 2019-01-01 10:50

I usually set_error_handler to 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_contents just nice and neat 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.

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 LONGER see 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' );

?>
查看更多
素衣白纱
4楼-- · 2019-01-01 10:53

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)

查看更多
回忆,回不去的记忆
5楼-- · 2019-01-01 10:53

Once set_error_handler() is defined, error handler is similar to Exception's. See code below:

 <?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
?>
查看更多
登录 后发表回答