Dichiarazioni preparate (prepared statements) e parametri associati

Una dichiarazione preparata è una funzionalità utilizzata per eseguire ripetutamente le stesse (o simili) istruzioni SQL con elevata efficienza.

Le dichiarazioni preparate funzionano fondamentalmente in questo modo:

  1. Preparazione: si crea un modello di dichiarazione SQL e lo si invia al database.Alcuni valori, chiamati “parametri”, non vengono specificati (sono indicati con "?"). Esempio: INSERT INTO MyGuests VALUES(?, ?, ?)
  2. Il database analizza, compila ed esegue l'ottimizzazione della query sul modello di dichiarazione SQL e memorizza il risultato senza eseguirlo
  3. Esecuzione: in un secondo momento, l'applicazione associa i valori ai parametri e il database esegue l'istruzione. L'applicazione può eseguire l'istruzione tutte le volte che vuole con valori diversi

Rispetto all'esecuzione diretta di dichiarazioni SQL, le dichiarazioni preparate presentano tre vantaggi principali:

  • Le dichiarazioni preparate riducono il tempo di analisi poiché la preparazione della query viene eseguita solo una volta (sebbene l'istruzione venga eseguita più volte)
  • I parametri associati riducono al minimo la larghezza di banda del server poiché è necessario inviare ogni volta solo i parametri e non l'intera query
  • Le dichiarazioni preparate sono molto utili contro le iniezioni SQL, perché i valori dei parametri, che vengono trasmessi in seguito utilizzando un protocollo diverso, non devono essere “escaped”. Se il modello di dichiarazioni originale non è derivato da un input esterno, l'iniezione SQL non può verificarsi.

Dichiarazioni preparate in MySQLi

L'esempio seguente utilizza dichiarazioni preparate e parametri associati in MySQLi:

         
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "mioDB";

// Crea connessione
$conn = new mysqli($servername, $username, $password, $dbname);

// Controlla connessione
if ($conn->connect_error) {
  die("Connessione fallita: " . $conn->connect_error);
}

// prepara e vincola
$stmt = $conn->prepare("INSERT INTO Ospiti (nome, cognome, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $nome, $cognome, $email);

// imposta i parametri ed esegui
$nome = "Mario";
$cognome = "Rossi";
$email = "mario@esempio.it";
$stmt->execute();

$nome = "Maria";
$cognome = "bianchi";
$email = "maria@esempio.it";
$stmt->execute();

$nome = "Giovanni";
$cognome = "Verdi";
$email = "giovanni@esempio.it";
$stmt->execute();

echo "Nuovi record creati con successo";

$stmt->close();
$conn->close();
?>
         
     

Spiegazione del codice precedente:

         
INSERT INTO Ospiti (nome, cognome, email) VALUES (?, ?, ?)"
               
     

Nel nostro codice SQL, inseriamo un punto interrogativo (?) come sostituto del valore intero, stringa, double o blob che vogliamo inserire.

Ora guardiamo la funzione bind_param():

         
$stmt->bind_param("sss", $nome, $cognome, $email); 
     

Questa funzione associa i parametri alla query SQL e indica al database quali sono i parametri. L'argomento "sss" definisce i tipi di dati dei parametri. Il carattere s dice a mysql che il parametro è una stringa.

L'argomento può essere di quattro tipi:

  • i - integer
  • d - double
  • s - string
  • b - BLOB

Deve essere specificato uno di questi argomenti per ogni parametro.

Dicendo a mysql che tipo di dati aspettarsi, riduciamo al minimo il rischio di SQL injection.

Nota: se vogliamo inserire dati da fonti esterne (come l'input dell'utente), è molto importante che i dati siano sanificati e convalidati.

Dichiarazioni preparate in PDO

L'esempio seguente utilizza dichiarazioni preparate e parametri associati in PDO:

        
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "mioDBPDO";

try {
  $conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
  // imposta la modalità errore di PDO in exception
  $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

  // prepara sql e vincoal i parametri
  $stmt = $conn->prepare("INSERT INTO Ospiti (nome, cognome, email)
  VALUES (:nome, :cognome, :email)");
  $stmt->bindParam(':nome', $nome);
  $stmt->bindParam(':cognome', $cognome);
  $stmt->bindParam(':email', $email);

  // inserisci una riga
  $nome = "Mario";
  $cognome = "Rossi";
  $email = "mario@esempio.it";
  $stmt->execute();

  // inserisci un'altra riga
  $nome = "Maria";
  $cognome = "Bianchi";
  $email = "maria@esempio.it";
  $stmt->execute();

  // inserisci un'altra riga
  $nome = "Giovanni";
  $cognome = "Verdi";
  $email = "giovanni@esempio.it";
  $stmt->execute();

  echo "Nuovi record creati con successo";
} catch(PDOException $e) {
  echo "Error: " . $e->getMessage();
}
$conn = null;
?>