IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

Interception erreurs et mail


Sujet :

Langage PHP

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé Avatar de scrouet
    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    280
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 280
    Par défaut Interception erreurs et mail
    Bonjour.

    J'ai défini un même gestionnaire d'erreur et d'exception, gerer_plantage(), qui me permet de logger l'événement dans un fichier et d'envoyer un mail à l'administrateur pour l'informer du plantage. Le mail est envoyé via une fonction envoyer_mail() qui permet de formater les messages pour l'ensemble du site.
    Je constate les choses suivantes :
    • lorsque la fonction standard mail(), utilisée par envoyer_mail(), plante (ex : impossible de contacter le serveur SMTP), une erreur est générée ;
    • l'erreur est bien récupérée par le gestionnaire, qui écrit dans le fichier de logs ;
    • la fonction envoyer_mail() ne semble pas appelée par le gestionnaire pour informer l'administrateur.


    La troisième constatation est-elle normale ? S'explique-t-elle par le fait que PHP ne rappelle pas une fonction ayant généré une erreur ?
    Pour éviter une boucle en cas de plantage de envoyer_mail(), j'ai bien tenté l'interception d'une exception (cf. code ci-après), qui ne fonctionne pas puisqu'il s'agit d'une erreur. A part traiter les exceptions et les erreurs de manière séparée, voyez-vous une autre solution ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    function gerer_plantage($Code, $Message, $Fichier, $Ligne, $Contexte=array()) {
    /* Gestion des plantages (erreurs et exceptions)
    /* Prend en arguments :
    /*	- $Code, le code du plantage (entier)
    /*	- $Message, le message décrivant le plantage (chaîne)
    /*	- $Fichier, le fichier ayant géneré le plantage (chaîne)
    /*	- $Ligne, la ligne où s'est produit le plantage (entier)
    /*	- $Contexte, le contexte lors du plantage (tableau)
    /* Renvoie true si tout s'est bien passé, false sinon. */
     
    	/* Ecriture du message d'erreur dans les logs */
    	ecrire_log("Plantage n°$Code \"$Message\" à la ligne $Ligne", "E", basename($Fichier));
     
    	/* Envoi d'un mail à l'administrateur */
    	$Sujet = "Plantage n°$Code";
    	$Message = "Plantage n°$Code \"$Message\" à la ligne $Ligne dans le fichier $Fichier";
    	try {
    		if (envoyer_mail($Sujet, $Message, DESTINATAIRE_MAILS_ERR)) {
    			return true;
    		} else {
    			print formater_message("Echec lors de la gestion de l'erreur. Merci de prévenir l'administrateur à l'adresse " . DESTINATAIRE_MAILS_ERR . ".", "E");
    			return false;
    		}
    	} catch (Exception $Excep) {
    		print formater_message("Echec lors de la gestion de l'erreur. Merci de prévenir l'administrateur à l'adresse " . DESTINATAIRE_MAILS_ERR . ".", "E");
    		return false;
    	}
    }

  2. #2
    Membre Expert Avatar de RunCodePhp
    Profil pro
    Inscrit en
    Janvier 2010
    Messages
    2 962
    Détails du profil
    Informations personnelles :
    Localisation : Réunion

    Informations forums :
    Inscription : Janvier 2010
    Messages : 2 962
    Par défaut
    Salut

    La troisième constatation est-elle normale ? S'explique-t-elle par le fait que PHP ne rappelle pas une fonction ayant généré une erreur ?
    Pour éviter une boucle en cas de plantage de envoyer_mail(), j'ai bien tenté l'interception d'une exception (cf. code ci-après), qui ne fonctionne pas puisqu'il s'agit d'une erreur. A part traiter les exceptions et les erreurs de manière séparée, voyez-vous une autre solution ?
    Cela dépend du code qu'il y a dans la fonction envoyer_mail().
    Admettons que c'est la fonction mail(), c'est bien, elle renvoie FALSE (une erreur) et non une Exception.

    Pour attraper une Exception il faut que ce soit une Exception qui soit renvoyée en cas d'erreur.


    La gestion des erreurs et des Exceptions est un sujet assez vaste.


    Mais si tu souhaites faire cela simplement (ne serait-ce que dans un premiers temps), il y a peut être moyen.
    C'est peut être pas très zen mais c'est juste une piste.
    Admettons ceci dans la fonction envoyer_mail() :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?php
    envoyer_mail {
        // Du code ... etc ...
        $verif = @mail();
        if (!$verif) {
            throw new Exception('Erreur fonction mail()');
        }
        return true;
    }
    ?>
    (le @ n'est peut être pas nécessaire, faut voir).
    Théoriquement, le fait ici de lancer une exception celle devrait pouvoir être attrapée dans le catch dans la fonction gerer_plantage();

    Avant de lancer l'Exception, on peu récupérer le message d'erreur avec error_get_last() pour éventuellement le mettre dans le message.
    De même qu'on peu rajouter le message d'erreur avec $Excep->getMessage() (au niveau du catch).

    Du coup, en admettant que cela se passe ainsi, on peu prévoir comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    try {
        envoyer_mail($Sujet, $Message, DESTINATAIRE_MAILS_ERR);
    }
    catch (Exception $Excep) {
        print formater_message("Echec lors de la gestion de l'erreur. Merci de prévenir l'administrateur à l'adresse " . DESTINATAIRE_MAILS_ERR . ".", "E");
        return false;
    }
     
    return true;
    Sans garantie

    Sinon, il faudrait définir à la fonction set_error_handler() une fonction qui s'occuperait de gérer les erreurs, comme dans la doc par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function exception_error_handler($errno, $errstr, $errfile, $errline ) {
     throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
    }
    set_error_handler("exception_error_handler");
    Faut voir.

  3. #3
    Membre éclairé Avatar de scrouet
    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    280
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 280
    Par défaut
    En fait envoyer_mail() est conçue comme suit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function envoyer_mail($Sujet, $Message, $Destinataire, $Expediteur=EXPEDITEUR_MAILS) {
    	/* Initialisation des variables */
    	$PrefObj = "[PREF]";	/* Préfixe de l'objet */
     
    	/* Préparation du mail */
    	$Message = wordwrap($Message,70);
    	$EnTetes = "From: $Expediteur\r\n";
    	$EnTetes .= "X-Mailer: PHP/" . phpversion();
     
    	/* Envoi du mail et retour du booléen */
    	try {
    		return mail($Destinataire, $PrefObj . " " . $Sujet, $Message, $EnTetes);
    	} catch (Exception $Excep) {
    		gerer_exception($Excep);
    		return false;
    	}
    }
    Et pour les interceptions d'erreurs et d'exceptions :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /* Gestion des exceptions non interceptées */
    set_exception_handler("gerer_exception");
    set_error_handler("gerer_erreur");
     
    function gerer_exception($Exception) {
    	return gerer_plantage($Exception->getCode(), $Exception->getMessage(), $Exception->getFile(), $Exception->getLine(), $Exception->getTrace());
    }
     
    function gerer_erreur($Code, $Message, $Fichier, $Ligne) {
    	return gerer_plantage($Code, $Message, $Fichier, $Ligne);
    }
    Lorsque mail() se plante faute de pouvoir joindre le serveur SMTP, il n'y a pas d'exception levée. gerer_erreur() est appelée, puis gerer_plantage(), qui s'arrête au début du try (ou avant).

  4. #4
    Membre Expert Avatar de RunCodePhp
    Profil pro
    Inscrit en
    Janvier 2010
    Messages
    2 962
    Détails du profil
    Informations personnelles :
    Localisation : Réunion

    Informations forums :
    Inscription : Janvier 2010
    Messages : 2 962
    Par défaut
    /* Gestion des exceptions non interceptées */
    set_exception_handler("gerer_exception");
    set_error_handler("gerer_erreur");
    Donc tu as défini 2 fonctions pour ça.
    Puis vu que gerer_erreur() appel gerer_plantage(), toutes Erreurs et Exceptions seront alors gérées dans gerer_plantage(), si j'ai bien suivi.


    Le truc que je remarques c'est qu'en cas d'erreurs il est prévu d'envoyer un mail.
    Tu dois tout de même te rendre compte que cela peu être très lourd, énormément même, peut être même que ça peut très bien être cette gestion d'erreurs et d'Exceptions en être la cause d'un plantage, ce qui peut être un comble.
    Disons il faut le savoir.

    De plus, vu qu'il y un try/catch dans cette fonction gerer_plantage(), et bien en cas d'erreur dans celle-ci on est pas loin d'une boucle infinie.
    Il y a là peut être un point à éclaircir.


    Mise à pas ça, théoriquement tu devrais rien avoir à faire, aucun try/catch autour de la fonction mail() du fait qu'en cas d'erreur la fonction gerer_erreur() devrait être déclenchée.

    En essayant d'attraper cette erreur (donc un try/catch) c'est pour pouvoir gérer cette erreur autrement que par le gestionnaire que tu as prévus pour ça (gerer_erreur).
    Or, ce n'est pas ton but apparemment.

    En supprimant tous les try/catch, est-ce que le message d'erreur et enregistré (ecrire_log), et un mail envoyé ? (en cas d'erreur sur mail() bien sûr)
    (faut espérer qu'il n'y ait pas d'erreur dans gerer_exception).

  5. #5
    Membre éclairé Avatar de scrouet
    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    280
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 280
    Par défaut
    Après avoir enlevé tous les blocs try/catch, mes fonctions ressemblent à ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    function gerer_plantage($Code, $Message, $Fichier, $Ligne, $Contexte=array()) {
     
    	/* Ecriture du message d'erreur dans les logs */
    	ecrire_log("Plantage n°$Code \"$Message\" à la ligne $Ligne", "E", basename($Fichier));
     
    	/* Envoi d'un mail à l'administrateur */
    	$Sujet = "Plantage n°$Code";
    	$Message = "Plantage n°$Code \"$Message\" à la ligne $Ligne dans le fichier $Fichier";
    	if (envoyer_mail($Sujet, $Message, DESTINATAIRE_MAILS_ERR)) {
    		return true;
    	} else {
    		print formater_message("Echec lors de la gestion de l'erreur. Merci de prévenir l'administrateur à l'adresse " . DESTINATAIRE_MAILS_ERR . ".", "E");
    		return false;
    	}
    }
     
    function envoyer_mail($Sujet, $Message, $Destinataire, $Expediteur=EXPEDITEUR_MAILS) {
     
    	/* Initialisation des variables */
    	$PrefObj = "[PREF]";	/* Préfixe de l'objet */
     
    	/* Préparation du mail */
    	$Message = wordwrap($Message,70);
    	$EnTetes = "From: $Expediteur\r\n";
    	$EnTetes .= "X-Mailer: PHP/" . phpversion();
     
    	/* Envoi du mail et retour du booléen */
    	return mail($Destinataire, $PrefObj . " " . $Sujet, $Message, $EnTetes);
    }
    La gestion du plantage de mail() se fait toujours via gerer_erreurs(), le message est bien écrit dans la log par gerer_plantage() mais c'est tout.

    Par ailleurs, j'ai les erreurs suivantes dans php-errors.log :
    [13-Apr-2012 12:20:26 UTC] PHP Warning: mail(): Failed to connect to mailserver at "smtp.neldoreth.fr" port 25, verify your "SMTP" and "smtp_port" setting in php.ini or use ini_set() in mon_fichier.php on line 101

    [13-Apr-2012 12:20:26 UTC] PHP Fatal error: Maximum execution time of 30 seconds exceeded in mon_fichier.php on line 101

  6. #6
    Membre Expert Avatar de RunCodePhp
    Profil pro
    Inscrit en
    Janvier 2010
    Messages
    2 962
    Détails du profil
    Informations personnelles :
    Localisation : Réunion

    Informations forums :
    Inscription : Janvier 2010
    Messages : 2 962
    Par défaut
    La gestion du plantage de mail() se fait toujours via gerer_erreurs(), le message est bien écrit dans la log par gerer_plantage() mais c'est tout.
    La fonction gerer_plantage() doit peut être "planter".

    A mon sens il faudrait éviter d'utiliser la fonction envoyer_mail() dans la fonction gerer_plantage(), utiliser directement la fonction mail() serait (peut être) mieux.
    (j'y vois un éventuel problème de boucle infinie sinon).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    [13-Apr-2012 12:20:26 UTC] PHP Fatal error: Maximum execution time of 30 seconds exceeded in mon_fichier.php on line 101
    Les erreur Fatal ne peuvent pas être gérées par un gestionnaire personnalisé, elles sont à juste titre trop grave, donc Php stoppe tout à ce moment.

    Ici tu as dépasser la limité d'exécution autorisé (de 30 secondes), c'est normal que tout s'arrête à ce moment.
    Tu ferais trop de traitements (trop d’envoi de mail, ou boucle trop longue, etc ...).
    Qu'est-ce qui est fait autout de cette ligne 101 de mon_fichier.php ?


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    else {
    	print formater_message("Echec lors de la gestion de l'erreur. Merci de prévenir l'administrateur à l'adresse " . DESTINATAIRE_MAILS_ERR . ".", "E");
    	return false;
    	}
    Tu devrais éviter de faire cette de code dans cette fonction gerer_plantage(), car si tu envoie des mail dans une partie où il ne faut pas de renvoie de contenu (comme avant un header par exemple) tu vas provoquer une erreur.
    Provoquer une erreur dans son propre gestionnaire d'erreur c'est contre productif (c'est pas logique).
    Faut faire au plus simple, juste enregistrer l'erreur.

    Le message pour les utilisateur fais le plutôt dans la fonction envoyer_mail(), suffit de récupérer la valeur de mail().
    Si false -> message à l'utilisateur.


    A coté de ça tu pourrais faire comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    function gerer_error ($errno, $errstr, $errfile, $errline) {
        throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
    }
    On renvoi directement toute erreurs vers ErrorException(), ce qui fait que gerer_plantage() va récupérer l'Exception.
    Toute erreur sera une Exception.

Discussions similaires

  1. Interception erreurs e-mails
    Par scrouet dans le forum Langage
    Réponses: 2
    Dernier message: 23/08/2009, 13h52
  2. [Mail] rapport d'erreur fonction mail()
    Par yanice dans le forum Langage
    Réponses: 3
    Dernier message: 05/04/2007, 16h31
  3. [Configuration] message d'erreur de mail
    Par arezki76 dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 1
    Dernier message: 27/03/2007, 16h00
  4. [Exception PHP] Interception Erreur WebServices
    Par yokoPim dans le forum Langage
    Réponses: 2
    Dernier message: 12/09/2006, 16h46
  5. [Mail] récupérer les code erreurs de mail()
    Par Djakisback dans le forum Langage
    Réponses: 2
    Dernier message: 22/03/2006, 18h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo