70 Preparazione ai Prepared statements (parte 2)

Ricapitolando, una volta loggati come docenti, sceglievamo di creare un questionario e dovevamo generare le domande da inserire ne questionario in base all’argomento. Riporto i 2 form quello argomento che una volta scelto, mi genera il secondo che contiene i checkbox delle domande per quell’argomento:

Descrizione questionario:

Scegliere un argomento_:

Scegli le domande che compongono il questionario
Se parliamo di centurioni…
Napoleone visse all’incirca nel

Una volta checkate le domande registreremo il nuovo questionario nel database in questo modo:

viene aggiunta una riga nella tabella questionari con descrizione, la data che la faremo generare in automatico, l’ id dello user che diventa la chiave esterna nel campo fkuser che è quel dato che ci stiamo passando da una pagina all’altra come variabile di sessione.

Inoltre i dati delle varie domande scelte per questo questionario, dovranno essere inseriti dentro la tabella items in questo modo:

l’ id del questionario assegnato poco prima nella tabella questionario associato alla descrizione he abbiamo inserito nel form, che sarà inserito nel campo fkQuestionario della tabella items

Tenendo fisso il codice associato al questionario, il programma inserirà l’ id delle varie domande checkate per quell’argomento. Dalla corrispondenza dell’id poi verranno richiamate le domande associate dalla tabella domande.

In questo caso avremo l’inserimento di più id domande con lo stesso id argomento dentro la tabella items. se avessimo 20 domande scelte, avremmo 20 istruzioni SQL di insert id domande in items con id argomento uguale. Avremo quindi 20 istruzioni SQL simili. Qui entra in gioco l’efficacia dei prepared statments.

Analizziamo ora il codice della nuova pagina registra_dati_questionario.php, saltando la parte iniziale, vista e rivista, di connessione col database:

$domande = $_POST[‘domande_scelte’];
for($i=0; $i<count($domande); $i++)
echo $domande[$i].$nl;

$_POST[‘domande_scelte’] fa riferimento alla compilazione del form della pagina crea_questionario.php dentro la <select> ho inserito i tags <input> type checkbox che ha come name ‘domande_scelte’ che passerà i dati alla pagina registra_dati_questionario.php. Ecco il pezzo di codice della pagina crea_questionario.php

 $risultato = @mysqli_query($conn, $comandoSQL);
while ($riga = mysqli_fetch_assoc($risultato) )
{
echo “<input type=’checkbox’ name=’domande_scelte[]’ “.
“value='”.$riga[‘iddomanda’].”‘>”;
echo $riga[‘testo_domanda’].”\n”.$nl;
}
echo $nl.”<input type=’submit’ name=’ok’ “.
“value=’Registra i dati del questionario’ />\n”;
echo “</form>”

I checkbox saranno tanti quante le domande flaggate, quindi $domande sarà un array.

Ora vediamo il codice per inserire nella tabella questionari la descrizione, la data e il codice dello user (docente)

$data_oggi = date(“Y-m-d”);
$comandoSQL = “insert into questionari values (null, ” .
“‘” . mysqli_escape_string($conn, $_POST[‘descrizione’]) . “‘, ” .
“‘” . $data_oggi . “‘, ” .
“‘” . $_SESSION[‘iduser’] . “‘)”;

Si deve rispettare l’ordine dei campi della tabella, quindi il primo è null perché è la chiave primaria. L’id user è memorizzato nella variabile di sessione.

$esito = mysqli_query($conn, $comandoSQL);

Eseguo il comando con la mysqli query e memorizzo il risultato nella variabile $esito.

Creo una condizione che se l’esito è ok recupero l’ultimo id inserito con il comando mysqli_insert_id che ci servirà per compilare i campi della tabella items:

  if ($esito)
$idQuestionario = mysqli_insert_id( $conn );
else
{
mysqli_close($conn);
header(“Location: login.php?errore=inserimento_fallito”); //inserimento fallito
}

Ora è il momento di far entrare in gioco i prepared statments, ma prima analizziamone i vantaggi:

  • Maggiore performance

    Ogni volta che eseguiamo un comando con la mysqli query, è necessario trasferire la query (la stringa del comando SQL) al server, il quale dovrà effettuarne l’analisi sintattica, verificare l’utente e i suoi privilegi, decidere in merito all’execution plain del database server (organizzazione del motore del tipo di database che gestisce campi e tabelle)

  • Maggiore chiarezza

    Basta analizzare il precedente codice del precedente comando $comandoSQL che impegna 4 righe di codice, incastrando variabili, richiamando mysqli_escape e unendo parti di codice per capire che il comando prepared statments che sta su 1 riga è nettamente più semplice. Inoltre vado proprio ad assegnae i valori ai campi interessati ($fkQuestionario, $fkDomanda)

  • Minor rischio

    Con i prepared statments i valori che vengono recuperati (per esempio da una form), non vengono incastrati in stringhe (come nel comando $comandoSQL ), ma avviene un passaggio secco dei valori che non da margine a tentativi di sql inject.

  • Ecco il codice che dichiara i prepared statments:
$comando_prepared =  mysqli_prepare($conn, “insert into items values (null, ?, ?)”);

Ora bisogna legare (bind) i valori dei campi fkQuestionario e fkDomanda ai punti interrogativi:

mysqli_stmt_bind_param($comando_prepared, “ii”, $fkQuestionario, $fkDomanda);

Notiamo che la doppia i significa che i 2 campi che andremo a scrivere sono interi (i=intero, d=double, s=stringa)

Recuperiamo l’array contenete i codici delle domande e un semplice ciclo for che assegna il valore alle variabili di bind che andranno a sostituire i punti di domanda :

$domande = $_POST[‘domande_scelte’];
for($i=0; $i<count($domande); $i++)
{
$fkQuestionario=$idQuestionario;
$fkDomanda=$domande[$i];
mysqli_stmt_execute($comando_prepared);
}

L’ultimo comando è quello che esegue il prepared statments. Infine chiudiamo con l’apposito comando di chiusura:

mysqli_stmt_close($comando_prepared);
mysqli_close($conn);

Non bisogna confondere i prepared statments con i stored procedure memorizzate in modo permanente sul server che funzionano con un meccanismo di parametrizzazione. Anche se simili e con gli stessi vantaggi, i prepared statments funzionano con la cache e il loro processo muore con la terminazione dell’applicativo, hanno una memorizzazione temporanea. Sono disponibili con gli oggetti e nello specifico con i PDO che vedremo nella prossima lezione.

Ecco il codice completo di crea_questionario.php

<?php
include($_SERVER[‘DOCUMENT_ROOT’].”/fagtest/my_include/setup_con_DB.php”);
  if (  !isset($_SESSION[‘iduser’]) )
{
header(“Location: login.php?errore=autenticazione_richiesta”); //user non autenticato
exit;
}

//connessione e selezione db
$conn = @mysqli_connect(“localhost”,$accessData[‘username’],$accessData[‘password’]);

if(!$conn)
{ echo “Connessione al server fallita. Impossibile procedere. Contattare …”;
die;
}

if ( !@mysqli_select_db($conn, $accessData[‘dbname’]) )
{
echo “Non trovo il data base …”;
die;
}

?>

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0 Transitional//EN”>
<html>
<head>
<title>CREAZIONE QUESTIONARIO</title>
</head>
<body>
<form name=”argomento” action=”crea_questionario.php” method=”post”>
Descrizione questionario:
<input type=”text” name=”descrizione”
//se siamo ritornati sulla pagina dopo aver scelto l’argomento riproponiamo
//la descrizione precedentemente inserita
<?php
if( isset($_POST[‘descrizione’]) )
echo ” value='”.$_POST[‘descrizione’].”‘”;
echo ” />”; //fine input
?>
<br />
Scegliere un argomento_:
<select name=”elenco_argomenti”>
<?php
//leggiamo elenco argomenti dalla tabella argomenti
$comandoSQL = “select * from argomenti”;
$risultato = @mysqli_query($conn, $comandoSQL);

while ($riga = mysqli_fetch_assoc($risultato) )
{
echo “<option value='”.$riga[‘idargomento’].”‘ “;

//siamo ritornati su questa pagina dopo aver scelto l’argomento
//e siamo sulla option corrispondente alla scelta fatta? -> selected
if( isset($_POST[‘elenco_argomenti’]) &&
$riga[‘idargomento’]==$_POST[‘elenco_argomenti’])
echo “selected”;

echo “> “.$riga[‘argomento’].”</option>\n”;
}
?>
</select>
<br />
<input type=”submit” name=”ok” value=”Domande per l’argomento scelto” />

</form>
<?php
//solo se avevamo già scelto l’argomento crea una form con la lista delle domande
//abbinabili
if( isset($_POST[‘elenco_argomenti’]) )
{
echo “<form name=’scelta_domande’ action=’registra_dati_questionario.php'”.
” method=’post’>\n”;

//descrizione riproposta come campo nascosto …
echo “<input type=’hidden’ name=’descrizione’ value='”.$_POST[‘descrizione’].”‘ />\n”;

echo “Scegli le domande che compongono il questionario “.$nl;
$comandoSQL = “select * from domande where fkargomento='”.
$_POST[‘elenco_argomenti’].”‘”;

$risultato = @mysqli_query($conn, $comandoSQL);
while ($riga = mysqli_fetch_assoc($risultato) )
{
echo “<input type=’checkbox’ name=’domande_scelte[]’ “.
“value='”.$riga[‘iddomanda’].”‘>\n”;
echo $riga[‘testo_domanda’].”\n”.$nl;

}
echo $nl.”<input type=’submit’ name=’ok’ “.
“value=’Registra i dati del questionario’ />\n”;
echo “</form>”;
}

mysqli_close($conn);
?>

</body>
</html>

e quello di registra_dati_questionario.php

<?php
include($_SERVER[‘DOCUMENT_ROOT’].”/fagtest/my_include/setup_con_DB.php”);

if (  !isset($_SESSION[‘iduser’]) )
{
header(“Location: login.php?errore=autenticazione_richiesta”); //user non autenticato
exit;
}

//connessione e selezione db
$conn = @mysqli_connect(“localhost”,$accessData[‘username’],$accessData[‘password’]);

if(!$conn)
{ echo “Connessione al server fallita. Impossibile procedere. Contattare …”;
die;
}

if ( !@mysqli_select_db($conn, $accessData[‘dbname’]) )
{
echo “Non trovo il data base …”;
die;
}

  //prima inseriamo la riga del questionario nella tabella questionari
$data_oggi = date(“Y-m-d”);
$comandoSQL = “insert into questionari values (null, ” .
“‘” . mysqli_escape_string($conn, $_POST[‘descrizione’]) . “‘, ” .
“‘” . $data_oggi . “‘, ” .
“‘” . $_SESSION[‘iduser’] . “‘)”;

$esito = mysqli_query($conn, $comandoSQL);

  if ($esito)
$idQuestionario = mysqli_insert_id( $conn );
else
{
mysqli_close($conn);
header(“Location: login.php?errore=inserimento_fallito”); //inserimento fallito
}

//per gli N insert into per la tabella degli items conviene usare
//i prepared statement
$comando_prepared =
mysqli_prepare($conn, “insert into items values (null, ?, ?)”);

//i=intero, d=double, s=stringa
mysqli_stmt_bind_param($comando_prepared, “ii”, $fkQuestionario, $fkDomanda);

$domande = $_POST[‘domande_scelte’];
for($i=0; $i<count($domande); $i++)
{
$fkQuestionario=$idQuestionario;
$fkDomanda=$domande[$i];
mysqli_stmt_execute($comando_prepared);
}

mysqli_stmt_close($comando_prepared);
mysqli_close($conn);
?>