52 votes

Comment puis-je faire en sorte que PHP produise un backtrace en cas d'erreur ?

Essayer de déboguer PHP en utilisant les messages d'erreur par défaut de la ligne courante seulement est horrible. Comment puis-je faire en sorte que PHP produise un backtrace (trace de la pile) lorsque des erreurs sont produites ?

48voto

chaos Points 69029

Mon script pour installer un gestionnaire d'erreur qui produit un backtrace :

<?php
function process_error_backtrace($errno, $errstr, $errfile, $errline, $errcontext) {
    if(!(error_reporting() & $errno))
        return;
    switch($errno) {
    case E_WARNING      :
    case E_USER_WARNING :
    case E_STRICT       :
    case E_NOTICE       :
    case E_USER_NOTICE  :
        $type = 'warning';
        $fatal = false;
        break;
    default             :
        $type = 'fatal error';
        $fatal = true;
        break;
    }
    $trace = array_reverse(debug_backtrace());
    array_pop($trace);
    if(php_sapi_name() == 'cli') {
        echo 'Backtrace from ' . $type . ' \'' . $errstr . '\' at ' . $errfile . ' ' . $errline . ':' . "\n";
        foreach($trace as $item)
            echo '  ' . (isset($item['file']) ? $item['file'] : '<unknown file>') . ' ' . (isset($item['line']) ? $item['line'] : '<unknown line>') . ' calling ' . $item['function'] . '()' . "\n";
    } else {
        echo '<p class="error_backtrace">' . "\n";
        echo '  Backtrace from ' . $type . ' \'' . $errstr . '\' at ' . $errfile . ' ' . $errline . ':' . "\n";
        echo '  <ol>' . "\n";
        foreach($trace as $item)
            echo '    <li>' . (isset($item['file']) ? $item['file'] : '<unknown file>') . ' ' . (isset($item['line']) ? $item['line'] : '<unknown line>') . ' calling ' . $item['function'] . '()</li>' . "\n";
        echo '  </ol>' . "\n";
        echo '</p>' . "\n";
    }
    if(ini_get('log_errors')) {
        $items = array();
        foreach($trace as $item)
            $items[] = (isset($item['file']) ? $item['file'] : '<unknown file>') . ' ' . (isset($item['line']) ? $item['line'] : '<unknown line>') . ' calling ' . $item['function'] . '()';
        $message = 'Backtrace from ' . $type . ' \'' . $errstr . '\' at ' . $errfile . ' ' . $errline . ': ' . join(' | ', $items);
        error_log($message);
    }
    if($fatal)
        exit(1);
}

set_error_handler('process_error_backtrace');
?>

Mise en garde : elle est impuissante à influencer les différents aspects de l'économie. Erreurs fatales de PHP car Zend, dans sa sagesse, a décidé que ces derniers ignoreraient set_error_handler() . Vous obtenez donc toujours des erreurs inutiles de localisation finale uniquement avec ces derniers.

2 votes

Et comme debug_backtrace ne va pas au-delà de la fonction d'arrêt (la pile a déjà été vidée), vous serez peut-être intéressé de savoir que vous pouvez toujours accéder aux variables à portée globale dans les fonctions d'arrêt. Trouvez donc la ligne a E_ERROR est arrivé à et a défini une variable à portée globale égale au contenu de debug_backtrace sur la ligne précédente. Viola, vous avez un backtrace complet qui commence à la ligne précédant l'erreur fatale et qui est accessible à partir de l'icône register_shutdown_function fonction définie !

0 votes

<fichier inconnu> doit être <fichier inconnu> sinon il sera traité comme une balise et debug_backtrace() ne fonctionnera que pour la portée dans laquelle vous vous trouvez (qui est process_error_backtrace).

47voto

patcoll Points 680

Xdebug imprime un tableau de backtrace en cas d'erreur, et vous n'avez pas à écrire de code PHP pour l'implémenter.

L'inconvénient est que vous devez l'installer comme une extension PHP.

9 votes

Je recommande définitivement Xdebug aussi : je n'imaginerais pas développer en PHP sans lui ! Notez simplement que vous ne devez absolument pas l'installer sur un serveur de production : cela nuit aux performances (et vous n'avez pas, en théorie, besoin de ce genre d'informations sur un serveur de production).

0 votes

0 votes

@PascalMARTIN De plus, il s'agit d'une faille de sécurité majeure car elle peut révéler les valeurs des variables internes au monde entier - comme les mots de passe, les adresses, les informations sur les cartes de crédit ....

28voto

kenorb Points 2464

Erreur PHP

Il s'agit d'un meilleur rapport d'erreur pour le PHP écrit en PHP. Aucune extension supplémentaire n'est nécessaire !

Il s'agit d'une utilisation triviale où toutes les erreurs sont affichées dans le navigateur pour les demandes normales, AJAXy (en état de pause). Ensuite, toutes les erreurs vous fournissent un backtrace et un contexte de code sur toute la trace de la pile, y compris les arguments de fonction, les variables de serveur.

Il suffit d'inclure un seul fichier et d'appeler la fonction (au début de votre code), par ex.

require('php_error.php');
\php_error\reportErrors();

Voir les captures d'écran :

PHP Error | Improve Error Reporting for PHP - screenshot of backtrace PHP Error | Improve Error Reporting for PHP - screenshot of backtrace PHP Error | Improve Error Reporting for PHP - screenshot of backtrace

GitHub : https://github.com/JosephLenton/PHP-Error

Ma fourchette (avec corrections supplémentaires) : https://github.com/kenorb-contrib/PHP-Error

Déboguer PHP classe

Une classe complète de débogueur PHP, avec prise en charge des exceptions, des erreurs, des alertes (de l'utilisateur), des lignes de code et des drapeaux de mise en évidence.

Exemple d'utilisation :

 <?php
        include( dirname(dirname(__FILE__))  . '/src/Debug.php' );
        //Catch all
        Debug::register();

        //Generate an errors
        if( this_function_does_not_exists( ) )
        {
            return false;
        }
    ?>

Gestion des erreurs en PHP

L'exemple ci-dessous montre le traitement des exceptions internes en déclenchant des erreurs et en les traitant avec une fonction définie par l'utilisateur :

Le chemin le plus court (PHP) :

<?php
function e($number, $msg, $file, $line, $vars) {
   print_r(debug_backtrace());
   die();
}
set_error_handler('e');

Le chemin le plus long (PHP) :

// set to the user defined error handler
$old_error_handler = set_error_handler("myErrorHandler");

// error handler function
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
    if (!(error_reporting() & $errno)) {
        // This error code is not included in error_reporting
        return;
    }

    switch ($errno) {
    case E_USER_ERROR:
        echo "<b>My ERROR</b> [$errno] $errstr<br />\n";
        echo "  Fatal error on line $errline in file $errfile";
        echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
        echo "Aborting...<br />\n";
        var_dump(debug_backtrace());
        exit(1);
        break;

    case E_USER_WARNING:
        echo "<b>My WARNING</b> [$errno] $errstr<br />\n";
        break;

    case E_USER_NOTICE:
        echo "<b>My NOTICE</b> [$errno] $errstr<br />\n";
        break;

    default:
        echo "Unknown error type: [$errno] $errstr<br />\n";
        break;
    }

    /* Don't execute PHP internal error handler */
    return true;
}

Voir : http://www.php.net/manual/en/function.set-error-handler.php

Remarque : vous ne pouvez avoir qu'une seule exception d'erreur à la fois. Lorsque vous appelez la fonction set_error_handler(), elle renvoie le nom de l'ancien gestionnaire d'erreurs. Vous pouvez le stocker et l'appeler vous-même à partir de votre gestionnaire d'erreurs, ce qui vous permet d'avoir plusieurs gestionnaires d'erreurs.


XDebug

Pour une solution plus avancée, vous pouvez utiliser XDebug pour PHP.

Par défaut, lorsque XDebug est chargé, il doit vous montrer automatiquement le backtrace en cas d'erreur fatale. Vous pouvez aussi tracer dans un fichier (xdebug.auto_trace) pour avoir un très gros backtrace de la requête entière ou faire le profilage (xdebug.profiler_enable) ou autres paramètres . Si le fichier de trace est trop gros, vous pouvez utiliser xdebug_start_trace() et xdebug_stop_trace() pour vider la trace partielle.

Installation

Utilisation de PECL :

pecl install xdebug

Sous Linux :

sudo apt-get install php5-xdebug

Sur Mac (avec Homebrew) :

brew tap josegonzalez/php
brew search xdebug
php53-xdebug

Exemple de configuration de la mine :

[xdebug]

; Extensions
extension=xdebug.so
; zend_extension="/YOUR_PATH/php/extensions/no-debug-non-zts-20090626/xdebug.so"
; zend_extension="/Applications/MAMP/bin/php/php5.3.20/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so" ; MAMP

; Data
xdebug.show_exception_trace=1       ; bool: Show a stack trace whenever an exception is raised.
xdebug.collect_vars = 1             ; bool: Gather information about which variables are used in a certain scope.
xdebug.show_local_vars=1            ; int: Generate stack dumps in error situations.
xdebug.collect_assignments=1        ; bool: Controls whether Xdebug should add variable assignments to function traces.
xdebug.collect_params=4             ; int1-4: Collect the parameters passed to functions when a function call is recorded.
xdebug.collect_return=1             ; bool: Write the return value of function calls to the trace files.
xdebug.var_display_max_children=256 ; int: Amount of array children and object's properties are shown.
xdebug.var_display_max_data=1024    ; int: Max string length that is shown when variables are displayed.
xdebug.var_display_max_depth=3      ; int: How many nested levels of array/object elements are displayed.
xdebug.show_mem_delta=0             ; int: Show the difference in memory usage between function calls.

; Trace
xdebug.auto_trace=0                 ; bool: The tracing of function calls will be enabled just before the script is run.
xdebug.trace_output_dir="/var/log/xdebug" ; string: Directory where the tracing files will be written to.
xdebug.trace_output_name="%H%R-%s-%t"     ; string: Name of the file that is used to dump traces into.

; Profiler
xdebug.profiler_enable=0            ; bool: Profiler which creates files read by KCacheGrind.
xdebug.profiler_output_dir="/var/log/xdebug"  ; string: Directory where the profiler output will be written to.
xdebug.profiler_output_name="%H%R-%s-%t"      ; string: Name of the file that is used to dump traces into.
xdebug.profiler_append=0            ; bool: Files will not be overwritten when a new request would map to the same file.

; CLI
xdebug.cli_color=1                  ; bool: Color var_dumps and stack traces output when in CLI mode.

; Remote debugging
xdebug.remote_enable=off            ; bool: Try to contact a debug client which is listening on the host and port.
xdebug.remote_autostart=off         ; bool: Start a remote debugging session even GET/POST/COOKIE variable is not present.
xdebug.remote_handler=dbgp          ; select: php3/gdb/dbgp: The DBGp protocol is the only supported protocol.
xdebug.remote_host=localhost        ; string: Host/ip where the debug client is running.
xdebug.remote_port=9000             ; integer: The port to which Xdebug tries to connect on the remote host.
xdebug.remote_mode=req              ; select(req,jit): Selects when a debug connection is initiated.
xdebug.idekey="xdebug-cli"          ; string: IDE Key Xdebug which should pass on to the DBGp debugger handler.
xdebug.remote_log="/var/log/xdebug.log" ; string: Filename to a file to which all remote debugger communications are logged.

Drupal 6&7

Avec Devel activé :

/**
 * Implements hook_watchdog().
 */
function foo_watchdog($log_entry) {
  if ($log_entry['type'] == 'php' && $log_entry['severity'] <= WATCHDOG_WARNING) {
    function_exists('dd') && dd(debug_backtrace());
  }
}

La fonction ci-dessus enregistrera les backtraces pour chaque erreur dans un fichier temporaire ( /tmp/drupal_debug.txt par défaut).

Ou localisez le fichier via : drush eval "echo file_directory_temp() . '/drupal_debug.txt' .

Sans Devel activé, utilisez l'approche de la vieille école : var_dump(debug_backtrace()); au lieu de dd() .

0 votes

XDebug est génial, mais il n'est pas toujours disponible. Ioncube, par exemple, qui est utilisé dans beaucoup de grandes applications php, n'est pas compatible avec xDebug (j'ai rencontré ce problème la semaine dernière en travaillant sur un site Magento). J'aimerais également mentionner que Ioncube est de la merde. C'est pourquoi nous devons aussi savoir comment faire du backtrace manuel.

0 votes

Ensuite, vous pouvez utiliser la méthode standard des fonctions de gestion des erreurs (j'ai mis à jour la réponse). Ou la vieille école var_dump(debug_backtrace());exit; à la ligne précédant l'erreur (et visualisez la source dans votre navigateur web pour un meilleur formatage). D'autres outils de débogage pourraient inclure strace , dtrace etc. Avec un bon usage, vous pouvez trouver la source du problème.

8voto

Tim Points 41

Je viens d'essayer de définir une variable de session contenant le contenu de debug_backtrace() à la ligne incriminée, puis de l'imprimer en utilisant register_shutdown_function(). Ça a marché comme sur des roulettes.

3voto

Mythica Points 735

Vous pouvez utiliser debug_backtrace

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X