IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

PHP & Base de données Discussion :

[PDO] Deux fois le même paramètre dans la requête préparée [MySQL]


Sujet :

PHP & Base de données

  1. #1
    Modérateur

    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 799
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 799
    Points : 34 031
    Points
    34 031
    Billets dans le blog
    14
    Par défaut [PDO] Deux fois le même paramètre dans la requête préparée
    Bonjour,

    Jusque là, je n'ai jamais eu de souci avec ça donc je me demande si c'est une nouveauté PHP 7...

    Soit la requête suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $sql = "
    			SELECT diplomeId, diplomeCode, diplomeLibelleCourt, diplomeLibelleLong
    			FROM v_diplome_superieur
    		";
    // (...)
    $sql.= "WHERE diplomeLibelleCourt LIKE :nomDiplome
    						OR diplomeLibelleLong LIKE :nomDiplome
    			";
    			$param = array('param' => ':nomDiplome', 'value' => trim($nomDiplome).'%', 'data_type' => \PDO::PARAM_STR);
    // (...)
    $result = self::executerRequete($sql, array($param));
    J'ai donc deux fois le même paramètre :nomDiplome et je ne vois pas pourquoi il faudrait que je l'envoie deux fois, non ?

    Voilà ce que j'obtiens à l'exécution :
    Erreur PDO : SQLSTATE[HY093]: Invalid parameter number dans le fichier /srv/www/htdocs/pef/Application/Controller/Modele.php à la ligne 83
    Requête : SELECT diplomeId, diplomeCode, diplomeLibelleCourt, diplomeLibelleLong FROM v_diplome_superieur WHERE diplomeLibelleCourt LIKE :nomDiplome OR diplomeLibelleLong LIKE :nomDiplome
    Paramètres :

    Array
    (
    [0] => Array
    (
    [param] => :nomDiplome
    [value] => Licence en économie et sociologie%
    [data_type] => 2
    )

    )
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole. Autoentrepreneur.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  2. #2
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Sauf erreur après test *, ça fonctionne avec ->bindValue()...

    ...encore faudrait-il voir le code de executerRequete()...


    * Test :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    	$search = 'chaz';
    	$rech_query = "SELECT *
    				FROM contact
    				WHERE mail LIKE :search OR nom LIKE :search
    				;";
      try {
    	$pdo_select = $pdo->prepare($rech_query);
    	$pdo_select->bindValue(':search', 	$search.'%',	PDO::PARAM_STR);
     
    	$pdo_select->execute();
    Dernière modification par Invité ; 13/01/2020 à 17h56.

  3. #3
    Modérateur

    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 799
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 799
    Points : 34 031
    Points
    34 031
    Billets dans le blog
    14
    Par défaut
    J'ai remplacé bindParam par bindValue dans executerRequete (voir code ci-dessous) mais j'ai le même résultat !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    	/**
    	 * Exécute une requête SQL éventuellement paramétrée
    	 * @param string $sql : Requête SQL à exécuter
    	 * @param array $params : Tableau de paramètres éventuels (param, value, data_type)
    	 * @return mixed
    	 */
    	protected static function executerRequete($sql, $params = null)
    	{
    		// Détermination du type de requête à exécuter
    		$mots = explode(' ', $sql, 2);
    		$typeRequete = trim($mots[0]);
     
    		try
    		{
    			// Exécution de la requête
    			if ($params == null)
    			{
    				$prep = self::getBdd()->query($sql);    // exécution directe
    			}
    			else
    			{
    				$prep = self::getBdd()->prepare($sql);  // requête préparée
     
    				foreach($params as $param)
    				{
    					if(count($param) == 4)
    					{
    						// Paramètre en sortie
    						$prep->bindValue($param['param'], $param['value'], $param['data_type'], $param['length']);
    					}
    					else
    					{
    						$prep->bindValue($param['param'], $param['value'], $param['data_type']);
    					}
    				}
     
    				$prep->execute();
    			}
     
    			// Envoi du résultat ou pas selon type de requête
    			switch ($typeRequete)
    			{
    				case "SELECT":
    					// Requête de type SELECT envoi de toutes les lignes de résultat sous forme de tableau
    					$resultat = $prep->fetchAll(PDO::FETCH_ASSOC);
    					break;
    				case 'INSERT':
    					// Requête de type INSERT => envoi de l'identifiant inséré
    					$resultat = self::getBdd()->lastInsertId();
    					break;
    				default:
    					// Autre type => pas de résultat à retourner
    					$resultat = '';
    			}
     
    			$prep->closeCursor();
    		}
    		catch (\PDOException $e)
    		{
    			echo '<br />Erreur PDO : '.$e->getMessage().' dans le fichier '.$e->getFile().' à la ligne '.$e->getLine();
    			echo '<br />Requête : '.$sql;
    			echo '<br />Paramètres : <pre>';
    			print_r($params);
    			echo '</pre>';
    			// TODO : à gérer avec les erreurs de l'application
    		}
     
    		return $resultat;
    	}
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole. Autoentrepreneur.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  4. #4
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    As-tu testé directement ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    $sql = "
    			SELECT diplomeId, diplomeCode, diplomeLibelleCourt, diplomeLibelleLong
    			FROM v_diplome_superieur
    		";
    // (...)
    $sql.= "WHERE diplomeLibelleCourt LIKE :nomDiplome
    						OR diplomeLibelleLong LIKE :nomDiplome
    			";
      try {
    	$pdo_select = $pdo->prepare($sql);
    	$pdo_select->bindValue(':nomDiplome', 	trim($nomDiplome).'%',	PDO::PARAM_STR);
     
    	$pdo_select->execute();
    	$rech_rowAll = $pdo_select->fetchAll();
      } catch (PDOException $e){ echo 'Erreur SQL : '. $e->getMessage().'<br/>'; die(); }
    Sinon... Il faut mettre 2 placeholders différents.

    N.B. Il semblerait qu'on ne puisse pas le faire :
    https://www.php.net/manual/fr/pdosta...lue.php#118662
    When binding parameters, apparently you can't use a placeholder twice (e.g. "select * from mails where sender=:me or recipient=:me"), you'll have to give them different names otherwise your query will return empty handed (but not fail, unfortunately).
    https://www.php.net/manual/fr/pdo.prepare.php#69291
    Using a placeholder multiple times inside a statement doesn't work. PDO just translates the first occurance und leaves the second one as is.
    Pourtant mon test (#2) a fonctionné correctement !
    Dernière modification par Invité ; 14/01/2020 à 11h46.

  5. #5
    Modératrice
    Avatar de Celira
    Femme Profil pro
    Développeuse PHP/Java
    Inscrit en
    Avril 2007
    Messages
    8 633
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Développeuse PHP/Java
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2007
    Messages : 8 633
    Points : 16 372
    Points
    16 372
    Par défaut
    Par rapport à ton cas de départ, je suppose que tu passes ton tableau de paramètres directement à execute. La doc dit seulement :

    Un tableau de valeurs avec autant d'éléments qu'il y a de paramètres à associer dans la requête SQL qui sera exécutée. Toutes les valeurs sont traitées comme des constantes PDO:ARAM_STR.

    La liaison de plus de valeurs que spécifié n'est pas possible ; s'il y a plus de clés dans input_parameters que dans le code SQL utilisé pour PDO::prepare(), alors la requête préparée échouera et une erreur sera levée.
    Je suppose que le "autant d'éléments" est testé indifféremment du nom des paramètres, donc ":nomDiplome" est compté comme deux paramètres (ce qui est un peu stupide quand même )
    Modératrice PHP
    Aucun navigateur ne propose d'extension boule-de-cristal : postez votre code et vos messages d'erreurs. (Rappel : "ça ne marche pas" n'est pas un message d'erreur)
    Cherchez un peu avant poser votre question : Cours et Tutoriels PHP - FAQ PHP - PDO une soupe et au lit !.

    Affichez votre code en couleurs : [CODE=php][/CODE] (bouton # de l'éditeur) et [C=php][/C]

  6. #6
    Invité
    Invité(e)
    Par défaut
    Désolé, mais je peux en mettre autant que je veux : ça fonctionne !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    	$rech_query = "SELECT *
    				FROM contact
    				WHERE mail LIKE :search OR nom LIKE :search OR prenom LIKE :search
    				;";
      try {
    	$pdo_select = $pdo->prepare($rech_query);
    	$pdo_select->bindValue(':search', 	'%'.$search.'%',	PDO::PARAM_STR);
     
    	$pdo_select->execute();
    ...
    J'obtiens bien les résultats voulus.

  7. #7
    Modérateur

    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 799
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 799
    Points : 34 031
    Points
    34 031
    Billets dans le blog
    14
    Par défaut
    Oui, je viens de lire toute la doc de pdoPrepare et j'y ai trouvé plusieurs choses :
    Citation Envoyé par doc PHP
    Vous devez inclure un marqueur avec un nom unique pour chaque valeur que vous souhaitez passer dans la requête lorsque vous appelez PDOStatement::execute(). Vous ne pouvez pas utiliser un marqueur avec deux noms identiques dans une requête préparée, à moins que le mode émulation ne soit actif.
    Citation Envoyé par doc PHP
    With PDO_MYSQL you need to remember about the PDO::ATTR_EMULATE_PREPARES option.

    The default value is TRUE, like
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES,true);

    This means that no prepared statement is created with $dbh->prepare() call. With exec() call PDO replaces the placeholders with values itself and sends MySQL a generic query string.

    The first consequence is that the call $dbh->prepare('garbage');
    reports no error. You will get an SQL error during the $dbh->exec() call.
    The second one is the SQL injection risk in special cases, like using a placeholder for the table name.

    The reason for emulation is a poor performance of MySQL with prepared statements. Emulation works significantly faster.
    J'ai pourtant PDO::ATTR_EMULATE_PREPARES => false dans la création de ma connexion PDO !
    jreaux62, peut-être que ça fonctionne avec la valeur par défaut à TRUE ; je vais essayer mais si j'avais mis ça, je pense que c'est pour un autre souci, peut-être avec l'exécution des procédures... je ne sais plus.

    Citation Envoyé par doc PHP
    Attention using MySQL and prepared statements.
    Using a placeholder multiple times inside a statement doesn't work. PDO just translates the first occurance und leaves the second one as is.

    select id,name from demo_de where name LIKE :name OR name=:name

    You have to use

    select id,name from demo_de where name LIKE :name OR name=:name2

    and bind name two times. I don't know if other databases (for example Oracle or MSSQL) support multiple occurances. If that's the fact, then the PDO behaviour for MySQL should be changed.
    Je vais donc envoyer deux fois le paramètre, ce sera plus simple.

    Merci pour vos réponses
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole. Autoentrepreneur.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  8. #8
    Invité
    Invité(e)
    Par défaut
    Ma config :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    $pdo_conn['extraParam']	= array(
    	PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,		// rapport d'erreurs sous forme d'exceptions
    	PDO::ATTR_PERSISTENT => true, 						// Connexions persistantes
    	PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, 	// fetch mode par defaut
    	PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"	// encodage UTF-8
    	);
    Mes tests fonctionnent... mais je ne sais pas dire pourquoi !...

  9. #9
    Modérateur

    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 799
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 799
    Points : 34 031
    Points
    34 031
    Billets dans le blog
    14
    Par défaut
    Et bien c'est parce que tu as la valeur par défaut TRUE à PDO::ATTR_EMULATE_PREPARES.

    Je l'ai passé à TRUE et ça fonctionne.

    Je verrai si ça a d'autres conséquences mais pour le moment toutes mes requêtes sont passées. On va donc dire que c'est résolu.
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole. Autoentrepreneur.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

  10. #10
    Invité
    Invité(e)
    Par défaut
    Maintenant que tu en parles....

    ... il est fort possible que j'ai supprimé de ma config. (PDO::ATTR_EMULATE_PREPARES,false), il y a bien longtemps.
    Mais je ne me souviens pas si c'était pour cette raison.

    J'avoue que je code de façon empirique : "Tant que ça marche,... je ne touche à rien"

  11. #11
    Membre expert
    Avatar de Dendrite
    Femme Profil pro
    Développeuse informatique
    Inscrit en
    Juin 2008
    Messages
    2 129
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 58
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeuse informatique
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Juin 2008
    Messages : 2 129
    Points : 3 627
    Points
    3 627
    Billets dans le blog
    8
    Par défaut
    Et sinon, ceci pourrait-il avoir un rapport ?

    PDO:ARAM_STR (entier)
    Représente les types de données CHAR, VARCHAR ou les autres types de données sous forme de chaîne de caractères SQL.
    PDO:ARAM_STR_NATL (integer)
    Indicateur pour désigner une chaîne utilise le jeu de caractères national. Disponible à partir de PHP 7.2.0
    PDO:ARAM_STR_CHAR (integer)
    Indicateur pour désigner une chaîne utilise le jeu de caractères normal. Disponible à partir de PHP 7.2.0
    PDO, une soupe et au lit !
    Partir de la fin est un bon moyen de retrouver son chemin. Bibi - 2020

  12. #12
    Modérateur

    Avatar de CinePhil
    Homme Profil pro
    Ingénieur d'études en informatique
    Inscrit en
    Août 2006
    Messages
    16 799
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur d'études en informatique
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2006
    Messages : 16 799
    Points : 34 031
    Points
    34 031
    Billets dans le blog
    14
    Par défaut
    A priori, non, Dendrite, pas de rapport avec le Scmilblick.
    Philippe Leménager. Ingénieur d'étude à l'École Nationale Supérieure de Formation de l'Enseignement Agricole. Autoentrepreneur.
    Mon ancien blog sur la conception des BDD, le langage SQL, le PHP... et mon nouveau blog sur les mêmes sujets.
    « Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
    À la maison comme au bureau, j'utilise la suite Linux Mageïa !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Afficher deux fois la même valeur dans une requête
    Par Tess_06 dans le forum Requêtes et SQL.
    Réponses: 1
    Dernier message: 25/01/2019, 15h33
  2. Utiliser deux fois le même script dans la même page
    Par atc666 dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 10/01/2012, 09h17
  3. Réponses: 0
    Dernier message: 25/02/2010, 19h07
  4. requêter deux fois le même champ dans une table
    Par SpaceFrog dans le forum Requêtes
    Réponses: 6
    Dernier message: 26/11/2007, 13h44
  5. empecher d'avoir deux fois la même chose dans une listebox
    Par Seb4657 dans le forum Composants VCL
    Réponses: 3
    Dernier message: 25/03/2006, 21h26

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo