PHP 5.6.0beta1 released

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 ? Vous pouvez les voir comme une sorte de modèle compilé pour le SQL que vous voulez 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 vos applications si vous devez répéter la même requête plusieurs fois avec différents paramètres. En utilisant les requêtes préparées, vous évitez 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 driver le gère pour vous. Si votre application utilise exclusivement les requêtes préparées, vous pouvez être sûr qu'aucune injection SQL n'est possible (Cependant, si vous construisez d'autres parties de la requête en vous basant sur des entrées utilisateurs, vous continuez à prendre un risque).

Les requêtes préparées sont tellement pratiques que c'est l'unique fonctionnalité que PDO émule pour les drivers qui ne les supportent pas. 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 ré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 = ?");
if (
$stmt->execute(array($_GET['name']))) {
  while (
$row $stmt->fetch()) {
    
print_r($row);
  }
}
?>

Si le driver de la base de données le supporte, vous pouvez également 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 complexe à utiliser que les paramètres d'entrée car vous devez savoir la longueur d'un paramètre donné pourra atteindre lorsque vous le liez. Si la valeur retournée est plus longue que la taille qui vous auriez suggéré, une erreur sera émise.

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

<?php
$stmt 
$dbh->prepare("CALL sp_returns_string(?)");
$stmt->bindParam(1$return_valuePDO::PARAM_STR4000); 

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

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

Vous devez é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.

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

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

// 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(array($_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(array("%$_GET[name]%"));
?>

add a note add a note

User Contributed Notes 5 notes

up
16
adam at pyramidpower dot com dot au
4 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
oldmoscow dot mail dot ru
3 years ago
Note for MySQL: to use input/output parameters for stored procedures with PDO use PDO.Query() statement.
For example:

<?php
$dbh
->query("CAST SomeStoredProcedure($someInParameter1, $someInParameter2, @someOutParameter)");
$dbh->query("SELECT @someOutParameter");
?>

Or, if you want very much to use PDO.Prepare(), insert "SELECT @someOutParameter" in your stored procedure and then use:

<?php
$stmt
= $dbh->prepare("CAST SomeStoredProcedure(?, ?)");
$stmt ->execute(array($someInParameter1, $someInParameter2));
?>
up
-2
Sebastien Gourmand
9 months ago
Replace the query() by a prepare()/execute() for having one line like a sprintf()

old query() renamed for remember to secure.

<?php
class MyPDO extends PDO{

    const
DB_HOST='localhost';
    const
DB_PORT='3306';
    const
DB_NAME='test';
    const
DB_USER='root';
    const
DB_PASS='';

    public function
__construct($options=null){
       
parent::__construct('mysql:host='.MyPDO::DB_HOST.';port='.MyPDO::DB_PORT.';dbname='.MyPDO::DB_NAME,
                           
MyPDO::DB_USER,
                           
MyPDO::DB_PASS,$options);
    }

    public function
query($query){ //secured query with prepare and execute
       
$args = func_get_args();
       
array_shift($args); //first element is not an argument but the query itself, should removed

       
$reponse = parent::prepare($query);
       
$reponse->execute($args);
        return
$reponse;

    }

    public function
insecureQuery($query){ //you can use the old query at your risk ;) and should use secure quote() function with it
       
return parent::query($query);
    }

}

$db = new MyPDO();
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

$t1 = isset($_GET["t1"])?$_GET["t1"]:1; // need to be securised for injonction
$t2 = isset($_GET["t2"])?$_GET["t2"]:2; // need to be securised for injonction
$t3 = isset($_GET["t3"])?$_GET["t3"]:3; // need to be securised for injonction

$ret = $db->query("SELECT * FROM table_test WHERE t1=? AND t2=? AND t3=?",$t1,$t2,$t3);
//$ret = $db->insecureQuery("SELECT * FROM table_test WHERE t1=".$db->quote($t1));

while ($o = $ret->fetch())
{
    echo
$o->nom.PHP_EOL;
}
?>
up
-8
Anonymous
1 month ago
/** Добавить модификации документа */
    public function insert_doc_modification($doc_id , array $notice ,array $modification_fields)
    {
     
        $fields = array_keys($modification_fields);
        $sql = "INSERT INTO doc_modification
                    (doc_id , " . implode( ",", $fields ) . ")
                VALUES
                    (:doc_id , :" . implode( ", :", $fields ) . " )
                ON DUPLICATE KEY UPDATE
                    `hash` = :hash ; ";
       
        $stmt = $this->pdo->prepare($sql);  
        $stmt->bindParam(':doc_id',$doc_id);
        //Создать бинды по полям
        foreach($modification_fields as $name => $val)
        {
            $stmt->bindParam(':'.$name,  $modification_fields[$name]);
        }
        //Обойти все модификации по документу
        foreach($notice as $value)
        {
            foreach($modification_fields as $name => $val)
            {
                if(empty($value[$name])) continue ;
                $modification_fields[$name] = $value[$name];
            }
            $stmt->execute();
        }

    }
up
-19
Anonymous
3 years ago
Note for MySQL: to use input/output parameters for stored procedures with PDO use PDO.Query() statement.
For example:

<?php
$dbh
->query("CAST SomeStoredProcedure($someInParameter1, $someInParameter2, @someOutParameter)");
$dbh->query("SELECT @someOutParameter");
?>

Or, if you want very much to use PDO.Prepare(), insert "SELECT @someOutParameter" in your stored procedure and then use:

<?php
$stmt
= $dbh->prepare("CAST SomeStoredProcedure(?, ?)");
$stmt ->execute(array($someInParameter1, $someInParameter2));
?>
To Top