Puis-je essayer / attraper un avertissement?

358

J'ai besoin d'attraper quelques avertissements provenant de certaines fonctions natives php, puis de les gérer.

Plus précisément:

array dns_get_record  ( string $hostname  [, int $type= DNS_ANY  [, array &$authns  [, array &$addtl  ]]] )

Il émet un avertissement lorsque la requête DNS échoue.

try/ catchne fonctionne pas car un avertissement ne fait pas exception.

J'ai maintenant 2 options:

  1. set_error_handler semble exagéré car je dois l'utiliser pour filtrer chaque avertissement de la page (est-ce vrai?);

  2. Ajustez le rapport / affichage des erreurs afin que ces avertissements ne soient pas répercutés à l'écran, puis vérifiez la valeur de retour; si c'est le cas false, aucun enregistrement n'est trouvé pour le nom d'hôte.

Quelle est la meilleure pratique ici?

user121196
la source
1
stackoverflow.com/questions/136899/… est une bonne discussion sur des choses comme ça.
Mez
il y avait une réponse ci-dessous qui a été supprimée? soit par le propriétaire ou par quelqu'un?
user121196
voir aussi: stackoverflow.com/questions/1087365
dreftymac
@ user121196: Oui. Par le propriétaire.
Courses de légèreté en orbite le

Réponses:

373

Définir et restaurer le gestionnaire d'erreurs

Une possibilité consiste à définir votre propre gestionnaire d'erreurs avant l'appel et à restaurer le gestionnaire d'erreurs précédent ultérieurement avec restore_error_handler().

set_error_handler(function() { /* ignore errors */ });
dns_get_record();
restore_error_handler();

Vous pouvez construire sur cette idée et écrire un gestionnaire d'erreurs réutilisable qui enregistre les erreurs pour vous.

set_error_handler([$logger, 'onSilencedError']);
dns_get_record();
restore_error_handler();

Transformer les erreurs en exceptions

Vous pouvez utiliser set_error_handler()et la ErrorExceptionclasse pour transformer toutes les erreurs php en exceptions.

set_error_handler(function($errno, $errstr, $errfile, $errline, $errcontext) {
    // error was suppressed with the @-operator
    if (0 === error_reporting()) {
        return false;
    }

    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});

try {
    dns_get_record();
} catch (ErrorException $e) {
    // ...
}

La chose importante à noter lors de l'utilisation de votre propre gestionnaire d'erreurs est qu'il contournera le error_reportingparamètre et transmettra toutes les erreurs (notifications, avertissements, etc.) à votre gestionnaire d'erreurs. Vous pouvez définir un deuxième argument set_error_handler()pour définir les types d'erreur que vous souhaitez recevoir ou accéder au paramètre actuel à l'aide ... = error_reporting()du gestionnaire d'erreurs.

Supprimer l'avertissement

Une autre possibilité consiste à supprimer l'appel avec l'opérateur @ et à vérifier ensuite la valeur de retour dns_get_record(). Mais je déconseille cela car les erreurs / avertissements sont déclenchés pour être traités, et non supprimés.

Philippe Gerber
la source
3
est-il conseillé de définir mon propre gestionnaire d'erreurs juste avant l'appel de la fonction, puis de restore_error_handler lorsque la vérification des erreurs est terminée?
user121196
2
sera-ce thread-safe s'il y a beaucoup de requêtes simultanées, et chaque requête fait 1.set_error_handler (). 2.doit 3.restore_error_handler?
user121196
4
Merci; CA aide. (Et ils disent que PHP n'est pas un désastre.)
Aaron Miller
2
+1 pour éviter d'utiliser @ pour supprimer les erreurs. E_WARNING est en fait une erreur non fatale. En général, vous devez toujours essayer de gérer les erreurs de manière appropriée. Si votre application nécessite l'utilisation de set_error_handler, faites-le. Il est généralement conseillé de consigner les erreurs et de désactiver leur affichage dans un environnement de production. Après avoir vérifié les journaux, vous pouvez voir où apporter des modifications dans votre environnement de développement. Trop de cas où j'ai vu @ fopen / @ unlink et me demande pourquoi le développeur n'a pas effectué de vérification pour éviter les erreurs ou gérer l'erreur à l'aide de set_error_handler.
fyrye
5
Une note sur la transformation des avertissements en exceptions: un avertissement n'interrompra pas votre application - une exception non capturée fera l'affaire!
Álvaro González
149

La solution qui fonctionne vraiment s'est avérée être de définir un gestionnaire d'erreurs simple avec un E_WARNINGparamètre, comme ceci:

set_error_handler("warning_handler", E_WARNING);
dns_get_record(...)
restore_error_handler();

function warning_handler($errno, $errstr) { 
// do something
}
Robert
la source
4
également anonyme callablepeut être utilisé ici au lieu de chaîne avec déclaration de fonction
vp_arth
Merci, mais comment supprimer le gestionnaire d'erreurs après le bloc critique?
Yevgeniy Afanasyev
3
Excellent! Fonction juste à l' trow new \Exception($errstr, $errno);intérieur warning_handler. Merci.
Vladimir Vukanac
C'est la meilleure réponse ici!
lewis4u
28

Soyez prudent avec l' @opérateur - s'il supprime les avertissements, il supprime également les erreurs fatales. J'ai passé beaucoup de temps à déboguer un problème dans un système où quelqu'un avait écrit @mysql_query( '...' )et le problème était que le support mysql n'était pas chargé dans PHP et cela a provoqué une erreur fatale silencieuse. Il sera sûr pour les choses qui font partie du noyau PHP, mais veuillez l' utiliser avec précaution.

bob@mypc:~$ php -a
Interactive shell

php > echo @something(); // this will just silently die...

Aucune sortie supplémentaire - bonne chance pour le débogage!

bob@mypc:~$ php -a
Interactive shell

php > echo something(); // lets try it again but don't suppress the error
PHP Fatal error:  Call to undefined function something() in php shell code on line 1
PHP Stack trace:
PHP   1. {main}() php shell code:0
bob@mypc:~$ 

Cette fois, nous pouvons voir pourquoi il a échoué.

GuruBob
la source
5

Je voulais essayer / attraper un avertissement, mais en même temps garder la journalisation d'avertissement / erreur habituelle (par exemple dans /var/log/apache2/error.log); pour lequel le gestionnaire doit retourner false. Cependant, puisque l'instruction "throw new ..." interrompt fondamentalement l'exécution, il faut alors faire l'astuce "wrap in function", également discutée dans:

Existe-t-il un moyen statique de lever l'exception dans php

Ou, en bref:

  function throwErrorException($errstr = null,$code = null, $errno = null, $errfile = null, $errline = null) {
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
  }
  function warning_handler($errno, $errstr, $errfile, $errline, array $errcontext) {
    return false && throwErrorException($errstr, 0, $errno, $errfile, $errline);
    # error_log("AAA"); # will never run after throw
    /* Do execute PHP internal error handler */
    # return false; # will never run after throw
  }
  ...
  set_error_handler('warning_handler', E_WARNING);
  ...
  try {
    mkdir($path, 0777, true);
  } catch (Exception $e) {
    echo $e->getMessage();
    // ...
  }

EDIT: après un examen plus approfondi, il s'avère que cela ne fonctionne pas: le " return false && throwErrorException ..." ne lève pas l'exception, et se connecte simplement au journal des erreurs; la suppression de la false &&partie " ", comme dans " return throwErrorException ...", fera fonctionner la levée d' exceptions, mais ne se connectera pas dans le error_log ... Je garderais quand même cela affiché, car je n'ai pas vu ce comportement documenté ailleurs.

sdaau
la source
4

Vous devriez probablement essayer de vous débarrasser complètement de l'avertissement, mais si ce n'est pas possible, vous pouvez ajouter l'appel à @ (c'est-à-dire @dns_get_record (...)), puis utiliser toutes les informations que vous pouvez obtenir pour déterminer si l'avertissement s'est produit ou pas.

rpjohnst
la source
4

Normalement, vous ne devez jamais utiliser @ sauf si c'est la seule solution. Dans ce cas spécifique, la fonction dns_check_record doit être utilisée en premier pour savoir si l'enregistrement existe.

florynth
la source
3

La combinaison de ces lignes de code autour d'un file_get_contents()appel vers une URL externe m'a aidé à gérer les avertissements du type " Échec de l'ouverture du flux: la connexion a expiré " bien mieux:

set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line, array $err_context)
{
    throw new ErrorException( $err_msg, 0, $err_severity, $err_file, $err_line );
}, E_WARNING);
try {
    $iResult = file_get_contents($sUrl);
} catch (Exception $e) {
    $this->sErrorMsg = $e->getMessage();
}
restore_error_handler();

Cette solution fonctionne également dans le contexte de l'objet. Vous pouvez l'utiliser dans une fonction:

public function myContentGetter($sUrl)
{
  ... code above ...
  return $iResult;
}
Bugfighter
la source
2

En cas d' dns_get_record()échec, il doit revenir FALSE, vous pouvez donc supprimer l'avertissement avec @puis vérifier la valeur de retour.

ambre
la source
0

essayez de vérifier s'il retourne une valeur booléenne, vous pouvez simplement le mettre comme condition. J'ai rencontré cela avec le oci_execute (...) qui renvoyait une violation avec mes clés uniques.

ex.
oci_parse($res, "[oracle pl/sql]");
if(oci_execute){
...do something
}
gborjal
la source
0

DossierStructure

index.php //Script File
logs //Folder for log Every warning and Errors
CustomException.php //Custom exception File

CustomException.php

/**
* Custom error handler
*/
function handleError($code, $description, $file = null, $line = null, $context = null) {
    $displayErrors = ini_get("display_errors");;
    $displayErrors = strtolower($displayErrors);
    if (error_reporting() === 0 || $displayErrors === "on") {
        return false;
    }
    list($error, $log) = mapErrorCode($code);
    $data = array(
        'timestamp' => date("Y-m-d H:i:s:u", time()),
        'level' => $log,
        'code' => $code,
        'type' => $error,
        'description' => $description,
        'file' => $file,
        'line' => $line,
        'context' => $context,
        'path' => $file,
        'message' => $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']'
    );
    $data = array_map('htmlentities',$data);
    return fileLog(json_encode($data));
}

/**
* This method is used to write data in file
* @param mixed $logData
* @param string $fileName
* @return boolean
*/
function fileLog($logData, $fileName = ERROR_LOG_FILE) {
    $fh = fopen($fileName, 'a+');
    if (is_array($logData)) {
        $logData = print_r($logData, 1);
    }
    $status = fwrite($fh, $logData . "\n");
    fclose($fh);
//    $file = file_get_contents($filename);
//    $content = '[' . $file .']';
//    file_put_contents($content); 
    return ($status) ? true : false;
}

/**
* Map an error code into an Error word, and log location.
*
* @param int $code Error code to map
* @return array Array of error word, and log location.
*/
function mapErrorCode($code) {
    $error = $log = null;
    switch ($code) {
        case E_PARSE:
        case E_ERROR:
        case E_CORE_ERROR:
        case E_COMPILE_ERROR:
        case E_USER_ERROR:
            $error = 'Fatal Error';
            $log = LOG_ERR;
            break;
        case E_WARNING:
        case E_USER_WARNING:
        case E_COMPILE_WARNING:
        case E_RECOVERABLE_ERROR:
            $error = 'Warning';
            $log = LOG_WARNING;
            break;
        case E_NOTICE:
        case E_USER_NOTICE:
            $error = 'Notice';
            $log = LOG_NOTICE;
            break;
        case E_STRICT:
            $error = 'Strict';
            $log = LOG_NOTICE;
            break;
        case E_DEPRECATED:
        case E_USER_DEPRECATED:
            $error = 'Deprecated';
            $log = LOG_NOTICE;
            break;
        default :
            break;
    }
    return array($error, $log);
}
//calling custom error handler
set_error_handler("handleError");

il suffit d'inclure le fichier ci-dessus dans votre fichier de script comme celui-ci

index.php

error_reporting(E_ALL);
ini_set('display_errors', 'off');
define('ERROR_LOG_FILE', 'logs/app_errors.log');

include_once 'CustomException.php';
echo $a; // here undefined variable warning will be logged into logs/app_errors.log
Juned Ansari
la source
-2

Je ne recommanderais d'utiliser @ pour supprimer les avertissements que lorsque c'est une opération simple (par exemple $ prop = @ ($ high / ($ width - $ depth)); pour ignorer la division par zéro avertissement). Cependant, dans la plupart des cas, il est préférable de gérer.

tanovellino
la source
2
C'est une fois que vous ne voulez certainement pas utiliser @ - vous avez le contrôle sur l'opération et pouvez vérifier si c'est une division par zéro ou non avant de le faire.
Eborbob