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_executefunction 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
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;
}