Bir PHP ölümcül (`E_ERROR`) hatasını nasıl yakalarım?


557

set_error_handler()Çoğu PHP hatalarını yakalamak için kullanabilirsiniz , ancak var olmayan E_ERRORbir işlevi çağırmak gibi ölümcül ( ) hataları için çalışmaz. Bu hataları yakalamanın başka bir yolu var mı?

mail()Tüm hataları aramak için çalışıyorum ve PHP 5.2.3 çalıştırıyorum.


Ben PHP tüm hataları yakalamak için komple bir çözüm ile wiki tarzı bir soru-cevap yazdı; burada Stack Overflow'da görülebilir / temizlenebilir / çalınabilir / eleştirilebilir . Çözüm, PHP'nin üretebileceği tüm hataları saran ve sonuçta adı geçen hataları 'ErrorHandler' türünde bir nesneye iletecek beş yöntem içerir.
DigitalJedi805



Yanıtlar:


635

register_shutdown_functionPHP 5.2 ve sonraki sürümleri gerektiren önemli hataları günlüğe kaydet:

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

error_mailVe format_errorişlevlerini tanımlamanız gerekecektir . Örneğin:

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

İşlevi yazmak için Swift Mailer'ı kullanın error_mail.

Ayrıca bakınız:


113
1 Bu gerçek , doğru cevap. İnsanların neden "ölümcül hatalardan kurtulamazsın" diye asıldığını bilmiyorum - soru iyileşme hakkında hiçbir şey söylemedi.
David Harkness

21
Teşekkürler, iyi olan. Ölümcül hatalardan kurtulmak (örneğin bellek sınırları) yapmaya çalıştığım bir şey değil, ancak bu hataları keşfedilebilir hale getirmek (müşteri destek bileti göndermeden) tüm farkı yaratıyor.
Ilija

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

4
@ScottNicol Slava V doğrudur, çünkü komut dosyasının çalışması her bittiğinde kapatma işlevi çağrılır. Kodun şimdi yazılma şekliyle, HER sayfa yüklemesinde bir e-posta gönderilecektir.
Nate

2
Not: Bu% 100 doğru bir cevap değildir. Hataları yoksaymak için @ sembolü kullanan herhangi bir yer yine de son hatayı ayarlar (böylece hataları işleyebilirsiniz). Komut dosyanız sorunsuz bir şekilde bitiyor, ancak register_shutdown_function hala bir hata olduğunu düşünüyor. Ancak PHP 7'den beri error_clear_last () fonksiyonuna sahipler.
Rahly

150

Ben sadece bu çözümü (PHP 5.2.0+) ile geldi:

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

Önceden Tanımlı Sabitler'de farklı hata türleri tanımlanır .


25
Bu çözüm benim için en çok puan alan cevaptan çok daha fazlasını yapıyor. En yüksek puanlı yanıt, hata olmasa bile komut dosyası her çalıştığında size bir e-posta gönderir. Bu kesinlikle ölümcül bir hata ile çalışır.
kmoney12 26:03

@periklis, son hata zaten ele alınmışsa, error_get_last yine de döndürür değil mi?
Pacerier

@Pacerier "istisna" ile ne demek istediğinizden emin değilim, çünkü hatalar istisna değildir, ancak cevabın "evet" olduğunu varsayalım
periklis

3
@Pacerier, ilginç bir soru. Php.net/error_get_last bir göz atın , yorumlardan biri " If an error handler (see set_error_handler ) successfully handles an error then that error will not be reported by this function."
periklis

1
Belki de bu açıktır, çağrı register_shutdown_function()herhangi bir ölümcül hatadan daha erken olmalıdır. use_1T_memory(); /* memory exhausted error here! */ register_shutdown_function('shutDownFunction');beklendiği gibi çalışmaz.
Nobu

117

PHP, ölümcül hataları yakalamak ve kurtarmak için geleneksel araçlar sağlamaz. Bunun nedeni, ölümcül bir hatadan sonra işlemenin genellikle kurtarılmaması gerektiğidir. Bir çıktı arabelleği ile eşleşen dize (orijinal yazı tarafından PHP.net'te açıklanan teknikte önerildiği gibi) kesinlikle tavsiye edilmez. Güvenilmez.

Mail () işlevini bir hata işleyici yönteminden çağırmak da sorunludur. Çok fazla hatayla karşılaşırsanız, posta sunucunuz işe yüklenir ve kendinizi korkunç bir gelen kutusuyla bulabilirsiniz. Bunu önlemek için, hata günlüklerini düzenli olarak taramak ve buna göre bildirim göndermek için bir cron çalıştırmayı düşünebilirsiniz. Nagios gibi sistem izleme yazılımlarına da bakmak isteyebilirsiniz .


Bir kapatma işlevinin kaydedilmesi hakkında biraz konuşmak için:

Bir kapatma işlevi kaydedebileceğiniz doğrudur ve bu iyi bir yanıttır.

Buradaki nokta, özellikle çıktı tamponunuza karşı düzenli bir ifade kullanarak değil, ölümcül hatalardan kurtulmaya çalışmamamız gerektiğidir. Php.net üzerinde değiştirilmiş veya kaldırılmış bir öneriyle bağlantılı olarak kabul edilen cevaba yanıt veriyordum.

Bu öneri, özel durum işleme sırasında çıktı arabelleğine bir normal ifade kullanmaktı ve önemli bir hata olması durumunda (beklediğiniz yapılandırılmış hata metniyle eşleştirme tarafından algılanıyorsa), bir tür kurtarma veya devam eden işlem yapmaya çalışın. Bu önerilen bir uygulama olmaz (Ben de bu yüzden orijinal öneri bulamıyorum inanıyorum. Ya ben ya gözden, ya da php topluluk vuruldu).

PHP'nin daha yeni sürümlerinin (yaklaşık 5.1), çıktı arabelleğe alma geri çağrılmasını sağlamadan önce kapatma işlevini daha önce çağırdığını belirtmek gerekir. Sürüm 5 ve önceki sürümlerde, bu sıra tersidir (çıktı arabelleğe alma geri aramasını ardından kapatma işlevi). Ayrıca, yaklaşık 5.0.5 (sorucının 5.2.3 sürümünden çok daha erken) olduğundan, kayıtlı bir kapatma işlevi çağrılmadan önce nesneler çok iyi kaldırılır, bu nedenle yapmak için bellek içi nesnelerinize güvenemezsiniz. çok şey var.

Bu nedenle, bir kapatma işlevinin kaydedilmesi iyidir, ancak bir kapatma işlevi tarafından gerçekleştirilmesi gereken görevler muhtemelen bir avuç nazik kapatma prosedürüyle sınırlıdır.

Buradaki anahtar teslim, bu soruya tökezleyen ve ilk kabul edilen cevapta tavsiyeyi gören herkes için sadece bazı bilgelik sözleridir. Çıktı tamponunuzu düzenli olarak düzenlemeyin.


25
Pfff, ertesi sabah aldığım 650.000'den fazla e-postayı hatırlıyorum. O zamandan beri ErrorHandler'ım web sunucusu başına 100 e-posta ile sınırlandırıldı.
Bob Fanger

14
Bu doğru değil. Register_shutdown_function ile önemli hataları yakalayabilirsiniz.
hipertracker

56
Ölümcül hataları yakalamak isteyen kullanım örnekleri vardır. Örneğin, test paketleri yalnızca biri başarısız olduğunda durmamalı, ölümcül hatayı rapor etmeli ve bir sonraki teste geçmelidir. PHP çok fazla şey "ölümcül" hataları yapar.
Çad

24
Evet, "yakalanmamaları" gerektiğini söylemek çok kısa görüşlü. Bir üretim sisteminde, bir şeyin ne zaman başarısız olduğunu bilmeniz gerekir (bir veritabanında e-postalar ayarlayın veya bir şeyleri günlüğe kaydedin - varsayılan php hata işleme çok karmaşık değildir).
BT

8
"Hatalar yakalanmalı, böylece onları düzeltebilmemiz için" hakkında söyledikleriniz hakkında hızlı bir yorum yapmak istiyorum ... Ini yönergeleri ini log_errors ve error_log.
Kelly Elton

37

Ölümcül hataları başka türlü yakalamak mümkün görünüyor :)

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
Yapabilseydim bu 10 oyu verirdim. Bir sayfa bombaladığında ve hiçbir şey kaydedilmediğinde oluşan garip hatalarda benim için mükemmel çalışıyor. Canlı prodüksiyon kodunda kullanmam, ancak neyin başarısız olduğuna hızlı bir cevap gerektiğinde bir sayfaya eklemek harika. Teşekkür ederim!
Night Owl

İnternette bulduğum en iyi çözümlerden biri. Cazibe gibi çalışır.
sıçrama

Ne şekilde? Özellikle internetteki en iyi çözümlerden biri ise (daha da iyi olabilir) bir açıklama yapılabilir.
Peter Mortensen

Örneğin tüm CSS içeriği gerekli mi? Şartlara indirgenemez mi? Cevabınızı düzenleyerek yanıtlayın, burada yorumlarda değil (uygun şekilde).
Peter Mortensen

@PeterMortensen Elinden gelenin en iyisini istemiyorum. Ayrıca bu benim kişisel çözüm sorunum var, daha profesyonel seçenekler çok daha iyi seçenekler var. Birinin önerdiği gibi üretime uygun değil. Css sadece kişisel kodumu kesilmiş bcz var
sakhunzai

36

Ölümcül hata veya geri kazanılabilir ölümcül hataları artık örneklerini atmak Erroriçinde PHP 7 veya daha yüksek sürümleri . Diğer istisnalar gibi, Errornesneler bir try/catchblok kullanılarak yakalanabilir .

Misal:

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

Veya Throwabletüm istisnaları yakalamak için arayüzü kullanabilirsiniz .

Misal:

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

https://3v4l.org/Br0MG

Daha fazla bilgi için: http://php.net/manual/en/language.errors.php7.php


2
Gibi bir hata yakalamak için bu nasıl kullanılacağına ilişkin herhangi ideia Fatal error: Trait 'FailedTrait' not found inkullanırken ReflectionClass?
TCB13

1
@ TCB13 deneyin iç içeriğini bir dosyada ve include "filename.php"bunun yerine tryblokta sarmaya çalışın , sonra Throwableen azından işe yarayan bloğu yakalayın ParseError.
Niloct

24

PHP tüm hata türlerini yakalamak için bir yol geliştirdim (neredeyse hepsi)! E_CORE_ERROR hakkında hiç emin değilim (Bence sadece bu hata için işe yaramaz)! Ancak, diğer önemli hatalar için (E_ERROR, E_PARSE, E_COMPILE ...) sadece bir hata işleyici işlevi kullanarak iyi çalışır! İşte benim çözümüm:

Bu kodu ana dosyanıza (index.php) yerleştirin:

<?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
@İnclude 'content.php' satırı ne yapar?
Marco

22

Önemli hataları yakalayamaz / işleyemezsiniz, ancak bunları günlüğe kaydedebilir / bildirebilirsiniz. Hızlı hata ayıklama için bu basit koda bir cevap değiştirdim

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

ama bu kod nereye giderdi?
TKoL

@TKoL ilk satırı. Temel olarak komut dosyanızın / programınızın giriş dosyası, bu yüzden ilk önce yürütülür, eğer bu mümkün değilse ortak bir dosyaya koyun
zainengineer

17

Kayıtlı bir kapatma işlevinin içine şöyle bir istisna atamazsınız:

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

Ancak isteği yakalayabilir ve başka bir sayfaya yönlendirebilirsiniz.

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

PHP> = 5.1.0 kullanıyorsanız, ErrorException sınıfıyla böyle bir şey yapın:

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

Zend Framework 2'de hoş bir çözüm bulundu:

/**
 * 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);
    }
}

Bu sınıf, ErrorHandlerihtiyaç duyduğunuzda belirli dersleri bazen başlatmanızı sağlar . Ve sonra İşleyiciyi de durdurabilirsiniz.

Bu sınıfı örneğin şu şekilde kullanın:

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;

Tam sınıf koduna bağlantı:
https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/ErrorHandler.php


Belki daha iyi bir çözüm, Monolog'dan bir çözümdür :

Tam sınıf koduna bağlantı:
https://github.com/Seldaek/monolog/blob/master/src/Monolog/ErrorHandler.php

Ayrıca register_shutdown_functionişlevi kullanarak FATAL_ERRORS ile başa çıkabilir . Bu sınıfa göre FATAL_ERROR aşağıdakilerden biridir 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

Bunun yerine üretim için statik hataları 503 Hizmet Kullanılamaz HTML çıktısı göstermek için ölümcül hataları ele gerekir . Bu kesinlikle "ölümcül hataları yakalamak" için makul bir yaklaşımdır. Ben de bunu yaptım:

Herhangi bir E_ERROR, E_USER_ERROR, vb. Üzerinde "503 hizmet kullanılamıyor" HTML sayfamı görüntüleyecek özel bir hata işleme fonksiyonu "error_handler" var.

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

benim özel error_handler fonksiyonu, eğer hata E_ERROR, E_USER_ERROR, vb. Ben de @ob_end_clean();tampon boşaltmak için çağrı , böylece PHP'nin "ölümcül hata" mesajını kaldırıyorum.

@Error_handler komut dosyalarımızın herhangi bir hata oluşturmasını istemediğimiz için katı isset () kontrol ve susturma fonksiyonlarını dikkate alın .

Hala keparo ile hemfikir olmakla birlikte, ölümcül hataları yakalamak "FATAL hatasının" amacını bozar, bu nedenle daha fazla işlem yapmanız amaçlanmamıştır. Kesinlikle posta sunucusunu veya gelen kutunuzu yedekleyeceğiniz için bu kapatma işleminde posta () işlevlerini çalıştırmayın. Bunun yerine, bu error.log dosyalarını bulmak ve yöneticilere postalamak için bir cron işi zamanlamak için bu olayları günlüğe kaydedin .


7

PHP'nin önemli ölümcül hataları var. Bunlar E_RECOVERABLE_ERROR olarak tanımlanır. PHP el kitabı bir E_RECOVERABLE_ERROR'u şöyle tanımlar:

Yakalanabilir ölümcül hata. Muhtemelen tehlikeli bir hatanın meydana geldiğini, ancak Motoru kararsız bir durumda bırakmadığını gösterir. Hata, kullanıcı tanımlı bir tanıtıcı tarafından yakalanmazsa (ayrıca bkz. Set_error_handler () ), uygulama bir E_ERROR olduğu gibi iptal edilir.

Set_error_handler () öğesini kullanarak ve E_RECOVERABLE_ERROR öğesini kontrol ederek bu "ölümcül" hataları "yakalayabilirsiniz" . Bu hata yakalandığında bir İstisna atmak yararlı buluyorum, o zaman try / catch kullanabilirsiniz.

Bu soru ve cevap yararlı bir örnek sağlar: PHP türü ipucunda nasıl "yakalanabilir bir ölümcül hata" yakalayabilirim?

Bununla birlikte, E_ERROR hataları işlenebilir, ancak motor kararsız durumda olduğu için kurtarılamaz.


6

İşte geçerli error_handler yöntemini almak için güzel bir hile =)

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

Ayrıca şunu da belirtmek isterim ki

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

PHP hatayı görüntülemeyi durdurur. Aksi takdirde, hata metni hata işleyicinizden önce istemciye gönderilir.


1
İni_set ('display_errors', false) satırı nedeniyle bunu oyladı.
Sahib Khan

Herhangi bir nedenden dolayı bu bit açıksa bile farklı ele bile php hataları gösterecektir
Sahib Khan

5

Buradaki cevapların çoğu gereksiz yere ayrıntılı olduğundan, işte en çok oy alan cevabın çirkin olmayan versiyonum:

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

Pek sayılmaz. Ölümcül hatalar denir, çünkü ölümcüldürler. Onlardan kurtulamazsın.


12
yakalamak ve iyileşmek çok farklı iki şeydir.
Simon Forsberg

3

Ben ölümcül hataya neden olabilir kod "sandbox" mümkün kılmak için bu işlevi geliştirdi. Kapatmadan atılan istisnalar, register_shutdown_functionölümcül hata çağrısı yığınından yayılmadığından, tekdüze bir kullanım yöntemi sağlamak için bu işlevden sonra çıkmak zorunda kaldım.

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

Ölümcül hataların bile yakalanması gereken bazı durumlar vardır (zarif bir şekilde çıkmadan önce biraz temizlik yapmanız gerekebilir ve sadece ölmeyin ..).

CodeIgniter uygulamalarımda bir pre_system kancası uyguladım, böylece e-postalarla ölümcül hatalarımı alabildim ve bu bana bildirilmedi (veya onlar zaten bildiği gibi, düzeltildikten sonra bildirildi) hataları bulmama yardımcı oldu :).

Sendemail, hatanın önceden bildirilip bildirilmediğini kontrol eder, böylece bilinen hatalarla size birkaç kez spam göndermez.

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

Nedir "SendEmail" ? Sendmail ( burada yorumlarda değil , cevabınızı düzenleyerek cevap verin ) mi demek istediniz ?
Peter Mortensen
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.