Gestion des erreurs 2 : plus poussé ^^

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : Gestion des erreurs 2 : plus poussé ^^

par rami » 20 juin 2006, 13:23

Tout à fait, je n'aurais pas dit mieux ;)

par sadeq » 20 juin 2006, 08:48

imagine que t'as 2 traitements parallèles totalement indépendants quant à la séquence des tâches nécessaires pour atteindre un objectif mais qui se partage un ensemble de données. La question est comment rendre possible ce partage? La réponse est de localiser les données à partager dans un support partageable qui peut être : une variable globale voir super-globale, un fichier, une base de données ect...

L'exemple de Rami en est un :
Ce qu'il appelle Vue est un traitement qui ne fait qu'afficher une donnée (Message) qui se trouve dans la variable super-globale $_SESSION qui est la session. Peu importe la valeur de cette variable, la Vue l'affiche toujours.
D'autres parts, plusieurs traitements indépendants peuvent agir sur le contenu de la variable partagée (Message)

par Sertt » 19 juin 2006, 23:51

Si j'ai bien compris, tu n'utilises pas de try pour tes erreurs fonctionnelles puisqu'elles sont "attrapées" par ton objet Vue.

Est-ce que tu pourrais néanmoins éclaircir ce point-ci :
[quote"Rami"]Lorsqu'une méthode génère une erreur fonctionnelle, ou un message d'informations à l'utilisateur (opération réussie, prise en compte d'une action...), je le mets en session et je redirige / stop l'exécution. La vue regarde automatiquement si un message est en session. Elle l'affiche puis l'efface de la session. [/quote]

Donc, si je résume, une erreur est casée dans l'object session, mais j'ai du mal à saisir la suite de la procédure.

D'avance merci,

par rami » 18 juin 2006, 14:20

Le fait de découper ne rajoute pas de la complexité au code, au contraire. Il permet de découper l'algorithme de base assez complexe, en fonction ayant un but unique, donc simple à comprendre. Il est facile de comprendre un code qui fait :
if(ajout_possible())
   if(contraintes_respectées())
       upload()
De plus, imagines le jour où tu souhaites ajouter un type d'image par exemple. Tu ne touches pas à ton algorithme général, juste à la fonction qui correspond à la vérification du type. D'une, tu limites les effets de bords de la modification, deux tu n'as juste qu'à modifier un bout de code très facile à comprendre.
Après, c'est aussi une question de goût. Sur un projet où tu es le seul à developper, c'est sûr que cela a moins d'importance, mais sur un projet où plusieurs pesonnes participent, et surtout maintiennent, cela prend tout son intérêt.

Pour revenir à ta question initiale, voici comment je fais pour gérer les erreurs utilisateurs. Note que je travaille exclusivement en objet, mais que c'est facilement convertible en proédurale.

J'utilise :
  • - un objet encapsulant la session PHP
    - un objet Message correspondant au message d'erreur / informations
    - un objet Vue qui se charge de gérer la sortie de l'application
    - je passe les objets Controller,Router...
Dans ma session, j'ai un attribut qui correspond au message destiné à l'utilisateur (qui est évidemment du type Message). Lorsqu'une méthode génère une erreur fonctionnelle, ou un message d'informations à l'utilisateur (opération réussie, prise en compte d'une action...), je le mets en session et je redirige / stop l'exécution. La vue regarde automatiquement si un message est en session. Elle l'affiche puis l'efface de la session.

Je sais pas si c'est très clair, n'hésites pas si ce n'est pas le cas.

par insomniak » 18 juin 2006, 00:10

Arf, je me doutais que tu allais dire ça... :(

D'abord, je tiens à préciser que certes j'aurais pu modifier mes if pour alleger tout ca, mais c'etait pour l'exemple. Il y a d'autres traitements qui sont aussi lourds voir plus lourds en conditions.

Pour ce qui est de la fonction, je ne suis pas trop pour en fait... je trouve que ca allourdit la compréhension du code pour pas grand chose, que tes if soient dans le code ou a part dans une fonction, ca reste à peu pres pareil (entre guillemets bien sur, j'ai bien vu ou tu voulais en venir).

En fait, ma question pour la résumer, c'est : "comment faites vous pour gérer vos erreurs quand vous gérez des cas particuliers d'utilisation de votre site (donc uniquement les erreurs utilisateurs quoi) sachant que vous devez stopper un bout de script après ce cas particulier sans pour autant stopper tout le script" ?

Encore merci en tout cas !

par rami » 17 juin 2006, 21:41

D'une part tu peux découper tes méthodes (ou fonctions) par rafinement successif. D'autre part, tu peux regrouper des conditions, voire même en supprimer.

function ajout_image_possible()
{
if(jai pas plus de 4 images dans ma banque d'images et ma 4eme image est vide)
    if(variable $_FILES[mon fichier] pas vide && pas d'erreur)
    sinon
      return "erreur d'upload"
  sinon
    return "vous avez deja 4 images"
}



function contrainte_image()
{
  if(fichier uploadé)
        //la condition suivante suffit
       //if(fichier uploadé est une image)
          if(fichier uploadé est un gif ou un jpeg)
            if(sa resolution x < 2500 et sa resol y < 1950)
              alors fais le traitement
            sinon
              return "resol trop elevée"
          sinon
            return"ce n'est pas un gif ou jpeg"
   sinon
        affiche "fichier pas uploadé"
} 

par insomniak » 17 juin 2006, 20:53

re !

comme je suis de retour et que j'ai un peu de temps devant moi, voici l'explication de mon truc :

J'ai un fichier index.php qui gere les modules qui sont affichés sur ma page.
un parametre dans l'url dit quel module doit etre chargé. Sans parametre il include le fichier main.php

Dans main.php, j'affiche moult choses donc plusieurs parties et plusieurs objets qui n'interferent pas entre eux. Imaginons qu'un objet crash en cours de route. Il faudrait qu'uniquement cette partie merde et pas le reste.
Maintenant, dans les objets si je leve une exception de type erreur de soft, ok, je met en vrac la page, ca ca me semble logique. Donc pour la partie crash d'objet normal pour moi c'est acquis y'a pas de lezard.

Par contre pour ce qui est des erreurs autres (admettons, lors d'un upload, je teste si l'upload est une image de type gif ou jpeg) là je galere. Car avec un systeme d'erreur lambda je dois faire des tests if.
Pour te citer le pire exemple que je puisse te donner, voici un algo de ce que je fais dans la partie upload :
if(jai pas plus de 4 images dans ma banque d'images)
  if(ma 4eme image est vide)
    if(variable $_FILES[mon fichier] pas vide && pas d'erreur)
      if(fichier uploadé)
        if(fichier uploadé est une image)
          if(fichier uploadé est un gif ou un jpeg)
            if(sa resolution x < 2500 et sa resol y < 1950)
              alors fais le traitement
            sinon
              affiche "resol trop elevée"
          sinon
            affiche "ce n'est pas un gif ou jpeg"
        sinon
          affiche "ce n'est pas une image"
      sinon
        affiche "fichier pas uploadé"
    sinon
      affiche "erreur d'upload"
  sinon
    affiche "vous avez deja 4 images"
sinon
  affiche "vous avez dépassé la limite de 4 images"
      
Comme tu peux le constater ca donne pas mal de lignes pourrav avec une indentation en montagne.... bref, c'est pas top.
Avec un systeme de throw, je dirais que le probleme de l'indentation n'est plus, mais par contre, c'est plus vraiment dans la logique de developpement. De plus, pour afficher l'erreur à l'utilisateur sans trop perturber la suite c'est pas top...

Comment ferais tu toi?
Là je dois dire que je m'embrume le cerveau avec tout ca ^^

Pour l'exemple que tu viens de citer, pas de probleme ca c'est acquis pour moi.

Merci d'avance

par rami » 17 juin 2006, 20:43

Généralement, le code de mes méthodes ne dépassent guère 30 ou 40 lignes, ce qui fait que je n'ai pas énormément de tests à effectuer. Il arrive parfois que ce soit le cas, alors j'utilise le principe suivant :
<?php
/**
 * Méthode classique plutôt courte
 */
function test($int)
{
  if(!is_int($int))
    throw new BadArgumentTypeException('Argument de type entier attendu');
 //ici le traitement à effectuer
}

/**
 * Méthode plus longue
 */
function toto($int, $array)
{
   try
  {
       //traitements longs à effectuer
  }
  catch(Exception $e)
  {
       throw new Exception('Erreur dans '.__METHOD__);
  }

}
}

Tu captures les exceptions dans un blocs try / catch, et dans le catch, tu lances une exception. Cette pratique est du coup un peu moins précise quant à l'exception levée car tu ne peux pas savoir où ca a crashé. Cependant, cela peut éviter d'avoir à faire un nombre rébarbatif de tests.

par insomniak » 17 juin 2006, 19:49

re Rami

Pour ce qui est de ne pas confondre exception et erreurs utilisateur, je suis d'accord avec toi. Seulement, j'ai un probleme avec le code, comme j'ai expliqué, si je dois tester tous les retours pour savoir si oui ou non je dois continuer le code, ca me donne une cascade phenomenale de if...
Le try catch m'aurait permis de gérer cela sans if.
Maintenant dans un soucis de logique de developpement, comment fais tu toi pour eviter cela ? (Cf exemple au dessus, consideres seulement que les methodes de la classe renvoient juste true ou false et que le false est une erreur utilisateur)

Merci !

ps : je re plus tard pour t'expliquer plus precisement le fonctionnement de mon site car je dois bouger là

par rami » 17 juin 2006, 18:00

Concernant ta méthode Rami : avec des exceptions BadTypeException, NullPointerException, elles sortent d'ou ces exceptions ? ce sont des classes dérivées de Exception ou bien tu utilises celles de php ?
J'utilise les exceptions de la SPL plus certaines que j'ai créé en dérivant la classe de base Exception. Elles ne font rien du tout, elles permettent seulement d'identifier quelles types d'erreur a entraîné l'exception.

Je ne suis pas sûr de saisir le fonctionnement de ton application. Peux-tu détaillé le fonctionnement de celle-ci?

En ce qui concerne la suite de ton message, je ne suis pas tout à fait d'accord. Pour moi, toute erreur impliquant que l'application ne puisse pas remplir sa fonction entraîne la levée d'une exception. Par exemple, un identifiant null ou égal à 0, un chemin qui n'existe pas, une connexion à une base impossible...

Si l'erreur provient d'une donnée extérieure (saisie d'un utilisateur le plus souvent), aucune exception n'est levée car cela ne cause pas le dysfonctionnement de l'application.

Il ne faut pas confondre erreurs de fonctionnement de l'application - qui doivent alors lever des exceptions - et erreurs fonctionnelles, qui doivent alors générer un message d'erreur à l'utilisateur.

Cependant, il est possible de gérer les erreurs fonctionnelles avec des exceptions. Il faut alors discrimer les exceptions "fatales", des exceptions "fonctionnelles". Il est possible de faire cela en dérivant Exception et en ajoutant un attribut. Je ne suis pas partisan de ce type de manipulation car , pour moi, les exceptions correspondent à un dysfonctionnement de l'application.

Je préfère passer par une classe qui gère les erreurs, ou bien par une variable de session. pour gérer les erreurs fonctionnelles.

par insomniak » 17 juin 2006, 17:19

Ahhhh ca devient interressant tout ca !

Lorenzo : je pense que Rami a raison car php5 est une version POO et en POO dans les autres langages (non web) on utilise les exceptions. Je pensais au départ que tu les utilisais, sinon je ne t'aurais pas demandé un exemple ^^. C'est justement pour éviter de galerer avec ce genre de systeme que je m'oriente vers les exceptions. Beaucoup plus simple, beaucoup plus flexible et surtout une gestion des erreurs bien plus accrue (notamment en terme de détails de bugs...). Désolé Lorenzo.

Rami : Effectivement je me situe dans ta situation : MVC + Exceptions.
Seul détail qui change : j'ai une étape intermédiaire par rapport a ce que tu viens d'expliquer.

Je suis plutot comme ceci :

index.php -> module.php -> classe.php

Mon soucis pour le moment est de savoir comment BIEN gerer les exceptions. Car les utiliser je sais faire, mais bien les utiliser, je n'en suis pas sur. C'est surtout ca l'objet de ma question.

Concernant ta méthode Rami : avec des exceptions BadTypeException, NullPointerException, elles sortent d'ou ces exceptions ? ce sont des classes dérivées de Exception ou bien tu utilises celles de php ?

Ensuite, j'avais pensé faire un truc, car gérer les exceptions uniquement au niveau de index.php est assez restrictif : admettons qu'un bug survienne dans une classe qui va servir uniquement à afficher un pauvre truc tout bidon dans une page mais qui ne gene en rien le reste. Beh si tu geres au niveau de ton index.php, le reste du code de module.php n'est pas executé donc en gros tu mets en rade toute une page pour finalement un petit bouiboui de rien du tout.

En reflechissant, je me suis dit que je pourrais éventuellement faire un truc interressant :

Faire 3 classes d'exceptions differentes héritant de Exception :

- une classe UserError
- une classe CodeError

La classe UserError serait donc catchée dans le module.php et ne zigouillerai pas toute la page, mais afficherai uniquement un message d'erreur à la place de ce qui aurait du etre affiché.

La classe CodeError qui serait elle catchée dans l'index.php et qui là causerait l'arret de la page.

Comme cela, on pourrait éviter quelques petits désagréments non ?
Qu'en pensez vous ?

Merci de vos participations en tout cas
@++

par Lorenzo » 17 juin 2006, 17:16

c'est exactement ce que je fais avec la classe erreur sauf que je n'ai plus a m'en occuper quand je programme une appli donc : temps gagné + facilité + presentation propre car - de code ...etc

par rami » 17 juin 2006, 17:00

Faire hériter toutes ses classes d'une classe destinée à gérer les erreurs est une erreur de conception à mon sens.

Pourquoi ne pas utilisée les exceptions? Elles sont faites pour cela.

Voici comment je les utilise. Je me place dans un contexte MVC, ce qui signifie que toute requête passe toujours par le même point d'entrée de mon application (généralement index.php).


Dans toutes les méthodes de classe, je teste les types des arguments passés, et je lance une exception spécifique (BadTypeException par exemple) s'ils ne correspondent pas au type attendu. De même, lors d'un accès à une ressource, je lance une exception si elle n'existe pas ou est occupée. Lorsqu'une varaible est null alors qu'elle ne devrait pas, je lance une exception du type NullPointerException, etc.

Comme je fais des "throw", ces exceptions "remontent" jusqu'au point d'entrée de l'application. Là, 2 choix sont possibles :

- faire un try / catch général et gérer les erreurs dans le bloc catch
- définir son propre gestionnaire d'exception

Cela permet de faire du log afin de tracer les erreurs, de personnaliser les erreurs, de centraliser la gestion de ces erreurs, de basculer entre un mode développement (où on affiche toutes les informations pour débugguer) et un mode production, etc.


J'espere t'avoir espérer aider un peu ;)

par Lorenzo » 17 juin 2006, 16:36

sur un cas similaire ca va etre dur vu que je n'ai jamais eu a faire une classe picture ou ressemblant ...

comme je l'ai dit plus haut : TOUTES mes classes descendent de ERROR.
donc dans chaque methode importantes (pas celle pour une simple lecture ou ecriture d'une var) j'appelle la methode erGestion()

voici un exemple pour la methode de la classe REP (repertoire) qui crée un pointeur sur un repertoire :

	/*** ---- CREATION POINTEUR ----
	*
	* Creer un pointeur sur le repertoire $stCheminRep
	*
	* (booléen) TRUE | FALSE | coupe le script
	*/
	function repPointeur($stCheminRep){
		$this->stCheminRep = $this->repModif($stCheminRep);
		$this->rsPtRep = @opendir($this->stCheminRep);
		if( !$this->rsPtRep ){
			return $this->erGestion(
				"class Rep->repPointeur() :<br>Erreur lors de la création du pointeur sur le répertoire.",
				"class Rep->repPointeur() :<br>Erreur lors de la création du pointeur sur le répertoire.<br>".$this->stCheminRep."<br>".@$php_errormsg
			);
		}
		return true;
	}
si je passe 2 parametres différents a erGestion() c'est juste que ca correspond a : erreur simple (production) ou complete (developpement)

par insomniak » 17 juin 2006, 16:05

Tu m'interresses justement ! j'aimerai savoir comment tu procèdes dans un cas concret s'il te plait.
Pour que je comprenne encore mieux comment tu te servirais de cela, pourrais tu me donner une sorte d'exemple sur un cas similaire au miens s'il te plait ?

Genre, un index.php qui include un module.php qui utilise une classe.php
Sachant que dans module.php les actions avant et apres le traitement utilisant la classe ne sont pas toutes liées à ta classe et donc devront peut etre etre executées.

Merci d'avance, tu me serais d'un grand secours ;)
@+