[Résolu] Problème Concaténation

Problème Concaténation [Résolu] - PHP - Programmation

Marsh Posté le 13-07-2017 à 14:51:44    

Bonjour, je débute en Php, j'essaie de concaténer une chaine nom=variable pour mettre à jour ma Bdd.

Code :
  1. <?php
  2. //connection à la base de donnée du jeu
  3.     $host_name  = "dbx.db.1and1.com";
  4.     $database   = "dbx";
  5.     $user_name  = "dbox";
  6.     $password   = "x";
  7.     $connect = mysqli_connect($host_name, $user_name, $password, $database);
  8. $ID = $_GET['ID'];
  9. foreach($_GET as $query_string_variable => $value) {
  10. $Var="'$Var''$query_string_variable'='$value', ";
  11. }
  12.  $sql="Update dbx.Personnage SET '$Var' WHERE ID='$ID'";
  13.  $result=mysqli_query($connect,$sql);
  14.  echo $result;
  15.  mysql_close();
  16. ?>


 
Comment faire ?
Merci de vos réponses.


Message édité par fig le 14-07-2017 à 20:12:40
Reply

Marsh Posté le 13-07-2017 à 14:51:44   

Reply

Marsh Posté le 13-07-2017 à 15:24:56    

De toute façon, c'est le genre de démarche qui va te valoir des injections SQL ...
 
Pour générer correctement l'UPDATE, il faudrait écrire quelque chose comme :

Code :
  1. $kv =[];
  2. foreach ($_GET as $k => $v) {
  3.    $kv[] = '`' . str_replace('`', '``', $k) . '` = "' . mysqli_real_escape_string($connect, $v) . '"';
  4. }
  5. if ($kv) { // un UPDATE avec une partie SET vide planterait (tableau $kv vide)
  6.    $sql = 'UPDATE Personnage SET ' . implode(', ', $kv) . ' WHERE ID=' . intval($_GET['ID']);
  7. }


 
Quelques remarques à propos du reste du code :

  • tu ne peux pas utiliser mysql_close, c'est son équivalent mysqli (mysqli_close) que tu dois employer
  • inutile de préfixer le nom de ta table par dbx. si c'est justement la base de données courante
  • si tu dois exclure des "clés" GET (logiquement ID en fait partie), il faut ajouter un test dans le foreach (type if ($k != 'ID')) ou, mieux, utiliser array_intersect_key ou array_diff_key :


Code :
  1. foreach ($_GET as $k => $v) {


Deviendrait :

Code :
  1. foreach (array_diff_key($_GET, array_fill_keys(['ID'], NULL)) as $k => $v) {


(on pourrait simplement faire unset($_GET['ID']); mais $_GET['ID'] ne serait plus disponible dans la suite du code s'il y en a besoin)
 
Mais je déconseille de générer dynamiquement des requêtes SQL, surtout à un débutant, c'est un piège à erreurs (et une injection SQL ça peut coûter très cher). Tant que c'est possible, il vaut mieux tout préparer. En plus, ici, on peut faire passer des colonnes qui n'existent pas (ce qui fera purement et simplement planter la requête)


Message édité par pluj le 13-07-2017 à 15:55:58
Reply

Marsh Posté le 13-07-2017 à 16:59:50    

Bonjour Pluj, merci beaucoup pour tes remarques.
 
J'ai 29 colonnes dans ma bdd.
Je peux aussi updater les 28 (29 - Id effectivement) colonnes à chaque fois que j'en touche une, mais je me suis dis que ça serait plus "économique" de n'envoyer que les colonnes qui ont été modifié, d'où ce que j'essayais de faire. (mais est ce vraiment le cas ? Je souhaite alléger au maximum les accès à la bdd pour avoir une réactivité bonne avec un maximum de client. Je n'ai pas de comparaison en la matière donc ...)
 
C'est un exécutable client qui dialogue avec le fichier php distant. L'utilisateur ne peut donc pas déterminer l'adresse du site où le fichier php est hébergé de manière triviale. Evidemment il est toujours possible de désassembler le prog pour trouver l'adresse, malheureusement. (je ne compte pas perdre du temps à obfusquer le code celui qui est motivé pourra de toute façon passer à travers)
 
Le code suivant fonctionne effectivement parfaitement. Merci infiniment !
 

Code :
  1. <?php
  2. //connection à la base de donnée du jeu
  3.     $host_name  = "dbx.db.1and1.com";
  4.     $database   = "dbx";
  5.     $user_name  = "dbox";
  6.     $password   = "x";
  7.     $connect = mysqli_connect($host_name, $user_name, $password, $database);
  8. $ID = $_GET['ID'];
  9. $kv =[];
  10. foreach (array_diff_key($_GET, array_fill_keys(['ID'], NULL)) as $k => $v) {
  11.     $kv[] = '`' . str_replace('`', '``', $k) . '` = "' . mysqli_real_escape_string($connect, $v) . '"';
  12. }
  13. if ($kv) { // un UPDATE avec une partie SET vide planterait (tableau $kv vide)
  14.     $sql = 'UPDATE Personnage SET ' . implode(', ', $kv) . ' WHERE ID=' . intval($ID);
  15. }
  16. $result=mysqli_query($connect,$sql);
  17. echo $result;
  18. mysqli_close();
  19. ?>


 
J'ai un question encore:
Vaut il mieux plusieurs petits fichiers php (un par fonctionnalité) ou un seul plus important avec chaque fonction déterminée par une variable passée par get ?
(genre si fct=1 écrire dans la bdd si fct=2 lire la ligne X etc...)


Message édité par fig le 13-07-2017 à 17:37:03
Reply

Marsh Posté le 13-07-2017 à 17:35:18    

Le SGBD n'écrasera pas une valeur par la même, il ignorera la modification donc la requête peut comporter les 28 colonnes. Certes la requête (binds si préparée) en elle-même est plus volumineuse mais tu n'as aucun risque d'injecter des colonnes qui n'existent pas ou d'avoir des injections SQL.
 
Je ne sais pas non plus à qu(o)i c'est réellement destiné mais d'une part en modifiant l'ID dans l'URL on peut cibler n'importe quel personnage ; d'autre part, s'il y a des attributs que l'utilisateur n'est pas censé pouvoir modifier, là, il peut également le réaliser en bidouillant l'URL (imaginons des crédits associés à un personnage, pour passer à un million, un ajout de &credit=1000000 à l'URL et le voilà avec un très net avantage).

Reply

Marsh Posté le 13-07-2017 à 17:50:22    

Il ignorera la modification mais néanmoins devra aller chercher la valeur et la comparer, opération dont je ne connais pas le coût en terme de temps mais intuitivement c'est toujours plus rapide de ne pas faire cette opération même si elle est rapide, n'est pas ?
 
Oui l'Id sert à cibler le personnage, tout à fait.
 
Le jeu est un exécutable donc l'adresse n'est pas apparente et c'est le programme qui se charge d'envoyer les requêtes au site. (donc il ne se trompera pas avec des colonnes inexistantes.
 
Le joueur ne peut pas connaitre l'Url donc à priori pas la bidouiller. (sous réserve comme je l'écrivais plus haut de désassembler le code).
 
Le jeu est le suivant si tu es curieux...
http://craft-maga.forumactif.com/t1-avancement-du-jeu
 
La bdd contient l'entrée du joueur identifié par une adresse email d'abord puis par l'Id. Les colonnes contiennent les cara du joueurs, l'argent, la vie, les objets et leur position, l'avancement des quetes etc...
J'ai déja fait la création du joueur dans la bdd, le chargement et là je fais la sauvegarde avec ton aide.
 

Reply

Marsh Posté le 13-07-2017 à 18:14:40    

Oui, je me pose quand même la question de savoir si ce ne serait pas mieux de tout renvoyer pour 1) avoir une trace côté SGBD et 2) en cas de concurrence (un client change quelque chose que l'autre aurait voulu garder). Du coup, toute la logique pour gérer ce qui a changé ou pas est côté client dans ton cas ?
 
J'ai loupé ton edit :
 

Citation :

Vaut il mieux plusieurs petits fichiers php (un par fonctionnalité) ou un seul plus important avec chaque fonction déterminée par une variable passée par get ?
(genre si fct=1 écrire dans la bdd si fct=2 lire la ligne X etc...)


 
Ca ne change rien, à toi de voir ce que tu trouves le plus pratique et lisible.
 
Sur ton mysqli_close :

Code :
  1. mysqli_close();


 
Il manque son paramètre, la connexion :

Code :
  1. mysqli_close($connect);


Ca aurait dû valoir une erreur (si tant est qu'elles sont affichées).
 
Ou tu peux carrément omettre l'instruction mysqli_close puisque PHP, à la fin de tout script, nettoie/ferme tout.


Message édité par pluj le 13-07-2017 à 18:16:06
Reply

Marsh Posté le 13-07-2017 à 18:28:41    

Une trace coté SGBD ? Pour quoi faire ?
Pas de concurence, chaque joueur à une ligne dans la bdd, chacun modifie sa ligne, pas d'interaction entre les données des joueurs.
Effectivement toute la logique est coté client puisque je maitrise cette programmation, le coté php est réduit à son plus simple appareil puisque je ne le maitrise pas et qu'il sera forcément plus lent.
 
Bon, j'ai fait avec des petits fichiers php, c'est plus simple pour moi qui débute.
 
Ah, je vais corriger le paramètre de cloture de connexion, merci !

Reply

Marsh Posté le 13-07-2017 à 20:54:21    

J'ai encore une question.
J'envoie la chaîne suivante avec l'url: "Objet=lingot%20d'acier;poign%C3%A9e;bois;ciseau;pierre%20brute;marteau;enclume;lame%20courte"
Je veux la mettre dans une colonne Text.
Les autres paramètres qui sont des nombres passent bien mais celui-ci ne fonctionne pas, Savez vous pourquoi ?


Message édité par fig le 13-07-2017 à 20:54:51
Reply

Marsh Posté le 13-07-2017 à 23:57:20    

Qu'entends-tu par "ne pas fonctionner" au juste ?
 
L'UPDATE échoue/ne fait rien ?
 
Si oui, qu'en dit mysqli_error ? (ou mieux un mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); en début de code pour que les requêtes lèvent directement une exception en cas d'erreur)
 
Si non, tu te retrouves avec quoi en base ?
 
C'est à cause de l'UTF-8 car tu n'as pas fait (juste après la connexion) de

Code :
  1. mysqli_set_charset($connect, 'utf8');

?
 
Par "trace côté SGBD", je pensais au journal des requêtes comme au journal binaire (backup/réplication)


Message édité par pluj le 13-07-2017 à 23:59:32
Reply

Marsh Posté le 14-07-2017 à 15:05:24    

Je viens de me rendre compte de mon erreur, j'ai oublié une majuscule sur le nom de la colonne...  :na:  
Je vais réessayer et tout devrait fonctionner.
 
 
 
Edit: J'avais effectivement aussi besoin de spécifier le mode d'envoi des caractères (utf-8) dans mon fichier php de chargement pour que je reçoive les caractères accentués entre autre.
 
Merci encore.  :jap:


Message édité par fig le 14-07-2017 à 18:24:30
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed