OVH Cloud OVH Cloud

Le debat du mois : gestion des formulaires

18 réponses
Avatar
John Gallet
Bonjour/bonsoir,

Du temps de fciwap, on faisait parfois un mini débat sur un thème classique
et si des choses intéressantes en ressortaient, je faisais un résumé pour la
FAQ. Je relance l'idée, on verra bien si ça intéresse les foules.

Le sujet du moment : vous devez gérer un formulaire avec deux variables
obligatoires, une facultative. Si les deux obligatoires sont reçues, vous
faites un insert en sgbdr. A noter que l'une des variables est clef primaire
de la table histoire de compliquer un peu. Sinon vous regénérez le
formulaire en préremplissant les champs déjà saisis. Comment faites vous ça
proprement (avec séparation code / présentation maximale, sans pour autant
utiliser des templates, avec gestion d'erreurs élégantes pour l'internaute
etc...).

a++
JG

8 réponses

1 2
Avatar
Thibaut Allender

<label for="var1">Var 1</label><span class="renvoi">*</span>
<input type="text" name="var1" id="var1"><br>


id ? Ca sert à quoi ça ?


le parametre "for" de <label> a besoin d'un id de reference
le name sert pour poster la variable, l'id sert pour le label

Et les valeurs déjà saisies par l'internaute quand il en manque d'autres
obligatoires ?


a la trappe ;)

--
freelance + web|system developer|designer
+ 32 496 26 75 76 + http://www.capsule.org


Avatar
Nicklas
Le Wed, 25 Feb 2004 23:21:22 +0000, John Gallet a écrit :

Bonsoir,

Je sépare mon code PHP de mon code HTML.
Mais je n'utilise pas de système de template, je trouve que cela
ne sert à rien. PHP est lui-même un moteur de template...


C'est une vision simplifiée mais ce que j'appelle couramment le "template du
pauvre"(c)(tm). je te rejoins sur le fait que, quelle que soit la manière
dont on le tourne, il faut bien nommer sa donnée et indiquer où on eut
qu'elle soit affichée dans le canevas html statique. Partant de là, la
solution la plus simple est de faire un <?php echo $nom_variable;?> là où il
faut dans le canevas html (je n'ai pas trop confiance dans la version courte
que tu utilises, j'ai eu des réultats "marrants" après qu'un infographiste
eut décidé d'ouvrir un fichier de ce genre sous son éditeur préféré, mais le
principe est rigoureusemen identique).


template du pauvre... ou de celui qui ne veut pas perdre des précieuses
ressources processeurs. Ou encore celui qui ne veut pas ou ne peut pas
imposer une solution de template à apprendre aux graphistes, qui ont
déjà du mal avec PHP.

$query = "INSERT INTO ....";
$result = mysql_query($query);



Comment ça pas de gestion d'erreur ? Mais c'est mal ! ;-)


Si, forcément, il y a toujours une vérification. Mais comme c'est
trivial, je ne l'ai pas mis.

$query = "....";
$result = @mysql_query($query);

if ($result...

include('page.html');
?>
Fichier page.html :


NB pour les lecteurs qui se poseraient la question : là on par du
principe que l'extension .html est parsée par php, ce qui n'est pas le
cas partout, mais il suffit de nommer ce fichier page.php est c'est
pareil.


Oui et non, vu que l'on fait un include, la gestion des extensions ne
sert à rien. On peut mettre ce que l'on veut.

if ($name == $value)
if (($name === 0) || ($value === 0))
return ((string) $name === (string) $value) ? 'checked' : '';


Moi j'aime pas jouer à vérifier aussi le type, mais c'est perso et
aussi lié à mes fonctions de filtrage.


Je ne joue pas souvent avec, mais j'en ai parfois besoin.

--
Nicklas
ProgWeb - Développer son site Internet
http://www.progweb.com


Avatar
John Gallet
Re,

Si, forcément, il y a toujours une vérification. Mais comme c'est
trivial, je ne l'ai pas mis.



Trivial, trivial... si tu savais ce qu'on lit comme code php... :-

Oui et non, vu que l'on fait un include, la gestion des extensions ne
sert à rien. On peut mettre ce que l'on veut.



Oui bien sûr tu as raison. De la non utilisation des .inc pour raisons de
sécurité, oeuf corse.

a++
JG

Avatar
Guillaume Bouchard
Nicklas wrote:

template du pauvre... ou de celui qui ne veut pas perdre des précieuses
ressources processeurs. Ou encore celui qui ne veut pas ou ne peut pas
imposer une solution de template à apprendre aux graphistes, qui ont
déjà du mal avec PHP.


Sera beni l'epoque ou il existera une technologie web permettant de
séparer le contenu de la presentation.
Il suffira aux programmeurs de faire leurs code generant une page avec
des données structurées et aux graphistes de faire leurs designs en
fonction de la structure des données aux quels il savent devoir s'attendre.
Cela deviendra tellement bien que le graphiste n'aura pas besoin
d'appeler le programmeur à la rescousse à 23 heures un dimanche soir
parce que en voulant ajouter du rouge sur un liens il a tout fait merder.
Cela semble tellement parfait que on se demande pourquoi personne ne l'a
pas deja inventer.
A si, ca existe deja... Pardon...


Vision extremiste à part, voici ma technique perso de gestion de formulaire.

1) je fait la plupart de mes formulaires en POST,

Je test le button du form. Vielle habitude de tous les appeler submit
D'ailleur si qqn a des infos sur le comportement des navigateurs par
rapport a l'envoye de la valeur du button submit si l'on ne clique pas
dessus mais si l'on utilise enter, ca m'interesse ;o)

<?php

if(isset($_POST['submit'])){
// Initalisation d'un tableaux d'erreur
$erreurs = array();
// Suite de tests concernant les champs et leurs validité,
principalement a coup de regex et de fonctions maisons
if(!preg_match('`^[a-zA-Z0-9]{6,15}$`',$_POST['trucmuch'])){
$erreurs[] = 'Désolé, la var trucmuch doit etre mieux remplie';
}
// Note, en utilisation anormale du formulaire, ca peut faire un
warning, mais bon, je me contre fiche des utilisation anormales qui ne
pose pas de problemes de sécurité:)

// Si l'on n'as rencontrer aucune erreur, on peut poursuivre
if(count($erreurs) == 0){
// poursuite
}
}
?>


Concernant la recuperation et le reafichage de valeurs, je me suis fait
une fonction maison default_value('nom','truc'); qui affiche le contenu
de $_POST['nom'] si elle existe ( passer a la moulinette
htmlspecialchars() ou 'truc' comme valeur par defaut ( optionel, si non
fournit, renvoye 1)

Cela fonctione pour tout ce qui est text et textarea, pour les
radio/checkbox et select, j'ai une autre fonction maison

default_select('item','var','valeur',TRUE/FALSE)
qui renvera l'etat check ou non correspondant a l'item ( cad
selected="selected" pour liste...) si $_POST['var'] correspond à
'valeur', l'etat inverse sinon
TRUE/FALSE indique l'etat par defaut.

Note, il est facile de remplacer dans les fonctions $_POST par $_REQUEST
pour obtenir le GET.

Concernant le traitement de la requete sql, si doublons --> j'indique le
doublons dans $erreurs. Si erreur dans la requete -> die sauvage, je
considere que une foix que le script tourne correctement, y a pas de
raison que il y ai une erreur dans la requete, Si autre erreur, ( base
out ou truc comme ca) je varie ( soit une page basic avec BASE OUT, soit
j'espere que le site reste navigable sur le cache le temps que la base
revient. En pratique comme ca n'arrive jamais ;o)

Derniere chose, si l'insertion se passe correctement, je vide la
variable $_POST pour que les infos ne reaparaissent pas dans le
formulaire ( ou je ne l'affiche pas :))


Voila voila, j'espere que ma vision du traitement des formulaires pourra
faire avancer le schmilbick.

--
Guillaume.

Avatar
John Gallet
Bonsoir,

Du temps de fciwap, on faisait parfois un mini débat sur un thème classique
et si des choses intéressantes en ressortaient, je faisais un résumé pour
la

FAQ. Je relance l'idée, on verra bien si ça intéresse les foules.



Bon, ça n'a pas passionné, mais ayant reçu quelques mails en ce sens, on
essaiera de persévérer.
Je vais donner une méthode générale permettant de résoudre le problème.

Le sujet du moment : vous devez gérer un formulaire avec deux variables
obligatoires, une facultative. Si les deux obligatoires sont reçues, vous
faites un insert en sgbdr. A noter que l'une des variables est clef
primaire

de la table histoire de compliquer un peu. Sinon vous regénérez le
formulaire en préremplissant les champs déjà saisis.


D'abord l'algorithme du script qui reçoit les 3 variables :
0 - filtrer les variables (pas vraiment algorithmique, mais n'oublions pas).
1. si l'une des deux variables obligatoires est absente (on se débarasse du
cas d'erreur)
alors : générer le formulaire pré-remplis et FIN.
2. (sinon) tenter l'insertion en base de données.
2.1 Si l'insertion échoue pour cause de doublons, alors afficher un message
d'erreur en ce sens et FIN (variante : afficher le formulaire de saisie).
2.2 (sinon) afficher un message "OK" (ou faire ce que doit faire le script
si c'était plus compliqué, bref, traitement normal).


Admettons donc maintenant qu'on dispose d'une fonction fx_input() qui admet
un paramètre obligatoire et deux paramètres facultatifs. Le premier
paramètre est le nom de la variable $nom_var. Le deuxième son type éventuel
$type, le troisième une éventuelle valeur par défaut $default.

Son algorithme :
Si la clef de nom $nom_var n'existe pas dans les variables reçues
($_REQUEST) alors renvoyer la valeur par défaut $default si elle est
définie, chaîne vide sinon.
Si le type de la variable $type est défini, vérifier que la valeur reçue
($_REQUEST[$nom_var] non, pas de ' ici...) est constituée uniquement de
caractères faisant partie de la liste autorisée pour ce type. Par exemple,
un entier ne peut être constitué que des caractères 0123456789 et
éventuellement de + et -
Dans tous les cas, tester la configuration de la plateforme et échapper les
" et les ' par le caractère d'échappement nécessaire à vore SGBDR (pour
mysql, faire un addslashes si get_magic_quotes_gpc renvoie FALSE).
On dispose aussi d'une fonction fx_connect() similaire à l'exemple donné
dans la FAQ de ce forum.

Un exemple de code PHP donc, sans les require qui définissent les fonctions
nécessaires :
<?php
// -- reception.php
$obligatoire1=fx_input('oblig1');
$obligatoire2=fx_input('oblig2');
$facultatif=fx_input('fac1');
// si une au moins des variables obligatoires est non renseignée
// on génère le formulaire et arrêt.
if($obligatoire1=="" || $obligatoire2=="")
{
require("genform.php");
exit();
}
$query="INSERT INTO latablekivabien VALUES (
'$obligatoire1', '$obligatoire2', '$facultatif') ";
$dad=fx_connect();
$res=mysql_query($query, $dad);
if($res=úLSE && mysql_errno($dad)!62)
{
require("excuses_erreur_technique.html");
exit();
}
if(mysql_errno($dad)=62)
{
echo "Ces donn&eacute; existent d&eacute;j&agrave; en base.<br>";
require("genform.php");
exit();
}
echo "Insertion effectu&eagrave; correctement<br>";
// etc... suite normale du script.
?>

Comment faites vous ça
proprement (avec séparation code / présentation maximale, sans pour autant
utiliser des templates, avec gestion d'erreurs élégantes pour l'internaute
etc...).
Reste alors genform.php qui est le "template du pauvre"(C)(TM)

<?php
// genform.php
// Toutes informations inutiles supprimmées, aucune mise en forme html
// nb : on part du principe qu'on a utilisé, automatiquement ou pas,
addslashes
// dans la fonction fx_input() pour échapper les " et '. Adapter selon
besoin.
define ("MSG_OBLIG", "* obligatoire");
?>
<FORM ACTION="reception.php">
<INPUT TYPE="TEXT" nom="oblig1" VALUE="<?php echo
stripslashes($obligatoire1);?>">
<?php if($obligatoire1=="") echo MSG_OBLIG;?>
<INPUT TYPE="TEXT" nom="oblig2" VALUE="<?php echo
stripslahes($obligatoire2);?>">
<?php if($obligatoire1=="") echo MSG_OBLIG;?>
<INPUT TYPE="TEXT" nom="fac1" VALUE="<?php echo stripslashes($fac1);?>">
etc...

Voilà donc pour une méthode générale qui est relativement blindée, et ne
vous réservera pas de
mauvaises surprises si vous la suivez. On peut bien entendu écrire du code
plus compact, en particulier dans la gestion des erreurs, là j'ai évité tous
les raccourcis syntaxiques ou appels de fonctions "maison" ou successions de
if/else.

HTH
JG

Avatar
xpatval
"John Gallet" a fait l'effort de proposer un
sujet/thème dans le message de
news:404633d5$0$28604$

. Je relance l'idée, on verra bien si ça intéresse les foules.
Peut-être pas la Grande foule, mais au moins certains...



Bon, ça n'a pas passionné, mais ayant reçu quelques mails en ce sens, on
essaiera de persévérer.
Oh oui, Oh oui !


Ceci étant, et puisqu'aucun post n'a suivi la "réponse" de J.G au sujet
proposé, je me permets de le féliciter quant à son initiative qui ,
j'espère, ne s'arrêtera pas à un seul sujet/thème/débat.

Voilà.

xpatval


Avatar
Guillaume Bouchard
// nb : on part du principe qu'on a utilisé, automatiquement ou pas,
addslashes


Je pense que il ne faudrais pas partir sur ce principe.
Je m'explique.

Si on part du principe que on a utiliser addslashes, on part du principe
que nos vars sont protegées contre toutes "attaques" en SQL injections.
Or, si jamais il n'y a pas ce passage aux addslashes en amont (un vil
cracker esseyant d'appeler les fichiers a inclure), il peut y avoir un
probleme. Bon, c'est vrai que si l'on code proprement, il y a toujours
la fonction fx_nettoyage() qui verifie que une mechante quote ne se
balade pas, mais l'on ne sais jamais ;o)
De plus, je n'aime pas me balader avec des variables pré addslasher dans
le script. Cela peut fausser pas mal de traitement, ( longueur de
chaine, expressions regulieres...)

Ce que je fait (et que j'explique en detail avec mon style vraiment
horrible sur mon blog, chercher sur google :) c'est :

1) set_magic_quote_runtime(0);

Comme cela, je suis certains que toutes les variables qui sortent par la
suite sont "propres".

2) une fonction qui nettoye les variables concernées si
get_magic_quote_gpc() == true. Des variables concerné, ils n'y en a pas
beaucoup, $_REQUEST et eventuelement $_FILES.

Bref, j'ai un environement sans quotes et je n'oublie pas de les ajouter
lors de la construction de ma requete. Cela m'apporte donc plusieurs
aventages :

1) Je suis completement independant du réglage serveur, j'ai juste mon
script à inclure en debut d'execution et je l'oublie completement.

2) La seule presence de addslashe dans mon code se situe aux niveaux des
requetes SQL.

3) Si jamais un petit malin arrive à eviter que mon fichier de tri sois
inclue, le script se retrouve avec soit des vars "propres" et cela ne
change rien, soit des vars deja addslasher, ce qui va provoquer des
comportements etrange comme la presence de slashes de partout, mais
aucun comportement nocif.

--
Guillaume.

Avatar
John Gallet
Re,

// nb : on part du principe qu'on a utilisé, automatiquement ou pas,
addslashes
Je pense que il ne faudrais pas partir sur ce principe.




J'indiquais seulement la raison pour laquelle il y avait ce stripslashes en
plein milieu du code, mais j'étais pris d'une crise de flemme suffisante
pour ne pas répercuter le cas général où on aurait géré un SGBDR où
l'échappement des ' ne se fait pas par un slash. Quant à faire l'échappement
avant ou après, on peut en effet dédocappilidécouper.

Si on part du principe que on a utiliser addslashes, on part du principe
que nos vars sont protegées contre toutes "attaques" en SQL injections.
Hum j'avais écrit : on a utilisé. C'est fait, c'est trop tard. C'était

seulement la justification du stripslashes dans le echo. Accessoirement non,
on part du principe qu'on se protège d'une injection SQL par utilisation de
' dans une requête, il y en a d'autres.

Or, si jamais il n'y a pas ce passage aux addslashes en amont (un vil
cracker esseyant d'appeler les fichiers a inclure), il peut y avoir un
probleme.


Non il ne peut pas y avoir de problème. Le fichier genform.php ne fait appel
qu'à des fonctions PHP qui font de l'affichage, éventuellement avec un test
contextuel. Si un attaquant appelle directement ce fichier dans l'URL en
register_globals=On (on s'en fout en off) alors il n'aura que l'affichage de
ses conneries à lui qu'il aurait aussi bien pu saisir dans le formulaire
html. Evidemment (mais ça va mieux en le disant) ne jamais faire
d'utilisation de ce type de fichiers inclus avec des variables déclarées
ailleurs en exécutant du code qui fait autre chose que de l'affichage.

Et s'il en est à réussir à faire des includes de tes fichiers PHP, il est un
peu tard pour se préoccuper de la sécurité...

De plus, je n'aime pas me balader avec des variables pré addslasher dans
le script. Cela peut fausser pas mal de traitement, ( longueur de
chaine, expressions regulieres...)



Pourquoi pas. En pratique, il y a peu de cas où j'ai besoin de réafficher
les données que je viens de recevoir dans le même script. Donc ou je les
stripslashes(), ou même dans certains cas bien précis je vais refaire un
select en base. Mais on pourrait parfaitement disposer des variables en deux
versions, ou faire l'échappement des quotes seulement au moment de
l'interaction avec la base.
En revanche, si tu utilises la méthodes que j'ai décrite, tu auras fait
toutes tes vérifications de cohérence (taille, regexp etc...) avant de faire
l'échappement des quotes par ce qui va bien (relis l'algo de fx_input()).

Bref, j'ai un environement sans quotes et je n'oublie pas de les ajouter
lors de la construction de ma requete.


Oui pourquoi pas. Mais je trouve pénible de le faire aussi tard, en général
... j'oublie. Refaire l'échappement des quotes avant **chaque** requête SQL
sur **toutes** les variables de la requête, ça me fatigue rien que d'y
penser. Enfin quand je dis "toutes" ça dépend des vérifs que tu as faites
avant, si tu as vérifié que tes entiers ne contenaient que [0-9] alors en
effet tu n'en as pas besoin.

1) Je suis completement independant du réglage serveur, j'ai juste mon
script à inclure en debut d'execution et je l'oublie completement.


Ah non je proteste, tu dois justement faire ce qui est en dessous :

2) La seule presence de addslashe dans mon code se situe aux niveaux des
requetes SQL.



Dans ce que je proposais, il y a une et une seule fois la string
"addslashes" : dans fx_input().

3) Si jamais un petit malin arrive à eviter que mon fichier de tri sois
inclue, le script se retrouve avec soit des vars "propres" et cela ne
change rien, soit des vars deja addslasher,


Je ne vois pas comment quiconque pourrait contourner l'affectation d'une
variable... Dès lors que tu travailles eclusivement sur des variables
locales qui ont été affectée par le retour de fx_input() tu peux toujours
essayer de contourner ce que tu voudras, c'est tout simplement impossible.

a++
JG


1 2