Ma requête préparée ne passe pas...

Eléphant du PHP | 71 Messages

18 déc. 2006, 01:31

Bonsoir,

Je tente de faire une insertion de données et je n'arrive pas à éxécuter correctement ma requête préparée...

Je récupère toujours ce message d'erreur mais ça ne me parle pas vraiment étant donné que j'ai bien l'impression d'entrer le bon nombre de paramètres :? :
Warning: mysqli_stmt::bind_param() [function.mysqli-stmt-bind-param]: Number of variables doesn't match number of parameters in prepared statement in E:\workdraft\verif.php5 on line 54

Fatal error: Requete invalide in E:\workdraft\verif.php5 on line 60
Mon code est le suivant :
<?php
session_start();

class DefineUser
{
	// Données d'accès à la BDD
	private $nom = ******;
	private $mdp = ******;
	private $serveur = "localhost";
	private $base = "galerie";

	// Méthode de création d'instance de l'objet
	public function __construct()
	{
		self::__define();
		return header('Location: ../interface.php5');
	}

	// Insertion des données utilisateurs dans la BDD
	private function __define()
	{

		// Si l'utilisateur envoie un login et un mot de passe
		if(isset($_POST['txtLogin']) && isset($_POST['pwdPassword']))
		{

			// Si une variable de session 'submitLogPwd' existe et est égale à celle renvoyée par le formulaire
			if(isset($_SESSION['submitLogPwd']) && $_POST['submitLogPwd'] == $_SESSION['submitLogPwd'])
			{

				// Création de la connexion à la BDD
				$connexion = new mysqli($this->serveur, $this->nom, $this->mdp, $this->base)
							 or trigger_error('Connexion a la base impossible', E_USER_ERROR);

				// Création d'une table utilisateur pour contenir les données
				$requete = "CREATE TABLE utilisateur(
					id int(11) NOT NULL auto_increment,
					login char(40),
					motdepasse char(32),
					graindesel char(32),
					PRIMARY KEY (id)
				)";
				$resultat = $connexion->query($requete) or trigger_error('Requete invalide', E_USER_ERROR);

				// Requête d'insertion des données
				$requete2 = "INSERT INTO utilisateur(
					login, mdp, graindesel
				) VALUES(
					?, ?, ?
				)";

				// Préparation de la requête et insertion des données
				$resultat2 = $connexion->prepare($requete);
				$resultat2->bind_param('sss', $txtLogin, $pwdPassword, $graindesel);

				// Données à insérer et éxécution de la requête
				$graindesel = md5(uniqid(rand(), true));
				$txtLogin = htmlentities($_POST['txtLogin']);
				$pwdPassword = md5((htmlentities($_POST['pwdPassword'])).$graindesel);
				$resultat2->execute() or trigger_error('Requete invalide', E_USER_ERROR);

				// Libération de la mémoire et fermeture de la connexion
				$connexion->free_result();
				$connexion->close();
			}
		}
	}

}
$user = new DefineUser;

?>
Quelqu'un aurait une petite idée de ce qu'il se passe parce que je ne trouve vraiment pas ? :cry:

PS : Si vous voyez des petits trucs à droite, à gauche qui vous chagrinent dans mon code, je suis tout ouïe parce que je n'ai pas pratiqué des masses jusqu'à aujourd'hui... :oops:

Merci.

ViPHP
ViPHP | 1961 Messages

18 déc. 2006, 01:48

Bonsoir,

Je ne te parlerai pas de ta class mais c'est pas très objet.

Concernant ton erreur
ici
$requete2 = "INSERT INTO utilisateur(
                    login, mdp, graindesel
                ) VALUES(
                    ?, ?, ?
                )";

                // Préparation de la requête et insertion des données
                $resultat2 = $connexion->prepare($requete); 
tu prépares une requête avec 3 pramètres
Mais ici
$resultat2->bind_param('sss', $txtLogin, $pwdPassword, $graindesel); 
Tu veux lui assigner 4 paramètres ton erreur est donc logique.
Deux choses sont infinies, l'Univers et la sottise humaine!!
Mais je ne suis pas sur de ce que j'affirme au sujet de l'Univers.

A. Einstein

Administrateur PHPfrance
Administrateur PHPfrance | 3088 Messages

18 déc. 2006, 01:56

Si vous voyez des petits trucs à droite, à gauche qui vous chagrinent dans mon code, je suis tout ouïe
Ça roule, alors voilà la liste :)

Ligne 15, tu appelles une méthode de façon statique alors qu'elle n'a pas été déclarée "static".

Ligne 20, il est déconseillé de préfixer le nom d'une méthode d'un double underscore, il vaut mieux laisser ce préfixe aux méthodes magiques de PHP (comme __construct(), __destruct(), __get(), etc...)

Ligne 33, si je ne me trompe pas, trigger_error() ne stoppe pas l'exécution d'un script donc si tu utilises le gestionnaire d'erreur par défaut le script continuera même en cas d'erreur.

Ligne 43, je recommande d'appeler les objets de requêtes préparées $stmt (pour "statement", de "prepared statement"). Ce n'est pas un résultat, d'où un nom différent.

Ligne 46, pourquoi utiliser un nouveau nom de variable ? tu ne te serviras plus du $requete original donc autant réutiliser la variable ici.

Ligne 53, ben voilà, à force de multiplier les noms de variables tu t'es planté de nom dans celle-là :lol: (tu as utilisé $requete à la place de $requete2) Mon conseil : réutilise toujours le même nom de variable si tu ne te sers plus de la variables originales. Ici, $resultat2 deviendrait $stmt.

Ligne 63, je ne sais pas ce que fait cette instruction :?: Si tu veux libérer une requête préparée, c'est $stmt->close()

Bonne continuation ;)

PS: on s'est croisé avec Ajoloca (dont la réponse est apparue pendant que je rédigeais la mienne) mais lui aussi s'est fait avoir par les noms de variables :lol: La ligne 54 est bonne du moment que tu renommes la ligne 46 en $requete

ViPHP
ViPHP | 1961 Messages

18 déc. 2006, 02:10

Re,

Vu que Hubert Roksor a commencé, je pousuis.

A quoi sert une class qui n'est utilisable qu'une fois ?
Si on l'utilise deux, elle plante (Création d'une table qui existe -- utilisateur --).

Jamais de variables en dur dans une class, dans ce cas elles deviennent des constantes.
Si ce sont des constantes, le jour ou un paramètre de connexion à la base change ==> Réecriture de la class. Le principe de la réutilisabilité est baffoué.

Ne jamais utiliser de variables globales dans une class ($_POST[], etc...)
Deux choses sont infinies, l'Univers et la sottise humaine!!
Mais je ne suis pas sur de ce que j'affirme au sujet de l'Univers.

A. Einstein

ViPHP
ViPHP | 1961 Messages

18 déc. 2006, 02:13

PS: on s'est croisé avec Ajoloca (dont la réponse est apparue pendant que je rédigeais la mienne) mais lui aussi s'est fait avoir par les noms de variables Laughing La ligne 54 est bonne du moment que tu renommes la ligne 46 en $requete
C'est exact, bien vu! :pouce:
Deux choses sont infinies, l'Univers et la sottise humaine!!
Mais je ne suis pas sur de ce que j'affirme au sujet de l'Univers.

A. Einstein

Eléphant du PHP | 71 Messages

18 déc. 2006, 11:38

Hello,

Tout d'abord, merci pour vos réponses, ça met du beaume au coeur... :o
Je ne te parlerai pas de ta class mais c'est pas très objet.
Ah ? Flute... :? Il est où le soucis ?
Tu veux lui assigner 4 paramètres ton erreur est donc logique.
Ben non ?! :shock: Je lui passe bien trois paramètres, le premier représentant le type des paramètres comme indiqué sur cette page. Dans le doute, j'ai testé sans les 'sss' mais ça ne passe pas plus, malheureusement.
Ligne 15, tu appelles une méthode de façon statique alors qu'elle n'a pas été déclarée "static".

Ligne 20, il est déconseillé de préfixer le nom d'une méthode d'un double underscore, il vaut mieux laisser ce préfixe aux méthodes magiques de PHP (comme __construct(), __destruct(), __get(), etc...)

Ligne 33, si je ne me trompe pas, trigger_error() ne stoppe pas l'exécution d'un script donc si tu utilises le gestionnaire d'erreur par défaut le script continuera même en cas d'erreur.

Ligne 43, je recommande d'appeler les objets de requêtes préparées $stmt (pour "statement", de "prepared statement"). Ce n'est pas un résultat, d'où un nom différent.

Ligne 46, pourquoi utiliser un nouveau nom de variable ? tu ne te serviras plus du $requete original donc autant réutiliser la variable ici.

Ligne 53, ben voilà, à force de multiplier les noms de variables tu t'es planté de nom dans celle-là :lol: (tu as utilisé $requete à la place de $requete2) Mon conseil : réutilise toujours le même nom de variable si tu ne te sers plus de la variables originales. Ici, $resultat2 deviendrait $stmt.
ok, j'ai changé tout ça. Merci. :wink:
Ligne 63, je ne sais pas ce que fait cette instruction :?: Si tu veux libérer une requête préparée, c'est $stmt->close()
J'ai ajouté $stmt->close() mais j'ai conservé $mysqli->close() parce que ça me sert à fermer la connexion manuellement.
A quoi sert une class qui n'est utilisable qu'une fois ?
Si on l'utilise deux, elle plante (Création d'une table qui existe -- utilisateur --).
On ne devrait pas pouvoir utiliser cette classe deux fois puisque le formulaire en question n'apparaît qu'une seule fois en début d'application et si tout se déroule comme prévu, l'utilisateur est redirigé sur une autre page. Est-ce une mauvaise pratique que de créer une classe pour celà ? :?
Jamais de variables en dur dans une class, dans ce cas elles deviennent des constantes.
Si ce sont des constantes, le jour ou un paramètre de connexion à la base change ==> Réecriture de la class. Le principe de la réutilisabilité est baffoué.

Ne jamais utiliser de variables globales dans une class ($_POST[], etc...)
En effet, j'avais défini mes variables de connexion au sein de cette classe mais bon, j'avais prévu de les passer dans un fichier à part par la suite... C'était plus pour le test... Voilà qui est changé aussi du coup. :wink:

Donc, mon code est maintenant le suivant :

Fichier connexion.inc.php
<?php

// Données d'accès à la BDD
$nom = *******;
$mdp = *******;
$serveur = 'localhost';
$base = 'galerie';
$mysqli = new mysqli($serveur, $nom, $mdp, $base);
if(mysqli_connect_errno())
{
	printf("Connexion échouée : %s\n", mysqli_connect_error());
	exit();
}

?>
Fichier verif.php
<?php
session_start();

class DefineUser
{

	// Méthode de création d'instance de l'objet
	public function __construct()
	{
		$this->define();
		return header('Location: ../interface.php5');
	}

	// Insertion des données utilisateurs dans la BDD
	private function define()
	{

		// Si l'utilisateur envoie un login et un mot de passe
		if(isset($_POST['txtLogin']) && isset($_POST['pwdPassword']))
		{

			// Si une variable de session 'submitLogPwd' existe et est égale à celle renvoyée par le formulaire
			if(isset($_SESSION['submitLogPwd']) && $_POST['submitLogPwd'] == $_SESSION['submitLogPwd'])
			{

				// Création de la connexion à la BDD
				include_once 'connexion.inc.php';

				// Création d'une table utilisateur pour contenir les données
				$requete = "CREATE TABLE utilisateur(
					id int(11) NOT NULL auto_increment,
					login char(40),
					motdepasse char(32),
					graindesel char(32),
					PRIMARY KEY (id)
				)";
				$stmt = $mysqli->query($requete);
				$stmt->close();

				// Requête d'insertion des données
				$requete = "INSERT INTO utilisateur(
					login, mdp, graindesel
				) VALUES(
					?, ?, ?
				)";

				// Préparation de la requête et insertion des données
				$stmt = $mysqli->prepare($requete);
				$stmt->bind_param('sss', $txtLogin, $pwdPassword, $graindesel);

				// Données à insérer
				$graindesel = md5(uniqid(rand(), true));
				$txtLogin = htmlentities($_POST['txtLogin']);
				$pwdPassword = md5((htmlentities($_POST['pwdPassword'])).$graindesel);

				// Exécution de la requête
				$stmt->execute();
				$mysqli->close();
			}
		}
	}

}
$user = new DefineUser;

?>
Ben... ça ne marche pas ! :(

J'obtiens ceci :
Fatal error: Call to a member function close() on a non-object in E:\workdraft\verif.php5 on line 38
Mon objet est pourtant bien créé non ? :shock:

ViPHP
ViPHP | 1961 Messages

18 déc. 2006, 12:24

Bonjour,

Elle est où l'intentiation de $mysqi ?

Ta class ne la connait pas, tu dois lui passer en paramètre.

Regarde les modifs

Ton fichier verif.php
<?php
// Démarrage de la session
session_start();
// Inclusion du module de connexion (il instentie la connexion)
include_once('connexion.inc.php');
// LA CLASS
class DefineUser
{

    // Méthode de création d'instance de l'objet
    public function __construct($mysqli)
    {
        $this->define($mysqli);
        return header('Location: ../interface.php5');
    }

    // Insertion des données utilisateurs dans la BDD
    private function define($mysqli)
    {

        // Si l'utilisateur envoie un login et un mot de passe
        if(isset($_POST['txtLogin']) && isset($_POST['pwdPassword']))
        { 
......
....
} // Fin de la class
$user = new DefineUser($mysqli);

?>
Modifié en dernier par Ajoloca le 18 déc. 2006, 12:52, modifié 1 fois.
Deux choses sont infinies, l'Univers et la sottise humaine!!
Mais je ne suis pas sur de ce que j'affirme au sujet de l'Univers.

A. Einstein

Eléphant du PHP | 71 Messages

18 déc. 2006, 12:50

ben je créais l'instance au sein de mon fichier connexion.inc.php.

De là, j'incluais ce fichier via require_once une fois les conditions remplies pour ne créer la connexion que si l'utilisateur avait bien envoyé les données du formulaire.
En passant cet objet en tant que paramètre comme tu me l'as montré, j'obtiens la même ligne d'erreur. :?

ViPHP
ViPHP | 1961 Messages

18 déc. 2006, 13:43

Re,

Je n'avais pas fait attention à l'include dans la class, mais bon...

Ta requête de création de table
$stmt = $mysqli->query($requete);
ne retourne pas de $stmt (statement), mais un entier ou false.

Supprime la ligne
$stmt->close(); 
Deux choses sont infinies, l'Univers et la sottise humaine!!
Mais je ne suis pas sur de ce que j'affirme au sujet de l'Univers.

A. Einstein

Eléphant du PHP | 71 Messages

18 déc. 2006, 14:09

En fait, j'ai l'impression que l'erreur vient de l'instance $mysqli en elle-même... Si j'enlève $stmt->close(); j'obtiens l'erreur équivalente sur la requête suivante lorsque je tente de passer les paramètres...
Fatal error: Call to a member function bind_param() on a non-object in E:\workdraft\verif.php5 on line 48
Je n'ai pourtant pas l'impression de faire différemment des exemples que j'ai pu voir, à moins que je sois bigleux depuis le début... :?

ViPHP
ViPHP | 1961 Messages

18 déc. 2006, 14:21

Re,

Surement que ce que j'avais prévu arrive.
Tu est sur que ta table n'existepas dans la base ?

Teste le retour de
$stmt = $mysqli->query($requete); 
Il doit te retourner une erreur et donc il ne te crée pas ton $stmt.
Deux choses sont infinies, l'Univers et la sottise humaine!!
Mais je ne suis pas sur de ce que j'affirme au sujet de l'Univers.

A. Einstein

Eléphant du PHP | 71 Messages

18 déc. 2006, 16:28

Re ! Eurêka ! :D

Et dire que je cherchais la grosse bête... :lol:

Alors le soucis, c'était que... :oops: ... je créais un champ motdepasse et que je cherchais à remplir un champ mdp ! :langue:

Merci pour ton aide Ajoloca, tu m'as fait regarder au bon endroit. :wink:

Le code final est donc le suivant :

Fichier connexion.inc.php
<?php

// Données d'accès à la BDD
$nom = *******;
$mdp = *******;
$serveur = 'localhost';
$base = 'galerie';
$mysqli = new mysqli($serveur, $nom, $mdp, $base);
if(mysqli_connect_errno())
{
	printf("Connexion échouée : %s\n", mysqli_connect_error());
	exit();
}

?>
Fichier verif.php
<?php
session_start();

class DefineUser
{

	// Méthode de création d'instance de l'objet
	public function __construct()
	{
		$this->define();
		header('Location: ../interface.php5');
	}

	// Insertion des données utilisateurs dans la BDD
	private function define()
	{

		// Si l'utilisateur envoie un login et un mot de passe
		if(isset($_POST['txtLogin']) && isset($_POST['pwdPassword']))
		{

			// Si une variable de session 'submitLogPwd' existe et est égale à celle renvoyée par le formulaire
			if(isset($_SESSION['submitLogPwd']) && $_POST['submitLogPwd'] == $_SESSION['submitLogPwd'])
			{
				// Création de la connexion à la BDD
				include_once 'connexion.inc.php';

				// Création d'une table utilisateur pour contenir les données
				$requete = "CREATE TABLE utilisateur(
					id int(11) NOT NULL auto_increment,
					login varchar(40) NOT NULL default '',
					motdepasse char(32) NOT NULL default '',
					graindesel char(32) NOT NULL default '',
					PRIMARY KEY (id)
				)TYPE=MyISAM";
				$stmt = $mysqli->query($requete);
				if($stmt === false)
				{
					echo "La table n'est pas cr&eacute;&eacute;e !";
					exit();
				}

				// Requête d'insertion des données
				$requete = "INSERT INTO utilisateur SET login = ?, motdepasse = ?, graindesel = ?";

				// Préparation de la requête et insertion des données
				$stmt = $mysqli->prepare($requete);
				if($stmt === false)
				{
					echo "Les champs ne sont pas ins&eacute;r&eacute;s !";
					exit();
				}
				$stmt->bind_param('sss', $txtLogin, $pwdPassword, $graindesel);

				// Données à insérer
				$graindesel = md5(uniqid(rand(), true));
				$txtLogin = htmlentities($_POST['txtLogin']);
				$pwdPassword = md5((htmlentities($_POST['pwdPassword'])).$graindesel);

				// Exécution de la requête
				$stmt->execute();
				$mysqli->close();
			}
		}
	}

}
$user = new DefineUser;

?>
A bientôt... :wink: