Page 1 sur 1

Un error_handler qui respecte le niveau d'erreur

Posté : 01 sept. 2009, 22:57
par Rem73
error_handler permet de cour-circuiter le gestionnaire de PHP pour faire le votre, ça permet d'envoyer un mail contenant l'erreur à la place d'afficher l'erreur etc.

Cependant vous perdez le contrôle sur les erreurs, vous prenez toutes les erreurs même si elles sont préfixées de l'@ ou que vous avez désactivé les erreurs avec error_reporting() (ou depuis le php.ini).

Je vais donc vous donner le code du error_handler parfait, enfin qui rapporte pas une erreur quand il y a l'@ ou quand les erreurs sont désactivées (ou une seule erreur est désactivée).

Comment mettre en place error_handler :

error_handler et le nom de la fonction qui va se charger des erreurs PHP. Cette fonction va remplacer le système d'erreur de PHP. Vous êtes pas obligé de nommer votre fonction "error_handler", mais pour que vous comprenez mieux je vais l'appeler comme ça.
Pour définir la fonction qui va donc remplacer le gestionnaire d'erreur il faut utiliser set_error_handler, vous renseignez en argument le nom de la fonction qui va être utilisée pour gérer les erreurs PHP.

Voilà donc comment on dit à php d'utiliser tel fonction à la place du gestionnaire d'erreur de PHP, mais il faut savoir que les erreurs les plus critique vont rester à la charge de PHP, pour plus d'information la documentation.

Code du error_handler :
function error_handler($errno, $errstr, $errfile, $errline, $errcontext)
{
	if (error_reporting() & $errno) 
	{ 
		switch ($errno)
		{
		    case  E_ERROR:
		    case E_USER_ERROR :
		        $erreur = 'FATAL ERREUR';
		        $fatal = TRUE;
		    	break;
			
		    case  E_WARNING:
		    case E_USER_WARNING:
		    	$fatal = FALSE;
				$erreur = 'WARNING';
		    	break;
		
		    case  E_NOTICE:
		    case E_USER_NOTICE:
		    	$fatal = FALSE;
		        $erreur = 'NOTICE';
		    	break;
		    	
		    /*case E_DEPRECATED:
		    case E_USER_DEPRECATED:
		    	$erreur = 'DEPRECATED';
		        $fatal = FALSE;
		    	break;*/
			
			default:
				$erreur = 'Unknown';
				$fatal = FALSE;
				break;
		}
		    
		if (ini_get('display_errors'))
		{
			echo sprinf('Une erreur PHP de type <b>%s</b> est survenue.<br />Erreur n°%d : %s.<br />Dans le fichier %s, à la ligne %d', $erreur, $errno, $errstr, $errfile, $errline);
			if ($fatal)
				exit(0);
		}
				
		if (ini_get('log_errors'))
		{
			error_log(sprintf("PHP %s:  %s in %s on line %d", $erreur, $errstr, $errfile, $errline));
		}
	}
	else
		return false;
}
Explication :

Ça mérite quelques explications je pense, enfaite tout réside dans cette ligne
if (error_reporting() & $errno) 
le reste c'est du bonus.

Avant de vous expliquer ce qu'elle fait, il faut savoir que PHP à plusieurs type d'erreur et ils est possible de désactiver certaines erreurs ou d'en activer certaines. Je pense que E_ALL vous dis quelques chose, cette constante contient un nombre qui veux dire que toutes les erreurs sont activées sauf E_STRICT. Cette valeur est enfaite un nombre binaire : 1011111111111 pour E_ALL (en PHP 5.3) et 100000000000 pour E_STRICT. Enfaite l'intérêt de mettre ça en binaire c'est que chaque chiffre comporte l'état d'une erreur PHP. Donc vous pouvez avoir toutes les possibilitées (E_STRICT désactivé mais E_NOTICE activé ...) et c'est très simple de gestion. Si vous voulez plus d'informations sur toutes ces erreurs : la doc, attention cependant la valeur des constantes sont affichées en décimal et il faut savoir que PHP ne gère pas le type de variable binaire (si c'est pas le cas je suis preneur).

Bon maintenant on va s'attarder sur le & c'est un opérateur binaire qui va comparer dans notre fonction, le niveau d'erreur et l'erreur provoquée. L'erreur provoquée comporte une seule fois le chiffre 1. Donc la conditions c'est si le chiffre 1 de $errno est aussi a 1 dans error_reporting() (on va faire simple, le binaire c'est compliquer à expliquer). Et donc grâce à cette ligne si vous désactivez le E_STRICT et une erreur de type E_STRICT est présente, la fonction error_handler ne fait rien et donc l'erreur n'est pas prise en compte. Si vous voulez plus d'informations sur le binaire : la doc, ça vous permettra de savoir définir dans le php.ini ou avec error_reporting() tout les niveaux d'erreur, pour info toutes les erreurs c'est E_ALL & E_STRICT.

Plus d'info sur les bonus :

J'ai aussi inclue les erreurs USER, c'est des erreurs qui sont produites par l'utilisateur grâce à la fonction trigger_error(), c'est souvent utilisé dans les librairies telle que Smarty, PEAR...

J'ai désactivé les erreurs E_DEPRECATED parce que c'est possible que depuis PHP 5.3 et moi je travail en 5.2 (sur ubuntu). Donc si vous êtes sur PHP 5.3 il faut dé-commenter cette partie.

Le code vérifie dans la config de PHP si on doit afficher les erreurs, ça permet une meilleur intégration a PHP, pareil pour les logs.

J'utilise des conditions sans parenthèse pour gagner de la place, pour ceux qui le savent pas, on est pas obligé de mettre des parenthèses mais si on n'en met pas la boucle, conditions, fonction comportera juste la ligne suivante. C'est à dire
if (1==1)
{
echo 'tout va bien';
}
// Peut être codé comme ce-ci
if (1==1)
echo 'tout va bien';
Je n'ai pas inclue de ligne pour envoyer l'erreur par mail tout simplement parce que dans mon code j'utilise la librairie PEAR et puis sans sécurité vous allez vous faire spammer et vous faire bannir de votre serveur SMTP au pire des cas (et oui les personnes qui laissent enfoncer la touche F5 vont vous en envoyer des paquet de mails et le serveur SMTP qui va gérer tout ça va un moment vous bannir, ça m'est arriver chez free - j'avais une erreur dans la librairie de mail et donc ça rappelait la fonction error_handler qui appelait la librairie mail qui plantait etc -).

Voilà j'espère que ça vous sera utile et que vous avez compris comment accéder à ce résultat.

Re: Un error_handler propre

Posté : 01 sept. 2009, 23:15
par zeus
Je vais donc vous donner le code du error_handler parfait, enfin qui rapporte pas une erreur quand il y a @ ou quand les erreurs sont désactiver (ou une seule erreur est désactiver).
Selon moi, le @ ne devrait pas être utilisé car cacher les erreurs sous le tapis est une mauvaise pratique. Remonter ce genre d'erreur est donc une bonne chose.

Toutefois, bravo pour ton travail. :pouce:

Re: Un error_handler propre

Posté : 01 sept. 2009, 23:28
par Rem73
L'utilisation du @ est obligatoire pour certaines choses : quand tu veux afficher un message spécifique à une erreur et non sur une erreur PHP c'est très utile.
Pour mes session personnalisée j'utilise pour aller chercher le fichier (il peux avoir l'extension 1 ou 0) et faire une recherche à coup de glob c'est pas la bonne solution, et faire un file_exists est "lourd" pour aucun avantage.
Etc ...
Enfin c'est des cas particulier, c'est vrai qu'il vaut mieux éviter de cacher les erreurs.

L'astuce c'est surtout que ce code respecte le niveau d'erreur que vous avez spécifiez, j'en ai passer du temps à rien y comprendre avec mon error_handler qui aspirait tout les erreurs et mon gestionnaire d'erreur PHP qui sortait juste ce qu'il fallait.

En tout cas merci :D

Re: Un error_handler propre

Posté : 01 sept. 2009, 23:32
par zeus
le système de codage par l'erreur est quelques fois pratique (le INSERT UPDATE en est un bon exemple), mais il a ses limites.

Re: Un error_handler propre

Posté : 02 sept. 2009, 03:08
par Nagol
marchera pas entièrement

tu peux pas catcher un fatal error dans set_error_handler sans utiliser register_shutdown_function :(

Re: Un error_handler propre

Posté : 02 sept. 2009, 11:49
par Hywan
Hey :-),

Ensuite, l'usage de l'arobase est utile lorsqu'on veut surcharger des fonctionnalités de PHP. Par exemple, si on écrit notre propre flux et notre propre protocole. On remarquera qu'on doit gérer les erreurs à travers des variables booléens, du genre : &$error que l'on doit passer à true, pour que PHP qui chapotte tout ça puisse gérer nos erreurs proprement. Donc on doit en éteindre pour nos tests et lui fournir le résultat.

Enfin bref, c'était une petite aparté ;-).

Re: Un error_handler propre

Posté : 02 sept. 2009, 13:06
par Rem73
marchera pas entièrement

tu peux pas catcher un fatal error dans set_error_handler sans utiliser register_shutdown_function :(
Si je me trompe pas les FATAL ERROR sont prise en charge (je crois en avoir eu). Mais ça se peux que je me suis tromper puisque dans la doc : E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING sont les erreurs non prise en charge. Enfin pour produire ces erreurs il faut faire des fautes de frappes (E_PARSE, E_COMPILE_ERROR) pour ce qui est du E_ERROR je sais plus l'erreur type. Et les CORE je les connais pas mais je pense qu'il faut y aller ^^
Donc les erreurs non prise en charge sont la plus part du temps déceler depuis votre IDE.