Bagaimana cara menangkap kesalahan PHP fatal (`E_ERROR`)?


557

Saya dapat menggunakan set_error_handler()untuk menangkap sebagian besar kesalahan PHP, tetapi tidak berfungsi untuk E_ERRORkesalahan fatal ( ), seperti memanggil fungsi yang tidak ada. Apakah ada cara lain untuk menangkap kesalahan ini?

Saya mencoba memanggil mail()semua kesalahan dan menjalankan PHP 5.2.3.


Saya menulis Q&A gaya wiki dengan solusi lengkap untuk menangkap semua kesalahan dalam PHP; yang dapat dilihat / diperoleh / dicuri / dikritik di sini di Stack Overflow . Solusi ini mencakup lima metode yang membungkus semua kesalahan yang dapat dihasilkan oleh PHP yang pada akhirnya akan meneruskan kata kesalahan tersebut ke objek yang diketik 'ErrorHandler'.
DigitalJedi805



Jawaban:


635

Catat kesalahan fatal menggunakan register_shutdown_function, yang membutuhkan 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));
    }
}

Anda harus mendefinisikan error_maildan format_errorfungsinya. Sebagai contoh:

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;
}

Gunakan Swift Mailer untuk menulis error_mailfungsinya.

Lihat juga:


113
+1 Ini adalah jawaban yang benar sebenarnya . Saya tidak tahu mengapa orang-orang terpaku pada "Anda tidak dapat pulih dari kesalahan fatal" - pertanyaannya tidak mengatakan apa-apa tentang pemulihan.
David Harkness

21
Terima kasih, bagus. Memulihkan dari kesalahan fatal (batas memori misalnya) bukan sesuatu yang akan saya coba lakukan, tetapi membuat kesalahan ini dapat ditemukan (tanpa pelanggan mengirimkan tiket dukungan) membuat semua perbedaan.
Ilija

2
Menggunakan surat biasa:mail("myname@myemail.com", "My Site: FATAL ERROR", "Details: " . $errno . ' ' . $errstr . ' ' . $errfile . ' ' . $errline);
Eric Muyser

4
@ScottNicol Slava V benar, karena fungsi shutdown dipanggil setiap kali skrip selesai berjalan. Dengan cara kode ditulis sekarang, email akan dikirim pada SETIAP halaman memuat.
Nate

2
Catatan: ini bukan jawaban yang 100% benar. Setiap tempat yang menggunakan simbol @ untuk mengabaikan kesalahan masih akan SET kesalahan terakhir (sehingga Anda dapat menangani kesalahan). Jadi skrip Anda selesai tanpa masalah tetapi fungsi register_shutdown_f Masih berpikir kesalahan terjadi. Hanya sejak PHP 7 mereka memiliki fungsi error_clear_last ().
Rahly

150

Saya baru saja menemukan solusi ini (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');

Jenis kesalahan yang berbeda didefinisikan di Konstanta yang Ditentukan sebelumnya .


25
Solusi ini lebih bermanfaat bagi saya daripada jawaban teratas. Jawaban berperingkat teratas akan mengirimi Anda email setiap kali skrip berjalan, meskipun tidak ada kesalahan. Ini benar-benar berjalan pada kesalahan fatal.
kmoney12

@periklis, jika kesalahan terakhir sudah ditangani, error_get_last masih akan mengembalikannya bukan?
Pacerier

@Pacerier Saya tidak yakin apa yang Anda maksud dengan "ditangani", karena kesalahan bukan pengecualian, tapi saya kira jawabannya adalah "ya"
periklis

3
@Pacerier, begitu, itu pertanyaan yang menarik. Silahkan lihat di php.net/error_get_last , salah satu komentar menyebutkan bahwa " If an error handler (see set_error_handler ) successfully handles an error then that error will not be reported by this function."
periklis

1
Mungkin ini jelas, panggilan register_shutdown_function()harus lebih awal dari kesalahan fatal. use_1T_memory(); /* memory exhausted error here! */ register_shutdown_function('shutDownFunction');tidak akan berfungsi seperti yang diharapkan.
Nobu

117

PHP tidak menyediakan cara konvensional untuk menangkap dan memulihkan dari kesalahan fatal. Ini karena pemrosesan biasanya tidak dapat dipulihkan setelah kesalahan fatal. String yang cocok dengan buffer output (seperti yang disarankan oleh posting asli teknik yang dijelaskan di PHP.net) jelas-jelas keliru. Itu tidak bisa diandalkan.

Memanggil fungsi mail () dari dalam metode penangan kesalahan juga terbukti bermasalah. Jika Anda memiliki banyak kesalahan, server email Anda akan dimuat dengan pekerjaan, dan Anda bisa menemukan diri Anda dengan kotak masuk gnarly. Untuk menghindari hal ini, Anda dapat mempertimbangkan menjalankan cron untuk memindai log kesalahan secara berkala dan mengirimkan pemberitahuan yang sesuai. Anda mungkin juga ingin melihat ke dalam perangkat lunak pemantauan sistem, seperti Nagios .


Untuk berbicara sedikit tentang mendaftarkan fungsi shutdown:

Memang benar bahwa Anda dapat mendaftarkan fungsi shutdown, dan itu jawaban yang bagus.

Intinya di sini adalah bahwa kita biasanya tidak mencoba untuk memulihkan dari kesalahan fatal, terutama tidak dengan menggunakan ekspresi reguler terhadap buffer output Anda. Saya merespons jawaban yang diterima , yang tertaut ke saran di php.net yang sejak itu telah diubah atau dihapus.

Saran itu adalah untuk menggunakan regex terhadap buffer output selama penanganan pengecualian, dan dalam kasus kesalahan fatal (terdeteksi oleh pencocokan terhadap teks kesalahan yang dikonfigurasi apa pun yang mungkin Anda harapkan), cobalah untuk melakukan semacam pemulihan atau pemrosesan lanjutan. Itu tidak akan menjadi praktik yang disarankan (saya percaya itu sebabnya saya tidak dapat menemukan saran asli juga. Saya baik mengabaikannya, atau komunitas php menghapusnya).

Mungkin perlu dicatat bahwa versi PHP yang lebih baru (sekitar 5.1) tampaknya memanggil fungsi shutdown lebih awal, sebelum panggilan balik buffering output diaktifkan. Dalam versi 5 dan sebelumnya, urutan itu adalah kebalikannya (panggilan balik buffering output diikuti oleh fungsi shutdown). Juga, karena sekitar 5.0.5 (yang jauh lebih awal dari versi 5.2.3 si penanya), objek dibongkar dengan baik sebelum fungsi shutdown terdaftar dipanggil, jadi Anda tidak akan dapat mengandalkan objek di dalam memori untuk melakukan banyak hal.

Jadi mendaftarkan fungsi shutdown tidak apa-apa, tetapi jenis tugas yang seharusnya dilakukan oleh fungsi shutdown mungkin terbatas pada beberapa prosedur shutdown yang lembut.

Kuncinya dibawa pulang di sini adalah hanya beberapa kata-kata bijak bagi siapa saja yang tersandung pada pertanyaan ini dan melihat saran dalam jawaban yang awalnya diterima. Jangan regex buffer output Anda.


25
Pfff, saya ingat 650.000+ email yang saya dapatkan keesokan paginya. Sejak itu, ErrorHandler saya dibatasi 100 email per server web.
Bob Fanger

14
Itu tidak benar. Anda dapat menangkap kesalahan fatal dengan register_shutdown_function.
hipertracker

56
Ada kasus penggunaan yang ada karena ingin menangkap kesalahan fatal. Suite tes, misalnya, tidak boleh berhenti begitu saja ketika salah satu gagal, mereka harus melaporkan kesalahan fatal dan melanjutkan ke tes berikutnya. PHP terlalu banyak membuat kesalahan "fatal".
Chad

24
Ya mengatakan mereka "tidak boleh ditangkap" sangat pendek. Dalam sistem produksi, Anda perlu tahu kapan sesuatu gagal (mengatur email atau mencatat sesuatu dalam database - penanganan kesalahan php default tidak terlalu canggih).
BT

8
Saya ingin membuat komentar singkat tentang apa yang Anda semua katakan tentang "Kesalahan perlu ditangkap, sehingga kami dapat memperbaikinya" ... Ini arahan ini log_errors dan error_log.
Kelly Elton

37

Yah, sepertinya mungkin untuk menangkap kesalahan fatal dengan cara lain :)

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
Saya akan memberikan 10 upvotes ini jika saya bisa. Ini berfungsi dengan baik bagi saya pada kesalahan-kesalahan aneh yang kadang-kadang terjadi ketika sebuah halaman membom dan tidak ada yang dicatat. Saya tidak akan menggunakan kode produksi langsung tetapi sangat bagus untuk menambahkan ke halaman ketika jawaban cepat untuk apa yang gagal diperlukan. Terima kasih!
Night Owl

Salah satu solusi terbaik yang saya temukan di Internet. Bekerja seperti pesona.
Bangkit

Dengan cara apa? Penjelasan akan diurutkan, terutama jika itu adalah salah satu solusi terbaik di Internet (itu bisa menjadi lebih baik).
Peter Mortensen

Apakah semua konten CSS dibutuhkan? Tidak bisakah itu dipotong ke esensi? Tanggapi dengan mengedit jawaban Anda, bukan di sini dalam komentar (yang sesuai).
Peter Mortensen

@PeterMortensen Saya tidak mengklaim yang terbaik. Juga ini solusi pribadi saya untuk masalah, ada banyak pilihan lain yang lebih baik dan lebih profesional. Seperti yang disarankan oleh seseorang itu tidak cocok untuk produksi. Css ada di sana bcz saya baru saja menyisipkan kode pribadi saya
sakhunzai

36

Kesalahan fatal atau kesalahan fatal yang dapat dipulihkan sekarang melempar instance Errordalam PHP 7 atau versi yang lebih tinggi . Seperti pengecualian lainnya, Errorobjek dapat ditangkap menggunakan try/catchblok.

Contoh:

<?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

Atau Anda dapat menggunakan Throwableantarmuka untuk menangkap semua pengecualian.

Contoh:

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

https://3v4l.org/Br0MG

Untuk informasi lebih lanjut: http://php.net/manual/en/language.errors.php7.php


2
Adakah ideia tentang cara menggunakan ini untuk menangkap kesalahan seperti Fatal error: Trait 'FailedTrait' not found insaat menggunakan ReflectionClass?
TCB13

1
@ TCB13 mencoba untuk membungkus coba coba isi dalam file dan include "filename.php"sebagai gantinya di tryblok, lalu Throwabletangkap blok yang paling tidak berfungsi ParseError.
Niloct

24

Saya mengembangkan cara untuk menangkap semua jenis kesalahan di PHP (hampir semua)! Saya tidak yakin tentang E_CORE_ERROR (saya pikir tidak akan berfungsi hanya untuk kesalahan itu)! Tetapi, untuk kesalahan fatal lainnya (E_ERROR, E_PARSE, E_COMPILE ...) berfungsi dengan baik hanya menggunakan satu fungsi penangan kesalahan! Ada solusi saya:

Letakkan kode berikut ini di file utama Anda (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
Apa yang dilakukan baris @sertakan 'content.php'?
Marco

22

Anda tidak dapat menangkap / menangani kesalahan fatal, tetapi Anda dapat mencatat / melaporkannya. Untuk debugging cepat, saya memodifikasi satu jawaban untuk kode sederhana ini

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

tetapi kemana kode ini akan pergi?
TKol

@TKoL baris pertama. Pada dasarnya, masukkan file skrip / program Anda, jadi jalankan terlebih dahulu, jika itu tidak memungkinkan, letakkan di file umum
zainengineer

17

Anda tidak bisa melempar pengecualian di dalam fungsi shutdown terdaftar seperti itu:

<?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
    }
?>

Tetapi Anda dapat menangkap dan mengarahkan permintaan ke halaman lain.

<?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

Jika Anda menggunakan PHP> = 5.1.0 Lakukan saja hal seperti ini dengan kelas 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

Solusi bagus ditemukan di 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);
    }
}

Kelas ini memungkinkan Anda untuk memulai spesifik ErrorHandlersesekali jika Anda membutuhkannya. Dan kemudian Anda juga bisa menghentikan Handler.

Gunakan kelas ini misalnya seperti ini:

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;

Tautan ke kode kelas lengkap:
https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/ErrorHandler.php


Solusi yang mungkin lebih baik adalah yang dari Monolog :

Tautan ke kode kelas lengkap:
https://github.com/Seldaek/monolog/blob/master/src/Monolog/ErrorHandler.php

Itu juga dapat menangani FATAL_ERRORS menggunakan register_shutdown_functionfungsi. Menurut kelas ini FATAL_ERROR adalah salah satu dari yang berikut 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

Saya perlu menangani kesalahan fatal untuk produksi sebagai gantinya menunjukkan 503 Layanan statis tidak tersedia keluaran HTML. Ini tentunya merupakan pendekatan yang masuk akal untuk "menangkap kesalahan fatal". Inilah yang telah saya lakukan:

Saya memiliki fungsi penanganan kesalahan khusus "error_handler" yang akan menampilkan halaman HTML "503 tidak tersedia" pada setiap E_ERROR, E_USER_ERROR, dll. Sekarang ini akan dipanggil pada fungsi shutdown, menangkap kesalahan fatal saya,

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

dalam fungsi error_handler kustom saya, jika kesalahannya adalah E_ERROR, E_USER_ERROR, dll. Saya juga memanggil @ob_end_clean();untuk mengosongkan buffer, sehingga menghapus pesan "kesalahan fatal" PHP.

Perhatikan @fungsi-fungsi ketat pemeriksaan dan pembungkaman isset () karena kami tidak ingin skrip error_handler kami menghasilkan kesalahan.

Masih setuju dengan keparo, menangkap kesalahan fatal tidak mengalahkan tujuan "kesalahan FATAL" sehingga tidak benar-benar ditujukan bagi Anda untuk melakukan pemrosesan lebih lanjut. Jangan menjalankan fungsi mail () apa pun dalam proses pematian ini karena Anda pasti akan mencadangkan server email atau kotak masuk Anda. Alih-alih mencatat kejadian ini untuk mengajukan dan menjadwalkan pekerjaan cron untuk menemukan file error.log ini dan mengirimkannya ke administrator.


7

PHP memiliki kesalahan fatal yang bisa ditangkap. Mereka didefinisikan sebagai E_RECOVERABLE_ERROR. Manual PHP menggambarkan E_RECOVERABLE_ERROR sebagai:

Kesalahan fatal yang bisa ditangkap. Ini menunjukkan bahwa kemungkinan kesalahan berbahaya terjadi, tetapi tidak meninggalkan Engine dalam keadaan tidak stabil. Jika kesalahan tidak ditangkap oleh pegangan yang ditentukan pengguna (lihat juga set_error_handler () ), aplikasi dibatalkan karena itu adalah E_ERROR.

Anda dapat "menangkap" kesalahan "fatal" ini dengan menggunakan set_error_handler () dan memeriksa E_RECOVERABLE_ERROR. Saya merasa berguna untuk membuang Exception ketika kesalahan ini tertangkap, maka Anda dapat menggunakan try / catch.

Pertanyaan dan jawaban ini memberikan contoh yang bermanfaat: Bagaimana saya bisa menangkap "kesalahan fatal yang bisa ditangkap" pada petunjuk jenis PHP?

Namun, kesalahan E_ERROR dapat diatasi, tetapi tidak dapat dipulihkan kembali karena mesin dalam keadaan tidak stabil.


6

Ini hanyalah trik yang bagus untuk mendapatkan metode error_handler saat ini =)

<?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;
        }
    }
?>

Saya juga ingin mencatat bahwa jika Anda menelepon

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

PHP berhenti menampilkan kesalahan. Jika tidak, teks kesalahan akan dikirim ke klien sebelum penangan kesalahan Anda.


1
Terpilih ini karena baris ini_set ('display_errors', false);
Sahib Khan

Jika karena alasan tertentu bit ini ada di dalamnya masih akan menampilkan kesalahan php bahkan Anda menanganinya secara berbeda
Sahib Khan

5

Karena sebagian besar jawaban di sini adalah verbose yang tidak perlu, inilah versi non-jelek dari jawaban pilihan utama saya:

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

Tidak juga. Kesalahan fatal disebut demikian, karena kesalahan fatal. Anda tidak dapat pulih dari mereka.


12
menangkap dan memulihkan adalah dua hal yang sangat berbeda.
Simon Forsberg

3

Saya mengembangkan fungsi ini untuk memungkinkan kode "kotak pasir" yang dapat menyebabkan kesalahan fatal. Karena pengecualian yang dilemparkan dari penutupan register_shutdown_functiontidak dipancarkan dari tumpukan panggilan kesalahan pra-fatal, saya terpaksa keluar setelah fungsi ini untuk menyediakan cara yang seragam untuk menggunakannya.

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

Ada beberapa kondisi tertentu di mana kesalahan fatal harus ditangkap (Anda mungkin perlu melakukan pembersihan sebelum keluar dengan anggun dan tidak hanya mati ..).

Saya telah menerapkan kait pre_system di aplikasi CodeIgniter saya sehingga saya bisa mendapatkan kesalahan fatal melalui email, dan ini membantu saya menemukan bug yang tidak dilaporkan (atau dilaporkan setelah diperbaiki, seperti yang sudah saya ketahui tentang mereka :)).

Sendemail memeriksa apakah kesalahan telah dilaporkan sehingga tidak mengirimkan spam kepada Anda dengan kesalahan yang diketahui beberapa kali.

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();
    }
}

Apa itu "Sendemail" ? Apakah maksud Anda Sendmail (merespons dengan mengedit jawaban Anda , bukan di sini dalam komentar)?
Peter Mortensen
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.