Page 1 sur 1

Faire des requêtes préparées avec l'extension mysql

Posté : 10 nov. 2010, 18:45
par stealth35
L'extension Mysql commence a bien vieillir et elle est maintenant uniquement en correction de bug, l'extension mysqli la remplace, et PDO possède aussi un driver mysql. (plus d'info ici : http://fr2.php.net/manual/fr/mysqli.overview.php)

Avec PDO on a vite pris l'habitude de faire des requêtes préparées, de part sa simplicité, mais aussi sa sécurité. Y'a malheureusement encore des résistants du mysql_* qui eux n'ont pas de requêtes préparées avec l'api.

Mais rien n'empêche d'en faire, une requête préparée c'est avant tout une requête :
-- http://dev.mysql.com/doc/refman/5.0/fr/sqlps.html
PREPARE stmt1 FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse';
SET @a = 3;
SET @b = 4;
EXECUTE stmt1 USING @a, @b;
il nous faut donc :
- un nom de statement : stmt1
- une requête : SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse
- des variables avec leur valeurs : SET @a = 3;, SET @b = 4;
- et la requête final qui elle attribut les variables aux paramètres ( les ? )

le @a remplace le 1er ?
le @b remplace le 2eme ?

ce qui sera donc équivalant de :
SELECT SQRT(POW(3,2) + POW(4,2)) AS hypotenuse
l'avantage est qu'on peux exécuter la requête autant de fois qu'on veux en changement juste les paramètres :
SET @a = 7, @b = 8;
EXECUTE stmt1 USING @a, @b;
SET @a = 14, @b = 23;
EXECUTE stmt1 USING @a, @b;
Comment faire :

sous php ca donnerait
ini_set('mysql.trace_mode', true);
mysql_connect('127.0.0.1', 'root', '');

mysql_query("PREPARE stmt1 FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse'");
mysql_query("SET @a = 3");
mysql_query("SET @b = 4");

//ou juste mysql_query("SET @a = 3, @b = 4");
$result = mysql_query("EXECUTE stmt1 USING @a, @b");

echo '<pre>', print_r(mysql_fetch_assoc($result), true), '</pre>';
mysql_free_result($result);
ca peux vite devenir assez lourds a gerer, c'est pour ca que des extensions comme PDO ou mysqli propose une fonction 'prepare' et 'execute' plus ou moins dans le même esprit qui reviens a :
prepare('SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse');
execute(array(3, 4));
c'est qu'on va faire, une fonction mysql_prepare et une mysql_execute, dont voici le code :

mysql_prepare
function mysql_prepare($query)
{
    $stmt = uniqid(mysql_thread_id());
    $prep = sprintf('PREPARE `%s` FROM \'%s\'', $stmt, mysql_real_escape_string($query));
    
    if(mysql_query($prep))
    {
        return $stmt;
    }
    
    return false;
}
mysql_execute
function mysql_execute(array $input_parameters = array(), $stmt)
{
    foreach($input_parameters as $id => $input_parameter)
    {            
        $key = sprintf('@`%s`', $id);
        
        if(is_numeric($input_parameter))
        {
            $sf = '@`%s` = %s';
        }
        else
        {           
            $sf = '@`%s` = \'%s\'';
        }
        
        $sets[$key] = sprintf($sf, $id, mysql_real_escape_string((string) $input_parameter));
    }

    if(!empty($sets))
    {
        $set = sprintf('SET %s', implode(', ', $sets));
        
        if(mysql_query($set) === false)
        {
            return false;
        }
        
        $ext = sprintf('EXECUTE `%s` USING %s', $stmt, implode(', ', array_keys($sets)));
    }
    else
    {
        $ext = sprintf('EXECUTE `%s`', $stmt);
    }        
    
    return mysql_query($ext);        
}
et le code pour faire notre requête :
$stmt = mysql_prepare('SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse');
$result = mysql_execute(array(3, 4), $stmt);
echo '<pre>', print_r(mysql_fetch_assoc($result), true), '</pre>';
mysql_free_result($result) 
Details :

mysql_prepare :
mysql_prepare attend juste la requête en paramètre

- on crée un identifiant pour le statement (sur la doc mysql c'était stmt1) , grace a uniqid et on lui met en prefix le thread de la connexion pour renforcer l'unicité .
- on crée notre requete mysql_real_escape_string étant la pour échapper les caractères puisque'il faut pas oublier que c'est qu'une requête SQL comme toutes les autres.
- on execute notre requête et si ca a marcher on retourne l'identifiant du statement ce paramètre est très important

mysql_execute :
mysql_execute attend les valeurs a insérer et l'identifiant du statement

- On boucle donc sur notre tableau de valeur pour créer les @truc = machin, si c'est un nombre on fera @truc = 25 , si c'est autre chose @truc = 'machin'
le @truc étant la valeur de la clé courante du tableau donc @0 = 3, @1 = 4 pour array(3, 4), avec array('trois' => 3, 'quatre' => 4) on va avoir @trois = 3, @quarte = 4 (les valeurs étant toutes protégées par des `)
- On a notre tableau qui contient nos @truc = machin et chaque clé correspond à @truc
- Si on a des paramètres dans notre requête on creer le : SET @a = 3, @b = 4
Et on exécute EXECUTE stmt1 USING @a, @b
- Sinon juste EXECUTE stmt1
- On retourne le resultat :wink:


Tout est simplifié au maximum tout en gardant les erreurs activées ( le mysql.trace_mode ou faire des or exit/die vous renverra bien sur les erreurs), la gestion des types étant très survolée.


Et en bonus le mysql_fetch_all (le type correspond au mysql_fetch_*):
function mysql_fetch_all($result, $type = 'array')
{
    if($result === false)
    {
        return false;
    }        
       
    $func = 'mysql_fetch_' . strtolower($type);   
        
    while($row = call_user_func($func, $result))
    {            
        if($row !== false)
        {
            $rows[] = $row;
        }
        else
        {
            return false;
        }
    }
        
    mysql_free_result($result);
    
    if(!empty($rows))
    {
        return $rows;
    }
    
    return false;
}

Re: Faire des requêtes préparées avec l'extension mysql

Posté : 10 nov. 2010, 21:55
par moogli
salut,

intéressant, par contre je ne trouve pas d'exemple pratique à l'utilisation d'une requête préparé en php dans le cadre d'une page web.
En aurai tu un ou deux ?

Merci

@+

Re: Faire des requêtes préparées avec l'extension mysql

Posté : 10 nov. 2010, 22:13
par stealth35
salut,

intéressant, par contre je ne trouve pas d'exemple pratique à l'utilisation d'une requête préparé en php dans le cadre d'une page web.
En aurai tu un ou deux ?

Merci

@+
Les requêtes préparées prennent surtout leur intérêts en cas de requete multiple, un INSERT OU UPDATE, regarde par exemple l'autre sujet pour convertir en CSV en une base SQLite.

L'autre avantage est aussi au niveau sécurité la requete ne peux pas être modifié, imagine un formulaire type commentaire, avec un pseudo, un email, et le comm
pour peu que tout sois remplis (et dans l'ordre) tu peux direct faire :
$stmt = mysql_prepare("INSERT INTO comm SET pseudo = ?, email = ?, comm = ?");
mysql_execute($_POST);
t'y gagne en simplicité et en lisibilité comparer a
mysql_query("INSERT INTO comm SET pseudo = '" . mysql_real_escape_string($_POST['pseudo']). "', email =  '" . mysql_real_escape_string($_POST['email']). "', comm =  '" . mysql_real_escape_string($_POST['comm']). "'");
et meme si y'avais pas mysql_real_escape_string dans l'api les injections ne peuvent pas se faire (enfin si mais tu pourras pas faire grand chose)

Re: Faire des requêtes préparées avec l'extension mysql

Posté : 10 nov. 2010, 22:45
par moogli
hum wé dans le cas du CSV pourquoi pas, bien que je sache pas l'inpact entre faire un insert multiple (insert into table values (),()) que je fait dans ces cas où la requête préparé.

pour ce qui est de l'insertion de $_POST directement j'ai toujours un nom de bouton qui traine :d et j'avoue que je préfère fair ma tambouille seul qui a utiliser un array_walk pour faire moins moche.

par contre en terme de lisibilité c'est certain c'est un poil plus clair, bien que laissant un flou si, contrairement à ce que tu dit) il n'y a pas de vérif des champs avant.

Merci pour la réponse

Re: Faire des requêtes préparées avec l'extension mysql

Posté : 11 nov. 2010, 04:19
par stealth35
hum wé dans le cas du CSV pourquoi pas, bien que je sache pas l'inpact entre faire un insert multiple (insert into table values (),()) que je fait dans ces cas où la requête préparé.
sur 500 ligne toi tu feras 500 requêtes différentes, alors qu'en préparé tu feras un template en cache avec juste les paramètres qui changent.

Re: Faire des requêtes préparées avec l'extension mysql

Posté : 11 nov. 2010, 04:38
par moogli
heu ben une requete avec 500 couple de valeurs (insert into table values (couple1)(couple2)(couple3) .....(couple 500)) est ce équivalent à faire 500 variables ? (j'ai des lacunes de ce coté la :)).

@+

Re: Faire des requêtes préparées avec l'extension mysql

Posté : 13 nov. 2010, 04:38
par stealth35
heu ben une requete avec 500 couple de valeurs (insert into table values (couple1)(couple2)(couple3) .....(couple 500)) est ce équivalent à faire 500 variables ? (j'ai des lacunes de ce coté la :)).

@+
oui ca sera pareil, si tu veux plus d'infos sur les requetes préparer je t'invite a voir les liens suivant
http://www.siteduzero.com/tutoriel-3-36 ... paree.html
http://www.php.net/manual/fr/pdo.prepar ... ements.php

Re: Faire des requêtes préparées avec l'extension mysql

Posté : 03 mars 2011, 03:07
par stealth35
Pour ceux qui seraient intéressés par le projet voici les derniers ajouts :
  • Support du $link
  • Support du fetch group
  • Ajout des erreurs liés aux paramètres de fonction
L'adresse du projet : https://github.com/stealth35/mysql_prepare

:wink: