Làm cách nào để tôi bắt lỗi PHP nghiêm trọng (`E_ERROR`)?


557

Tôi có thể sử dụng set_error_handler()để bắt hầu hết các lỗi PHP, nhưng nó không hoạt động đối với các E_ERRORlỗi nghiêm trọng ( ), chẳng hạn như gọi một hàm không tồn tại. Có cách nào khác để bắt những lỗi này không?

Tôi đang cố gắng gọi mail()tất cả các lỗi và đang chạy PHP 5.2.3.


Tôi đã viết lên một câu hỏi và trả lời theo kiểu wiki với một giải pháp hoàn chỉnh để nắm bắt tất cả các lỗi trong PHP; có thể được xem / lượm lặt / đánh cắp / phê bình ở đây trên Stack Overflow . Giải pháp này bao gồm năm phương thức bao gồm tất cả các lỗi mà PHP có thể tạo ra, cuối cùng sẽ chuyển các lỗi đã nói đến một đối tượng được gõ 'ErrorHandler'.
DigitalJedi805



Câu trả lời:


635

Đăng nhập các lỗi nghiêm trọng bằng cách sử dụng register_shutdown_function, yêu cầu PHP 5.2+:

register_shutdown_function( "fatal_handler" );

function fatal_handler() {
    $errfile = "unknown file";
    $errstr  = "shutdown";
    $errno   = E_CORE_ERROR;
    $errline = 0;

    $error = error_get_last();

    if($error !== NULL) {
        $errno   = $error["type"];
        $errfile = $error["file"];
        $errline = $error["line"];
        $errstr  = $error["message"];

        error_mail(format_error( $errno, $errstr, $errfile, $errline));
    }
}

Bạn sẽ phải xác định error_mailvà các format_errorchức năng. Ví dụ:

function format_error( $errno, $errstr, $errfile, $errline ) {
    $trace = print_r( debug_backtrace( false ), true );

    $content = "
    <table>
        <thead><th>Item</th><th>Description</th></thead>
        <tbody>
            <tr>
                <th>Error</th>
                <td><pre>$errstr</pre></td>
            </tr>
            <tr>
                <th>Errno</th>
                <td><pre>$errno</pre></td>
            </tr>
            <tr>
                <th>File</th>
                <td>$errfile</td>
            </tr>
            <tr>
                <th>Line</th>
                <td>$errline</td>
            </tr>
            <tr>
                <th>Trace</th>
                <td><pre>$trace</pre></td>
            </tr>
        </tbody>
    </table>";
    return $content;
}

Sử dụng Swift Mailer để viết error_mailchức năng.

Xem thêm:


113
+1 Đây là câu trả lời đúng thực tế . Tôi không biết tại sao mọi người lại nôn nao vì "bạn không thể phục hồi sau những lỗi nghiêm trọng" - câu hỏi không nói lên điều gì về việc phục hồi.
David Harkness

21
Cảm ơn, một trong những tốt. Phục hồi từ các lỗi nghiêm trọng (ví dụ như giới hạn bộ nhớ) không phải là điều tôi sẽ cố gắng thực hiện, nhưng làm cho các lỗi này có thể phát hiện được (không có khách hàng gửi vé hỗ trợ) sẽ tạo ra sự khác biệt.
Ilija

2
Sử dụng thư cơ bản:mail("myname@myemail.com", "My Site: FATAL ERROR", "Details: " . $errno . ' ' . $errstr . ' ' . $errfile . ' ' . $errline);
Eric Muyser

4
@ScottNicol Slava V là chính xác, bởi vì chức năng tắt máy được gọi mỗi khi tập lệnh kết thúc chạy. Với cách viết mã bây giờ, một email sẽ được gửi khi MỌI trang tải.
Nate

2
Lưu ý: đây không phải là câu trả lời đúng 100%. Bất kỳ nơi nào sử dụng ký hiệu @ để bỏ qua lỗi vẫn sẽ đặt lỗi cuối cùng (để bạn có thể xử lý lỗi). Vì vậy, tập lệnh của bạn kết thúc mà không có vấn đề gì, nhưng register_shutdown_function vẫn cho rằng đã xảy ra lỗi. Chỉ kể từ PHP 7, mặc dù chúng có hàm error_clear_last ().
Rahly

150

Tôi vừa đưa ra giải pháp này (PHP 5.2.0+):

function shutDownFunction() {
    $error = error_get_last();
     // Fatal error, E_ERROR === 1
    if ($error['type'] === E_ERROR) {
         // Do your stuff
    }
}
register_shutdown_function('shutDownFunction');

Các loại lỗi khác nhau được xác định tại Hằng số được xác định trước .


25
Giải pháp này làm nhiều hơn cho tôi hơn câu trả lời đánh giá hàng đầu. Câu trả lời được xếp hạng cao nhất sẽ gửi cho bạn email mỗi khi tập lệnh chạy, ngay cả khi không có lỗi. Điều này nghiêm ngặt chạy trên một lỗi nghiêm trọng.
kmoney12

@periklis, nếu lỗi cuối cùng đã được xử lý, error_get_last vẫn sẽ trả lại phải không?
Pacerier

@Pacerier Tôi không chắc ý của bạn là "bị xử lý", vì lỗi không phải là ngoại lệ, nhưng tôi cho rằng câu trả lời là "có"
periklis

3
@Pacerier Tôi thấy, đó là một câu hỏi thú vị. Hãy xem php.net/error_get_last , một trong những ý kiến ​​đề cập rằng " If an error handler (see set_error_handler ) successfully handles an error then that error will not be reported by this function."
periklis

1
Có lẽ điều này là hiển nhiên, gọi register_shutdown_function()phải sớm hơn bất kỳ lỗi nghiêm trọng nào. use_1T_memory(); /* memory exhausted error here! */ register_shutdown_function('shutDownFunction');sẽ không làm việc như mong đợi.
Nobu

117

PHP không cung cấp các phương tiện thông thường để bắt và phục hồi từ các lỗi nghiêm trọng. Điều này là do quá trình xử lý thường không được phục hồi sau một lỗi nghiêm trọng. Chuỗi phù hợp với bộ đệm đầu ra (như được đề xuất bởi bài viết gốc, kỹ thuật được mô tả trên PHP.net) chắc chắn không được khuyến khích. Nó đơn giản là không đáng tin cậy.

Gọi hàm mail () từ bên trong một phương thức xử lý lỗi cũng chứng tỏ là có vấn đề. Nếu bạn có nhiều lỗi, máy chủ thư của bạn sẽ được tải với công việc và bạn có thể thấy mình có hộp thư đến sởn gai ốc. Để tránh điều này, bạn có thể cân nhắc chạy cron để quét nhật ký lỗi theo định kỳ và gửi thông báo phù hợp. Bạn cũng có thể muốn xem xét phần mềm giám sát hệ thống, chẳng hạn như Nagios .


Để nói chuyện một chút về việc đăng ký chức năng tắt máy:

Đúng là bạn có thể đăng ký một chức năng tắt máy, và đó là một câu trả lời tốt.

Vấn đề ở đây là chúng ta thường không nên cố gắng phục hồi từ các lỗi nghiêm trọng, đặc biệt là không sử dụng biểu thức chính quy đối với bộ đệm đầu ra của bạn. Tôi đã trả lời câu trả lời được chấp nhận , liên kết đến một đề xuất trên php.net đã bị thay đổi hoặc bị xóa.

Đề xuất đó là sử dụng regex đối với bộ đệm đầu ra trong quá trình xử lý ngoại lệ và trong trường hợp có lỗi nghiêm trọng (được phát hiện bởi kết quả khớp với bất kỳ văn bản lỗi nào được cấu hình mà bạn có thể mong đợi), hãy thử thực hiện một số loại phục hồi hoặc tiếp tục xử lý. Đó sẽ không phải là một thực tiễn được đề xuất (tôi tin rằng đó là lý do tại sao tôi không thể tìm thấy đề xuất ban đầu. Tôi cũng đang xem nó hoặc cộng đồng php đã bắn hạ nó).

Có thể đáng chú ý rằng các phiên bản gần đây hơn của PHP (khoảng 5.1) dường như gọi chức năng tắt máy trước đó, trước khi gọi lại bộ đệm đầu ra được kích hoạt. Trong phiên bản 5 trở về trước, thứ tự đó là ngược lại (cuộc gọi lại bộ đệm đầu ra được theo sau bởi chức năng tắt máy). Ngoài ra, vì khoảng 5.0.5 (sớm hơn nhiều so với phiên bản 5.2.3 của người hỏi), các đối tượng được dỡ tải tốt trước khi gọi chức năng tắt máy đã đăng ký, do đó bạn sẽ không thể dựa vào các đối tượng trong bộ nhớ để làm nhiều thứ

Vì vậy, đăng ký một chức năng tắt máy là tốt, nhưng loại nhiệm vụ phải được thực hiện bởi chức năng tắt máy có lẽ bị giới hạn trong một số ít các thủ tục tắt máy nhẹ nhàng.

Chìa khóa cất cánh ở đây chỉ là một số từ ngữ khôn ngoan cho bất kỳ ai vấp phải câu hỏi này và nhìn thấy lời khuyên trong câu trả lời được chấp nhận ban đầu. Đừng regex bộ đệm đầu ra của bạn.


25
Pfff, tôi nhớ hơn 650.000 email mà tôi đã nhận được vào sáng hôm sau. Kể từ đó ErrorHandler của tôi được giới hạn ở mức 100 email trên mỗi máy chủ web.
Bob Fanger

14
Đo không phải sự thật. Bạn có thể ghi lại các lỗi nghiêm trọng với register_shutdown_feft.
hipertracker

56
Có trường hợp sử dụng tồn tại vì muốn bắt lỗi nghiêm trọng. Ví dụ, các bộ thử nghiệm không nên dừng lại khi một lỗi, họ nên báo cáo lỗi nghiêm trọng và chuyển sang thử nghiệm tiếp theo. PHP chỉ tạo ra quá nhiều lỗi "nghiêm trọng".
Chad

24
Vâng nói rằng họ "không nên bị bắt" là rất thiển cận. Trong một hệ thống sản xuất, bạn cần biết khi nào có lỗi (thiết lập email hoặc ghi nhật ký mọi thứ trong cơ sở dữ liệu - việc xử lý lỗi php mặc định không tinh vi lắm).
BT

8
Tôi muốn bình luận nhanh về những gì bạn đang nói về "Lỗi cần phải bắt, để chúng tôi có thể sửa chúng" ... Ini chỉ thị ini log_errors và error_log.
Kelly Elton

37

Chà, có vẻ như có thể bắt lỗi nghiêm trọng theo cách khác :)

ob_start('fatal_error_handler');

function fatal_error_handler($buffer){
    $error = error_get_last();
    if($error['type'] == 1){
        // Type, message, file, line
        $newBuffer='<html><header><title>Fatal Error </title></header>
                      <style>
                    .error_content{
                        background: ghostwhite;
                        vertical-align: middle;
                        margin:0 auto;
                        padding: 10px;
                        width: 50%;
                     }
                     .error_content label{color: red;font-family: Georgia;font-size: 16pt;font-style: italic;}
                     .error_content ul li{ background: none repeat scroll 0 0 FloralWhite;
                                border: 1px solid AliceBlue;
                                display: block;
                                font-family: monospace;
                                padding: 2%;
                                text-align: left;
                      }
                      </style>
                      <body style="text-align: center;">
                        <div class="error_content">
                             <label >Fatal Error </label>
                             <ul>
                               <li><b>Line</b> ' . $error['line'] . '</li>
                               <li><b>Message</b> ' . $error['message'] . '</li>
                               <li><b>File</b> ' . $error['file'] . '</li>
                             </ul>

                             <a href="javascript:history.back()"> Back </a>
                        </div>
                      </body></html>';

        return $newBuffer;
    }
    return $buffer;
}

3
Tôi sẽ cung cấp cho 10 upvote này nếu tôi có thể. Nó hoạt động hoàn hảo với tôi về những lỗi kỳ lạ đôi khi xảy ra khi một trang bị đánh bom và không có gì được ghi lại. Tôi sẽ không sử dụng mã sản xuất trực tiếp nhưng thật tuyệt khi thêm vào trang khi cần trả lời nhanh cho những gì không thành công. Cảm ơn bạn!
Cú đêm

Một trong những giải pháp tốt nhất tôi tìm thấy trên Internet. Hoạt động như quyến rũ.
Trả lại

Bằng cách nào? Một lời giải thích sẽ theo thứ tự, đặc biệt nếu đó là một trong những giải pháp tốt nhất trên Internet (nó có thể trở nên tốt hơn nữa).
Peter Mortensen

Là ví dụ tất cả các nội dung CSS cần thiết? Nó không thể được cắt giảm đến mức cần thiết? Trả lời bằng cách chỉnh sửa câu trả lời của bạn, không phải ở đây trong các bình luận (nếu thích hợp).
Peter Mortensen

@PeterMortensen Tôi không yêu cầu tốt nhất của nó. Ngoài ra giải pháp cá nhân của tôi cho vấn đề, có những lựa chọn tốt hơn nhiều chuyên nghiệp. Theo đề xuất của ai đó, nó không phù hợp để sản xuất. Css là có bcz Tôi vừa cắt mã cá nhân của tôi
sakhunzai

36

Lỗi gây tử vong hoặc gây tử vong lỗi thu hồi tại ném các trường hợp Errortrong PHP 7 hoặc phiên bản cao hơn . Giống như bất kỳ trường hợp ngoại lệ khác, Errorcác đối tượng có thể được bắt gặp bằng cách sử dụng một try/catchkhối.

Thí dụ:

<?php
$variable = 'not an object';

try {
    $variable->method(); // Throws an Error object in PHP 7 or higger.
} catch (Error $e) {
    // Handle error
    echo $e->getMessage(); // Call to a member function method() on string
}

https://3v4l.org/67vbk

Hoặc bạn có thể sử dụng Throwablegiao diện để bắt tất cả các ngoại lệ.

Thí dụ:

<?php
    try {
        undefinedFunctionCall();
    } catch (Throwable $e) {
        // Handle error
        echo $e->getMessage(); // Call to undefined function undefinedFunctionCall()
    }

https://3v4l.org/Br0MG

Để biết thêm thông tin: http://php.net/manual/en/lingu.errors.php7.php


2
Bất kỳ ideia về cách sử dụng này để bắt lỗi như Fatal error: Trait 'FailedTrait' not found inkhi sử dụng ReflectionClass?
TCB13

1
@ TCB13 cố gắng bọc nội dung thử bên trong một tệp và include "filename.php"thay vào đó trong trykhối, sau đó Throwablebắt khối ít nhất hoạt động ParseError.
Niloct

24

Tôi đã phát triển một cách để bắt tất cả các loại lỗi trong PHP (gần như tất cả)! Tôi không chắc chắn về E_CORE_ERROR (Tôi nghĩ rằng sẽ không hoạt động chỉ với lỗi đó)! Nhưng, đối với các lỗi nghiêm trọng khác (E_ERROR, E_PARSE, E_COMPILE ...) hoạt động tốt chỉ bằng một chức năng xử lý lỗi! Có giải pháp của tôi:

Đặt mã sau đây vào tệp chính của bạn (index.php):

<?php
    define('E_FATAL',  E_ERROR | E_USER_ERROR | E_PARSE | E_CORE_ERROR |
            E_COMPILE_ERROR | E_RECOVERABLE_ERROR);

    define('ENV', 'dev');

    // Custom error handling vars
    define('DISPLAY_ERRORS', TRUE);
    define('ERROR_REPORTING', E_ALL | E_STRICT);
    define('LOG_ERRORS', TRUE);

    register_shutdown_function('shut');

    set_error_handler('handler');

    // Function to catch no user error handler function errors...
    function shut(){

        $error = error_get_last();

        if($error && ($error['type'] & E_FATAL)){
            handler($error['type'], $error['message'], $error['file'], $error['line']);
        }

    }

    function handler( $errno, $errstr, $errfile, $errline ) {

        switch ($errno){

            case E_ERROR: // 1 //
                $typestr = 'E_ERROR'; break;
            case E_WARNING: // 2 //
                $typestr = 'E_WARNING'; break;
            case E_PARSE: // 4 //
                $typestr = 'E_PARSE'; break;
            case E_NOTICE: // 8 //
                $typestr = 'E_NOTICE'; break;
            case E_CORE_ERROR: // 16 //
                $typestr = 'E_CORE_ERROR'; break;
            case E_CORE_WARNING: // 32 //
                $typestr = 'E_CORE_WARNING'; break;
            case E_COMPILE_ERROR: // 64 //
                $typestr = 'E_COMPILE_ERROR'; break;
            case E_CORE_WARNING: // 128 //
                $typestr = 'E_COMPILE_WARNING'; break;
            case E_USER_ERROR: // 256 //
                $typestr = 'E_USER_ERROR'; break;
            case E_USER_WARNING: // 512 //
                $typestr = 'E_USER_WARNING'; break;
            case E_USER_NOTICE: // 1024 //
                $typestr = 'E_USER_NOTICE'; break;
            case E_STRICT: // 2048 //
                $typestr = 'E_STRICT'; break;
            case E_RECOVERABLE_ERROR: // 4096 //
                $typestr = 'E_RECOVERABLE_ERROR'; break;
            case E_DEPRECATED: // 8192 //
                $typestr = 'E_DEPRECATED'; break;
            case E_USER_DEPRECATED: // 16384 //
                $typestr = 'E_USER_DEPRECATED'; break;
        }

        $message =
            '<b>' . $typestr .
            ': </b>' . $errstr .
            ' in <b>' . $errfile .
            '</b> on line <b>' . $errline .
            '</b><br/>';

        if(($errno & E_FATAL) && ENV === 'production'){

            header('Location: 500.html');
            header('Status: 500 Internal Server Error');

        }

        if(!($errno & ERROR_REPORTING))
            return;

        if(DISPLAY_ERRORS)
            printf('%s', $message);

        //Logging error on php file error log...
        if(LOG_ERRORS)
            error_log(strip_tags($message), 0);
    }

    ob_start();

    @include 'content.php';

    ob_end_flush();
?>

2
Dòng @include 'content.php' làm gì?
Marco

22

Bạn không thể bắt / xử lý các lỗi nghiêm trọng, nhưng bạn có thể đăng nhập / báo cáo chúng. Để gỡ lỗi nhanh, tôi đã sửa đổi một câu trả lời cho mã đơn giản này

function __fatalHandler()
{
    $error = error_get_last();

    // Check if it's a core/fatal error, otherwise it's a normal shutdown
    if ($error !== NULL && in_array($error['type'],
        array(E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING,
              E_COMPILE_ERROR, E_COMPILE_WARNING,E_RECOVERABLE_ERROR))) {

        echo "<pre>fatal error:\n";
        print_r($error);
        echo "</pre>";
        die;
    }
}

register_shutdown_function('__fatalHandler');

Nhưng mã này sẽ đi đâu?
TKoL

@TKoL dòng đầu tiên. Về cơ bản tệp nhập của tập lệnh / chương trình của bạn, vì vậy nó sẽ thực thi trước, nếu không thể đặt nó vào một tệp chung
zainengineer

17

Bạn không thể ném ngoại lệ bên trong chức năng tắt đã đăng ký như thế:

<?php
    function shutdown() {
        if (($error = error_get_last())) {
           ob_clean();
           throw new Exception("fatal error");
        }
    }

    try {
        $x = null;
        $x->method()
    } catch(Exception $e) {
        # This won't work
    }
?>

Nhưng bạn có thể nắm bắt và chuyển hướng yêu cầu đến một trang khác.

<?php
    function shutdown() {
        if (($error = error_get_last())) {
           ob_clean();
           # Report the event, send email, etc.
           header("Location: http://localhost/error-capture");
           # From /error-capture. You can use another
           # redirect, to e.g. the home page
        }
    }
    register_shutdown_function('shutdown');

    $x = null;
    $x->method()
?>

11

Nếu bạn đang sử dụng PHP> = 5.1.0 Chỉ cần làm một cái gì đó như thế này với lớp ErrorException:

<?php
    // Define an error handler
    function exception_error_handler($errno, $errstr, $errfile, $errline ) {
        throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
    }

    // Set your error handler
    set_error_handler("exception_error_handler");

    /* Trigger exception */
    try
    {
        // Try to do something like finding the end of the internet
    }
    catch(ErrorException $e)
    {
        // Anything you want to do with $e
    }
?>

9

Giải pháp tuyệt vời được tìm thấy trong Zend Framework 2:

/**
 * ErrorHandler that can be used to catch internal PHP errors
 * and convert to an ErrorException instance.
 */
abstract class ErrorHandler
{
    /**
     * Active stack
     *
     * @var array
     */
    protected static $stack = array();

    /**
     * Check if this error handler is active
     *
     * @return bool
     */
    public static function started()
    {
        return (bool) static::getNestedLevel();
    }

    /**
     * Get the current nested level
     *
     * @return int
     */
    public static function getNestedLevel()
    {
        return count(static::$stack);
    }

    /**
     * Starting the error handler
     *
     * @param int $errorLevel
     */
    public static function start($errorLevel = \E_WARNING)
    {
        if (!static::$stack) {
            set_error_handler(array(get_called_class(), 'addError'), $errorLevel);
        }

        static::$stack[] = null;
    }

    /**
     * Stopping the error handler
     *
     * @param  bool $throw Throw the ErrorException if any
     * @return null|ErrorException
     * @throws ErrorException If an error has been catched and $throw is true
     */
    public static function stop($throw = false)
    {
        $errorException = null;

        if (static::$stack) {
            $errorException = array_pop(static::$stack);

            if (!static::$stack) {
                restore_error_handler();
            }

            if ($errorException && $throw) {
                throw $errorException;
            }
        }

        return $errorException;
    }

    /**
     * Stop all active handler
     *
     * @return void
     */
    public static function clean()
    {
        if (static::$stack) {
            restore_error_handler();
        }

        static::$stack = array();
    }

    /**
     * Add an error to the stack
     *
     * @param int    $errno
     * @param string $errstr
     * @param string $errfile
     * @param int    $errline
     * @return void
     */
    public static function addError($errno, $errstr = '', $errfile = '', $errline = 0)
    {
        $stack = & static::$stack[count(static::$stack) - 1];
        $stack = new ErrorException($errstr, 0, $errno, $errfile, $errline, $stack);
    }
}

Lớp này cho phép bạn bắt đầu cụ thể ErrorHandlerđôi khi nếu bạn cần nó. Và sau đó bạn cũng có thể dừng Handler.

Sử dụng lớp này, ví dụ như thế này:

ErrorHandler::start(E_WARNING);
$return = call_function_raises_E_WARNING();

if ($innerException = ErrorHandler::stop()) {
    throw new Exception('Special Exception Text', 0, $innerException);
}

// or
ErrorHandler::stop(true); // directly throws an Exception;

Liên kết đến mã lớp đầy đủ:
https://github.com/zendframework/zf2/blob/master/l Library / Zend / Durlib / ErrorHandler.php


Một giải pháp có thể tốt hơn là một từ Monolog :

Liên kết đến mã lớp đầy đủ:
https://github.com/Seldaek/monolog/blob/master/src/Monolog/ErrorHandler.php

Nó cũng có thể xử lý FATAL_ERRORS bằng cách sử dụng register_shutdown_functionchức năng. Theo lớp học này, FATAL_ERROR là một trong những điều sau đây array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR).

class ErrorHandler
{
    // [...]

    public function registerExceptionHandler($level = null, $callPrevious = true)
    {
        $prev = set_exception_handler(array($this, 'handleException'));
        $this->uncaughtExceptionLevel = $level;
        if ($callPrevious && $prev) {
            $this->previousExceptionHandler = $prev;
        }
    }

    public function registerErrorHandler(array $levelMap = array(), $callPrevious = true, $errorTypes = -1)
    {
        $prev = set_error_handler(array($this, 'handleError'), $errorTypes);
        $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap);
        if ($callPrevious) {
            $this->previousErrorHandler = $prev ?: true;
        }
    }

    public function registerFatalHandler($level = null, $reservedMemorySize = 20)
    {
        register_shutdown_function(array($this, 'handleFatalError'));

        $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize);
        $this->fatalLevel = $level;
    }

    // [...]
}

9

Tôi cần xử lý các lỗi nghiêm trọng cho sản xuất để thay vào đó hiển thị đầu ra HTML Dịch vụ 503 không có kiểu dáng tĩnh . Đây chắc chắn là một cách tiếp cận hợp lý để "bắt lỗi nghiêm trọng". Đây là những gì tôi đã làm:

Tôi có chức năng xử lý lỗi tùy chỉnh "error_handler" sẽ hiển thị trang HTML "503 dịch vụ không khả dụng" của tôi trên bất kỳ E_ERROR, E_USER_ERROR, v.v. Điều này sẽ được gọi trên chức năng tắt máy, bắt lỗi nghiêm trọng của tôi,

function fatal_error_handler() {

    if (@is_array($e = @error_get_last())) {
        $code = isset($e['type']) ? $e['type'] : 0;
        $msg = isset($e['message']) ? $e['message'] : '';
        $file = isset($e['file']) ? $e['file'] : '';
        $line = isset($e['line']) ? $e['line'] : '';
        if ($code>0)
            error_handler($code, $msg, $file, $line);
    }
}
set_error_handler("error_handler");
register_shutdown_function('fatal_error_handler');

trong hàm error_handler tùy chỉnh của tôi, nếu lỗi là E_ERROR, E_USER_ERROR, v.v. Tôi cũng gọi @ob_end_clean();để làm trống bộ đệm, do đó loại bỏ thông báo "lỗi nghiêm trọng" của PHP.

Lưu ý quan trọng về việc kiểm tra nghiêm ngặt () @ chức năng im lặng vì chúng tôi không muốn các tập lệnh error_handler của chúng tôi tạo ra bất kỳ lỗi nào.

Khi vẫn đồng ý với keparo, việc bắt lỗi nghiêm trọng không đánh bại mục đích của "lỗi FATAL" vì vậy nó không thực sự có ý định để bạn xử lý thêm. Không chạy bất kỳ chức năng mail () nào trong quy trình tắt máy này vì bạn chắc chắn sẽ sao lưu máy chủ thư hoặc hộp thư đến của mình. Thay vào đó hãy ghi lại những lần xuất hiện này vào tệp và lên lịch cho một công việc định kỳ để tìm những tệp error.log này và gửi chúng cho quản trị viên.


7

PHP có lỗi nghiêm trọng có thể bắt được. Chúng được định nghĩa là E_RECOVERABLE_ERROR. Hướng dẫn PHP mô tả E_RECOVERABLE_ERROR là:

Lỗi nghiêm trọng có thể bắt được. Nó chỉ ra rằng có thể xảy ra lỗi nguy hiểm, nhưng không để Động cơ ở trạng thái không ổn định. Nếu lỗi không được xử lý bởi người xử lý do người dùng xác định (xem thêm set_error_handler () ), ứng dụng sẽ hủy bỏ vì đó là E_ERROR.

Bạn có thể "bắt" các lỗi "nghiêm trọng" này bằng cách sử dụng set_error_handler () và kiểm tra E_RECOVERABLE_ERROR. Tôi thấy hữu ích khi ném Ngoại lệ khi lỗi này bị bắt, sau đó bạn có thể sử dụng thử / bắt.

Câu hỏi và câu trả lời này cung cấp một ví dụ hữu ích: Làm cách nào tôi có thể bắt được "lỗi nghiêm trọng có thể bắt được" trên gợi ý loại PHP?

Tuy nhiên, các lỗi E_ERROR có thể được xử lý, nhưng không được phục hồi do động cơ ở trạng thái không ổn định.


6

Đây chỉ là một mẹo hay để có được phương thức error_handler hiện tại =)

<?php
    register_shutdown_function('__fatalHandler');

    function __fatalHandler()
    {
        $error = error_get_last();

        // Check if it's a core/fatal error. Otherwise, it's a normal shutdown
        if($error !== NULL && $error['type'] === E_ERROR) {

            // It is a bit hackish, but the set_exception_handler
            // will return the old handler
            function fakeHandler() { }

            $handler = set_exception_handler('fakeHandler');
            restore_exception_handler();
            if($handler !== null) {
                call_user_func(
                    $handler,
                    new ErrorException(
                        $error['message'],
                        $error['type'],
                        0,
                        $error['file'],
                        $error['line']));
            }
            exit;
        }
    }
?>

Ngoài ra tôi muốn lưu ý rằng nếu bạn gọi

<?php
    ini_set('display_errors', false);
?>

PHP dừng hiển thị lỗi. Nếu không, văn bản lỗi sẽ được gửi đến máy khách trước khi xử lý lỗi của bạn.


1
Nâng cấp điều này vì dòng ini_set ('display_errors', false);
Sahib Khan

Nếu vì lý do nào đó, bit này vẫn bật, nó vẫn hiển thị lỗi php ngay cả khi bạn xử lý nó khác đi
Sahib Khan

5

Vì hầu hết các câu trả lời ở đây đều dài dòng một cách không cần thiết, đây là phiên bản không xấu của câu trả lời được bình chọn hàng đầu của tôi:

function errorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = array()) {
    //Do stuff: mail, log, etc
}

function fatalHandler() {
    $error = error_get_last();
    if($error) errorHandler($error["type"], $error["message"], $error["file"], $error["line"]);
}

set_error_handler("errorHandler")
register_shutdown_function("fatalHandler");

4

Không hẳn vậy. Lỗi nghiêm trọng được gọi là như vậy, bởi vì chúng gây tử vong. Bạn không thể phục hồi từ họ.


12
bắt và phục hồi là hai điều rất khác nhau
Simon Forsberg

3

Tôi đã phát triển chức năng này để làm cho mã "hộp cát" có thể gây ra lỗi nghiêm trọng. Vì các ngoại lệ được ném từ bao đóng register_shutdown_functionkhông được phát ra từ ngăn xếp cuộc gọi lỗi nghiêm trọng, tôi buộc phải thoát ra sau chức năng này để cung cấp cách sử dụng thống nhất.

function superTryCatchFinallyAndExit( Closure $try, Closure $catch = NULL, Closure $finally )
{
    $finished = FALSE;
    register_shutdown_function( function() use ( &$finished, $catch, $finally ) {
        if( ! $finished ) {
            $finished = TRUE;
            print "EXPLODE!".PHP_EOL;
            if( $catch ) {
                superTryCatchFinallyAndExit( function() use ( $catch ) {
                    $catch( new Exception( "Fatal Error!!!" ) );
                }, NULL, $finally );                
            } else {
                $finally();                
            }
        }
    } );
    try {
        $try();
    } catch( Exception $e ) {
        if( $catch ) {
            try {
                $catch( $e );
            } catch( Exception $e ) {}
        }
    }
    $finished = TRUE;
    $finally();
    exit();
}

3

Có một số trường hợp nhất định trong đó phải bắt lỗi ngay cả (bạn có thể cần phải dọn dẹp trước khi thoát ra một cách duyên dáng và đừng chết ..).

Tôi đã triển khai hook pre_system trong CodeIgniter của mình các ứng dụng để tôi có thể nhận được các lỗi nghiêm trọng của mình qua email và điều này giúp tôi tìm thấy các lỗi không được báo cáo (hoặc đã được báo cáo sau khi chúng được sửa, như tôi đã biết về chúng :)).

Sendemail kiểm tra nếu lỗi đã được báo cáo để nó không spam bạn với các lỗi đã biết nhiều lần.

class PHPFatalError {

    public function setHandler() {
        register_shutdown_function('handleShutdown');
    }
}

function handleShutdown() {
    if (($error = error_get_last())) {
        ob_start();
        echo "<pre>";
        var_dump($error);
        echo "</pre>";
        $message = ob_get_clean();
        sendEmail($message);
        ob_start();
        echo '{"status":"error","message":"Internal application error!"}';
        ob_flush();
        exit();
    }
}

Là gì "SendEmail" ? Bạn có nghĩa là Sendmail (trả lời bằng cách chỉnh sửa câu trả lời của bạn , không phải ở đây trong các bình luận)?
Peter Mortensen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.