RequĂȘtes prĂ©parĂ©es et procĂ©dures stockĂ©es

La plupart des bases de donnĂ©es supportent le concept des requĂȘtes prĂ©parĂ©es. Qu'est-ce donc ? Il est possible de les voir comme une sorte de modĂšle compilĂ© pour le SQL que l'on veut exĂ©cuter, qui peut ĂȘtre personnalisĂ© en utilisant des variables en guise de paramĂštres. Les requĂȘtes prĂ©parĂ©es offrent deux fonctionnalitĂ©s essentielles :

  • La requĂȘte ne doit ĂȘtre analysĂ©e (ou prĂ©parĂ©e) qu'une seule fois, mais peut ĂȘtre exĂ©cutĂ©e plusieurs fois avec des paramĂštres identiques ou diffĂ©rents. Lorsque la requĂȘte est prĂ©parĂ©e, la base de donnĂ©es va analyser, compiler et optimiser son plan pour exĂ©cuter la requĂȘte. Pour les requĂȘtes complexes, ce processus peut prendre assez de temps, ce qui peut ralentir les applications si l'on doit rĂ©pĂ©ter la mĂȘme requĂȘte plusieurs fois avec diffĂ©rents paramĂštres. En utilisant les requĂȘtes prĂ©parĂ©es, l'on Ă©vite ainsi de rĂ©pĂ©ter le cycle analyse/compilation/optimisation. Pour rĂ©sumer, les requĂȘtes prĂ©parĂ©es utilisent moins de ressources et s'exĂ©cutent plus rapidement.
  • Les paramĂštres pour prĂ©parer les requĂȘtes n'ont pas besoin d'ĂȘtre entre guillemets ; le pilote gĂšre cela. Si l'application utilise exclusivement les requĂȘtes prĂ©parĂ©es, il est possible d'ĂȘtre sĂ»r qu'aucune injection SQL n'est possible (Cependant, si l'on construit d'autres parties de la requĂȘte en se basant sur des entrĂ©es utilisateurs, l'on continue Ă  prendre un risque).

Les requĂȘtes prĂ©parĂ©es sont tellement pratiques que c'est l'unique fonctionnalitĂ© que PDO Ă©mule pour les pilotes qui ne les prennent pas en charge. Ceci assure de pouvoir utiliser la mĂȘme technique pour accĂ©der aux donnĂ©es, sans se soucier des capacitĂ©s de la base de donnĂ©es.

Exemple #1 Insertions rĂ©pĂ©titives en utilisant les requĂȘtes prĂ©parĂ©es

Cet exemple effectue une requĂȘte INSERT en y substituant un nom et une valeur pour les marqueurs nommĂ©s.

<?php
$stmt
= $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);

// insertion d'une ligne
$name = 'one';
$value = 1;
$stmt->execute();

// insertion d'une autre ligne avec des valeurs différentes
$name = 'two';
$value = 2;
$stmt->execute();
?>

Exemple #2 Insertions rĂ©pĂ©tĂ©es en utilisant des requĂȘtes prĂ©parĂ©es

Cet exemple effectue une requĂȘte INSERT en y substituant un nom et une valeur pour les marqueurs ?.

<?php
$stmt
= $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $value);

// insertion d'une ligne
$name = 'one';
$value = 1;
$stmt->execute();

// insertion d'une autre ligne avec différentes valeurs
$name = 'two';
$value = 2;
$stmt->execute();
?>

Exemple #3 RĂ©cupĂ©ration des donnĂ©es en utilisant des requĂȘtes prĂ©parĂ©es

Cet exemple récupÚre des données basées sur la valeur d'une clé fournie par un formulaire. L'entrée utilisateur est automatiquement échappée, il n'y a donc aucun risque d'attaque par injection SQL.

<?php
$stmt
= $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
$stmt->execute([$_GET['name']]);
foreach (
$stmt as $row) {
print_r($row);
}
?>

Exemple #4 Appel d'une procédure stockée avec un paramÚtre de sortie

Si le pilote de la base de données le prend en charge, il est également possible de lier des paramÚtres aussi bien pour l'entrée que pour la sortie. Les paramÚtres de sortie sont utilisés typiquement pour récupérer les valeurs d'une procédure stockée. Les paramÚtres de sortie sont un peu plus complexes à utiliser que les paramÚtres d'entrée car on doit savoir la longueur qu'un paramÚtre donné pourra atteindre lorsqu'on le lie. Si la valeur retournée est plus longue que la taille que l'on aura suggérée, une erreur sera émise.

<?php
$stmt
= $dbh->prepare("CALL sp_returns_string(?)");
$stmt->bindParam(1, $return_value, PDO::PARAM_STR, 4000);

// Appel de la procédure stockée
$stmt->execute();

print
"La procédure a retourné : $return_value\n";
?>

Exemple #5 Appel d'une procédure stockée avec un paramÚtre d'entrée/sortie

Il faut également spécifier les paramÚtres qui gÚrent les valeurs aussi bien pour l'entrée que pour la sortie ; la syntaxe est similaire aux paramÚtres de sortie. Dans le prochain exemple, la chaßne 'Bonjour' est passée à la procédure stockée et lorsqu'elle retourne la valeur, 'Bonjour' est remplacée par la valeur retournée par la procédure.

<?php
$stmt
= $dbh->prepare("CALL sp_takes_string_returns_string(?)");
$value = 'hello';
$stmt->bindParam(1, $value, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 4000);

// appel de la procédure stockée
$stmt->execute();

print
"La procédure a retourné : $value\n";
?>

Exemple #6 Utilisation invalide de marqueur

<?php
$stmt
= $dbh->prepare("SELECT * FROM REGISTRY where name LIKE '%?%'");
$stmt->execute([$_GET['name']]);

// un marqueur doit ĂȘtre utilisĂ© Ă  la place d'une valeur complĂšte
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE ?");
$stmt->execute(["%$_GET[name]%"]);
?>
add a note

User Contributed Notes 3 notes

up
219
adam at pyramidpower dot com dot au ¶
16 years ago
Note that when using name parameters with bindParam, the name itself, cannot contain a dash '-'. 

example:
<?php
$stmt = $dbh->prepare ("INSERT INTO user (firstname, surname) VALUES (:f-name, :s-name)");
$stmt -> bindParam(':f-name', 'John');
$stmt -> bindParam(':s-name', 'Smith');
$stmt -> execute();
?>

The dashes in 'f-name' and 's-name' should be replaced with an underscore or no dash at all.

See http://bugs.php.net/43130

Adam
up
3
theking2(at)king.ma ¶
2 years ago
Example #5 gives an 1414 wenn tried on MariaDB. Use this function to call a stored procedure with the last parameter as INOUT returning a value like a (uu)id or a count;

<?php
/**
 * call_sp Call the specified stored procedure with the given parameters.
 * The first parameter is the name of the stored procedure.
 * The remaining parameters are the (in) parameters to the stored procedure.
 * the last (out) parameter should be an int like state or number of affected rows.
 *
 * @param  mixed $sp_name The name of the stored procedure to call.
 * @param  mixed $params The parameters to pass to the stored procedure.
 * @return int The number of affected rows.
 */
function call_sp( \PDO $db, string $sp_name, ...$params ): mixed
{
  $placeholders   = array_fill( 0, count( $params ), "?" );
  $placeholders[] = "@new_id";

  $sql = "CALL $sp_name( " . implode( ", ", $placeholders ) . " ); SELECT @new_id AS `new_id`";

  try {
    LOG->debug( "calling Stored Procedure", [ "sql" => $sql ] );

    $stmt = $db->prepare( $sql );
    $i    = 0;
    foreach( $params as $param ) {
      $stmt->bindValue( ++$i, $param );
    }
    $stmt->execute();
    $new_id = $stmt->fetch( PDO::FETCH_ASSOC )['new_id'];

    return $new_id;

  } catch ( \Exception $e ) {
    LOG->error( "Error calling Stored Procedure", [ "sql" => $sql, "params" => $params, "error" => $e->getMessage() ] );
    throw $e;
  }
up
7
w37090 at yandex dot ru ¶
6 years ago
Insert a multidimensional array into the database through a prepared query:
We have an array to write the form:

$dataArr:
Array
(
    [0] => Array
        (
            [0] => 2020
            [1] => 23
            [2] => 111111
        )
 
    [1] => Array
        (
            [0] => 2020
            [1] => 24
            [2] => 222222222
        )
....

Task: prepare a request and pass through binds
$array = [];
foreach ($dataArr as $k=>$v) {
// $x = 2020, the variable is predetermined in advance, does not change the essence
$array[] = [$x, $k, $v];
}
$sql = ("INSERT INTO `table` (`field`,`field`,`field`) VALUES (?,?,?)");

$db->queryBindInsert($sql,$array);

public function queryBindInsert($sql,$bind) {
        $stmt = $this->pdo->prepare($sql);
 
        if(count($bind)) {
            foreach($bind as $param => $value) {
                $c = 1;
                for ($i=0; $i<count($value); $i++) {
                    $stmt->bindValue($c++, $value[$i]);
                }
                $stmt->execute();
            }
        }
    }