Inserimento dei dati

Adesso che sappiamo connetterci al server MySQL vediamo come effettuare le query. Cominciamo con un inserimento dati, che segue la sintassi:

INSERT INTO 
   nome_tabella 
SET 
   nome_campo_1 = valore_1,
   nome_campo_2 = valore_2

oppure:

INSERT INTO nome_tabella 
   (nome_campo_1, nome_campo_2) 
VALUES 
   (valore_1, valore_2)

In generale per lanciare una query in uno script PHP dobbiamo semplicemente chiamare la funzione mysql_query() a cui passare come parametro la stringa contenete la query. Nel caso di un inserimento mysql_query() restituisce TRUE o FALSE, a seconda dell'esito dell'operazione. Ci basta quindi controllare tale valore per verificare se i dati sono stati correttamente immessi nella tabella. In caso negativo con la funzione mysql_error() possiamo ottenere maggiori indicazioni su cosa è andato storto.

Il seguente esempio fa riferimento al database ed alla tabella presentati nella Lezione 2. La connessione avviene grazie agli script config.php e connect.php presentati nella Lezione 4.

<?php
// richiamo il file di configurazione
require 'config.php';

// richiamo lo script responsabile della connessione a MySQL
require 'connect.php';

// preparo la query
$query = "INSERT INTO utenti (nome,email,sesso,newsletter,attivita,messaggio)
		VALUES ('Mario Rossi','m.rossi@email.it',1,1,2,'inviatemi del materiale illustrativo')";

// lancio la query
$result = mysql_query($query);

// controllo l'esito
if (!$result) {
	die("Errore nella query $query: " . mysql_error());
}

// chiudo la connessione a MySQL
mysql_close();

echo 'Query eseguita correttamente';
?>

E' importante osservare che si è preferito non passare direttamente alla funzione mysql_query() la stringa contenente la query. Tale stringa è stata memorizzata nella variabile $query perché potrebbe risultare utile mostrarla in caso di errore, soprattutto se è stata costruita dinamicamente in funzione di dati provenienti dall'utente (caso mostrato più avanti).

Se avessimo effettuato due o più connessioni a diversi server MySQL, per specificare a quale di questi stiamo inviando la query avremmo dovuto passare alla funzione mysql_query() come secondo parametro anche l'identificativo di connessione ottenuto durante la procedura di connessione al server.

Vediamo ora come eseguire la query di inserimento nel caso in cui i dati provengano da un form compilato da un utente. L'interazione con l'utente complica l'esecuzione della query poiché questa volta non sappiamo a priori cosa verrà inviato con il form. Sono necessarie diverse precauzioni quindi e nel seguente esempio ci concentreremo su quelle che assicurano la creazione di una query corretta, tralasciando altri controlli di validità dei dati non meno importanti ma al di la degli scopi di questo corso.

Lo script che segue si occupa di presentare all'utente un form composto da diversi campi (nome, email, sesso, newsletter, attivita, messaggio) appartenenti a differenti tipologie di controlli (textbox, radio button, checkbox, lista, textarea). Quando l'utente invia il form, lo script estrae dall'array $_POST le informazioni necessarie a construire la query di inserimento.

Uno dei principali problemi di questo processo è costituito dall'inserimento all'interno della query di stringhe arbitrarie inviate dall'utente. Sappiamo infatti che in una query le stringhe vanno racchiuse da apici:

INSERT INTO elenco_film SET titolo = 'Al bar dello sport'

ma cosa succede se la stringa contiene a sua volta un apice (apostrofo)?

INSERT INTO elenco_film SET titolo = 'L'allenatore nel pallone'

E' evidente che questa query non è corretta, infatti non è possibile distinguere l'apostrofo del titolo del film dall'apice che delimita la fine della stringa. Per evitare l'ambiguità la query va corretta nel seguente modo, inserendo il carattere \ (backslash) prima dell'apostrofo:

INSERT INTO elenco_film SET titolo = 'L\'allenatore nel pallone'

Il PHP possiede una funzionalità chiamata magic quotes GPC che consiste nell'eseguire automaticamente l'escape, ovvero l'inserimento di un backslash, appena prima di un ' (apice singolo), " (apice doppio), \ (backslash) e NUL per tutti i dati provenienti da GPC (Get, Post e Cookie), ma solo se nel php.ini è abilitata la direttiva magic_quotes_gpc. Sembrerebbe quindi che il problema sia immediatamente risolvibile abilitando tale direttiva.

In realtà la questione è più complessa, infatti ci sono altri caratteri che potrebbero creare dei problemi se inseriti in una query ed inoltre questi dipendono anche dal set di caratteri impiegato nella connessione a MySQL. L'approccio più corretto quindi è disabilitare la funzionalità "magic quotes GPC" ed affidarsi ad una particolare funzione, mysql_real_escape_string(), che effettua il corretto escape di tutti i caratteri potenzialmente pericolosi, tenendo in conto il particolare set di caratteri impiegato nella connessione tra PHP e MySQL (alcuni programmatori utilizzano la funzione addslash() al posto di mysql_real_escape_string(), approccio non del tutto errato ma sicuramente meno specifico e sicuro per un database MySQL).

Se non è possibile disabilitare il "magic quotes GPC", oppure se vogliamo realizzare uno script indipendente da tale funzionalità, è possibile procedere in questo modo. Dapprima controlliamo, interrogando la funzione get_magic_quotes_gpg(), se il magic quote è attivato. In caso affermativo rimuoviamo i backslash inseriti dal PHP con la funzione stripslashes(). Procediamo poi con mysql_real_escape_string() per effettuare un corretto escape di tutte le stringhe da inserire nella query.

Ciò che stiamo facendo è proteggere il nostro codice da uno dei più pericolosi problemi di sicurezza: l'SQL Injection.

Vediamo lo script completo:

<?php
if($_POST) {
	inserisci_record();
}
else {
	mostra_form();
}

function inserisci_record()
{
	// richiamo il file di configurazione
	require 'config.php';

	// richiamo lo script responsabile della connessione a MySQL
	require 'connect.php';

	// recupero i campi di tipo "stringa"
	$nome      = trim($_POST['nome']);
	$email     = trim($_POST['email']);
	$messaggio = trim($_POST['messaggio']);

	// verifico se devo eliminare gli slash inseriti automaticamente da PHP
	if(get_magic_quotes_gpc())
	{
		$nome      = stripslashes($nome);
		$email     = stripslashes($email);
		$messaggio = stripslashes($messaggio);
	}

	$nome      = mysql_real_escape_string($nome);
	$email     = mysql_real_escape_string($email);
	$messaggio = mysql_real_escape_string($messaggio);

	// recupero gli altri campi del form
	$sesso      = isset($_POST['sesso']) ? intval($_POST['sesso']) : 0;
	$newsletter = isset($_POST['newsletter']) ? 1 : 0;
	$attivita   = intval($_POST['attivita']);

	// verifico la presenza dei campi obbligatori
	if(!$nome)
	{
		$messaggio = urlencode("Non hai inserito il nome");
		header('location: '.$_SERVER['PHP_SELF'].'?msg='.$messaggio);
		exit;
	}

	// preparo la query
	$query = "INSERT INTO utenti (nome,email,sesso,newsletter,attivita,messaggio)
			  VALUES ('$nome','$email',$sesso,$newsletter,$attivita,'$messaggio')";

	// invio la query
	$result = mysql_query($query);

	// controllo l'esito
	if (!$result) {
		die("Errore nella query $query: " . mysql_error());
	}

	// recupero l'id autoincrement generato da MySQL per il nuovorecord inserito
	$id_inserito = mysql_insert_id();

	// chiudo la connessione a MySQL
	mysql_close();

	$messaggio = urlencode("Inserimento effettuato con successo (ID=$id_inserito)");
	header('location: '.$_SERVER['PHP_SELF'].'?msg='.$messaggio);
}

function mostra_form()
{
	// mostro un eventuale messaggio
	if(isset($_GET['msg']))
		echo '<b>'.htmlentities($_GET['msg']).'</b><br /><br />';
	?>
	<form name="form_registrazione" method="post" action="">
	  <label>nome:
	  <input name="nome" type="text" />
	  </label>
	  (obbligatorio)
	  <p>
	    <label>email:
	    <input name="email" type="text" />
	    </label>
	  </p>
	  <p> Sesso:
	    <label>
	    <input type="radio" name="sesso" value="1" />
	    M</label>
	    <label>
	    <input type="radio" name="sesso" value="2" />
	    F</label>
	  </p>
	  <p>
	    <label>inviami newletter:
	    <input name="newsletter" type="checkbox" value="1" />
	    </label>
	  </p>
	  <p>
	    <label>attivit&agrave;:
	    <select name="attivita">
	      <option value="0">:: seleziona ::</option>
	      <option value="1">studente</option>
	      <option value="2">lavoratore</option>
	      <option value="3">disoccupato</option>
	    </select>
	    </label>
	  </p>
	  <p>
	    <label>messaggio:<br />
	    <textarea name="messaggio" cols="40" rows="5"></textarea>
	    </label>
	  </p>
	  <p>
	    <input name="invia" type="submit" value="Invia" />
	  </p>
	</form>
	<?php
}
?>

Tutto lo script si basa su due funzioni, mostra_form() per mostrare il form all'utente e inserisci_record() per prelevare i dati inviati via POST, preparare ed eseguire la query di inserimento.

La funzione mostra_form() non necessita di particolari commenti. Si osservino le righe 72-73 che permettono di mostrare un messaggio passato via GET la cui utilità verrà spiegata in seguito.

La funzione inserisci_record() invece presenta diversi aspetti su cui vale la pena soffermarsi:

  • E' buona norma eliminare eventuali spazi vuoti presenti all'estremità delle stringhe inviate dall'utente. Questi spazi, oltre a costituire uno spreco di risorse del database, possono compromettere il corretto funzionamento dei controlli sulla stringa stessa. Si pensi ad esempio alla verifica della lunghezza della stringa. Per eliminare questi spazio basta usare la funzione trim() (righe 18-20).
  • Come ampiamente discusso in precedenza, è necessario effettuare l'escape dei caratteri potenzialmente "pericolosi" presenti nelle stringhe inviate dall'utente. Per prima cosa controlliamo se il PHP (probabilmente in maniera non adeguata) ha già effettuato tale operazione interrogando get_magic_quotes_gpc() (riga 23). In caso affermativo dobbiamo eliminare i backslash inseriti con stripslashes() (righe 25-27). Procediamo poi al corretto escape delle stringhe con la funzione mysql_real_escape_string() (righe 30-32).
  • Particolare attenzione bisogna prestare al trattamento dei dati provenienti dalle checkbox e dai radio button. Se una checkbox viene selezionata dall'utente allora il suo valore sarà presente nell'array $_POST, altrimenti tale valore sarà assente. Pertanto usiamo la funzione isset() per effettuare tale verifica. Anche un set di radio buttons potrebbe essere privo della selezione dell'utente, per cui usiamo anche in questo caso la funzione isset().
  • Se un campo della tabella ospita un intero (come i campi "sesso" e "attivita") allora è meglio assicurarci che la query contenga un valore intero da inserire. Per farlo utilizziamo la funzione intval(). Anche questo accorgimento serve a proteggere lo script da una SQL Injection.
  • Quando si inserisce un record in una tabella è necessario assicurarsi che almeno un campo sia stato riempito. Nell'esemio riportato si è scelto di verificare che il campo "nome" non venga lasciato vuoto. Questo controllo sarebbe stato inefficace se non avessimo usato precedentemente la funzione trim().
  • E' importante preparare la query e conservarla in una variabile ($query) invece di passarla direttamente alla funzione mysql_query(). In caso di errore è molto utile visualizzare la query costruita con i dati dell'utente per cercare al suo interno eventuali errori causati dalla sua realizzazione dinamica.
  • Una tabella contenente un campo auto_increment genera automaticamente un numero intero ad ogni inserimento di un nuovo record. Questo numero spesso rappresenta l'identificativo (ID) del record inserito e pertanto potrebbe essere utile recuperarlo dopo aver eseguito la query. La funzione mysql_insert_id() serve proprio ad ottenere questo numero.

Uno dei problemi più comuni in questo tipo di script è la gestione del refresh della pagina. Se l'utente, dopo aver inviato il form, ricarica la pagina (pulsante "aggiorna" del browser) è possibile che lo script di inserimento venga mandato nuovamente in esecuzione e che quindi la query di inserimento venga ripetuta. Una soluzione è quella di reindirizzare il browser verso un URL qualsiasi, anche quello dello stesso script di inserimento, in modo da perdere i dati inviati dall'utente (POST). Pertanto, eseguita l'interazione col database, lo script termina chiamando la funzione header() che conduce il browser all'indirizzo specificato. Conviene comunque mostrare un messaggio all'utente, per informarlo dell'esito dell'operazione. Per farlo possiamo accodare all'URL utilizzato per il redirect il messaggio da mostrare all'utente. Siccome l'URL non può contenere qualsiasi carattere allora prepariamo il messaggio convertendo gli eventuali caratteri illeciti con la funzione urlencode(). Ecco spiegato anche l'utilità della riga 73 che provvede a visualizzare il contenuto di $_GET['msg'].

18 commenti

1 Luca Luca martedì 21 aprile 2009, ore 23:52
Ciao, complimenti per l'ottimo corso, chiedo un aiutino, sto seguendo tutto passo passo, ma dopo aver inserito il codice che riporti qui sopra quando clicco sul bottone "invia" mi compare questo:
Warning: Cannot modify header information - headers already sent by (output started at C:\Programmi\Apache Software Foundation\Apache2.2\htdocs\config.php:8) in C:\Programmi\Apache Software Foundation\Apache2.2\htdocs\index.php on line 66

e nel DB prova la riga viene inserita comunque...
Grazie per un eventuale risposta!
ciao
Luca
2 Giorgio kama mercoledì 23 marzo 2011, ore 18:10
Salve, leggendo anche altri forum circa l'argomento sql injection, volevo chiedere se per evitare anche attacchi di tipo xss (se non ricordo male, sono l'inserimento di codice javascript malevolo), sarebbe bene anteporre alle funzioni mysql_real_escape_string, la funzione htmlentities. qualcosa tipo:

$nome = htmlentities(mysql_real_escape_string($nome));
$email = htmlentities(mysql_real_escape_string($email));
$messaggio = htmlentities(mysql_real_escape_string($messaggio));

Grazie anticipatamente per la risposta

Giorgio
3 Gianni Tomasicchio Gianni Tomasicchio mercoledì 23 marzo 2011, ore 21:48
No, mysql_real_escape_string serve a preparare le stringhe dinamiche prima del loro inserimento all'interno di uno statement SQL.

In queste stringhe infatti ci potrebbero essere dei caratteri, come ad esempio gli apici, che potrebbero modificare lo statement tanto da renderlo potenzialmente pericoloso.

mysql_real_escape_string quindi previene l'SQL Injection.

Gli attacchi di tipo XSS (Cross-site scripting) invece avvengono quando si prelevano delle stringhe dal database e si inseriscono in una pagina HTML senza prima aver convertito alcuni caratteri, come ad esenpio < e >, nelle relative entità HTML.

E' in questo momento, ovvero quando si compone dinamicamente la pagina HTML, che bisogna usare le funzioni htmlentities o htmlspecialchars.
4 Giorgio kama giovedì 24 marzo 2011, ore 15:24
Ciao Gianni grazie per la risposta. Quindi se qualcuno in uno dei campi stringa, inserisse lì una stringa malevola fra tag javascript che rimandano ad un sito con un virus, tutto l'indirizzo verrebbe scritto dentro il database, magari avrebbe effetto silente... poi al momento di stampare la stringa a video, l'htmlentities per la stampa a video annullerebbe gli effetti... se non ho capito male.

E prevenirli alla fonte? Questo intendevo....
Grazie, scusa se insisto ma quando sull'argomento ci sono "troppe info" in giro, poi perdi la bussola, si necessita di un referente unico..

saluti
Giorgio
5 Gianni Tomasicchio Gianni Tomasicchio giovedì 24 marzo 2011, ore 16:02
Puoi prevenire queste situazioni solo se certi caratteri (gli < e > ad esempio) sono a priori inaccettabili per un certo campo, come ad esempio un nome o un cognome. Questa "prevenzione" consisterebbe nell'impedire l'inserimento di tali caratteri nel database.

Ma comunque è buona norma utilizzare sempre htmlentities o htmlspecialchars nella creazione delle pagine, indipendentemente da un precedente filtraggio dei dati in input.
6 Giorgio kama giovedì 24 marzo 2011, ore 17:15
Ok grazie mille!

Giorgio
7 Luca Luca sabato 14 maggio 2011, ore 17:29
Ciao, cambia qualcosa se effettuo l'escape in questo modo?
$nome = mysql_real_escape_string(get_magic_quotes_gpc() ? stripslashes($nome) : $nome);
8 Gianni Tomasicchio Gianni Tomasicchio sabato 14 maggio 2011, ore 20:14
no, è la stessa cosa
9 tarzan76 giovedì 7 giugno 2012, ore 02:22
Ciao a tutti, sono nuovo su questo sito. Volevo chiedere come mai dopo che inserisco i dati di un utente mi appare questo pezzo di codice?
Grazie

'.htmlentities($_GET['msg']).'

'; ?>
10 foska28mm martedì 26 giugno 2012, ore 17:46
Complimenti per la guida ed il sito; viene spiegato tutto spiegato nel dettaglio e facendo esempi semplici ed intuibili.

Posso farle un paio di domande:

- dovrei fare un form che richiede login e password, lo posso apprendere seguendo questo corso e poi farlo combaciare alle richieste del mio database?

- se dovessi interagire con un database d'immagini e file audio video, da dove posso apprendere queste informazioni? O mi basta questo modello di approccio per poter fare il tutto?

Scusate se abuso di questo spazio per richieste personali, ma delle volte mi pare di non riuscirne a venire a capo.

Grazie ancora.
11 virg mercoledì 24 ottobre 2012, ore 12:18
ciao, il tuo corso è molto semplice e i programmi sono strutturati(si può dire in php?) come piace a me
è chiaro che sono un principiante per quanto riguarda php, evorrei porrei un problema riscontrato su questo esempio: ammettiamo che sia il nome che la mail siano dati obbligatori, l'utente non digita il nome ma la
mail,la funzione visualizza l'errore che non si è digitato il nome ma la mail adesso risulta a spazio.
Io nell'esempio nella funzione mostra_form ho corretto cosi:
<input name="email" type="text" value="<?php echo $_POST["email"]?>" /p>
ed ho fatto la stessa cosa con il nome, la form mi ritorna l'errore ma la mail o il nome debbono essere digitati. Perchè? Dov'è l'errore che commetto?
Grazie e cmq complimenti per il sito
12 naruto1234 martedì 4 dicembre 2012, ore 09:12
é possibile inserire un immagine che poi venga associata ad un nome inserito?
13 FrankieTM2 giovedì 6 giugno 2013, ore 13:24
Ottimo corso e spiegato bene con una buona strategia: se riesci a correggere gli errori in automatico hai imparato il linguaggio...è possibile pubblicare insert senza errori ?
Grazie
14 raz0rgeek sabato 18 gennaio 2014, ore 22:21
Ciao, ho copiato ed incollato il codice (con i 3 file, configurazione, connessione e index.php), ma nel momento in cui vado a cliccare invio sul pulsante del form, mi da questo errore:

Errore nella query INSERT INTO utenti (... ): No database selected

Ho controllato e ricontrollato il codice e pare tutto esatto, il database lo gestisco con phpmyadmin, da utente root, ho creato il database prova con la tabella utenti tale e quale quella indicata nella Lezione 2.

Sapresti dirmi cosa può essere?
15 raz0rgeek domenica 19 gennaio 2014, ore 13:27
Anche ad un mio amico da lo stesso problema, quindi non è una questione di privilegi corrotti di phpmyadmin, quanto piuttosto un errore del codice...
16 raz0rgeek domenica 19 gennaio 2014, ore 13:39
Trovato l'errore!

mysql_close vuole l'argomento ($link), correggilo! :)
17 raz0rgeek domenica 19 gennaio 2014, ore 13:42
E va tolta la riga mysql_close in connect.php, altrimenti fa perdere la connessione.
18 Maxime mercoledì 3 febbraio 2016, ore 11:44
Salve, innanzitutto grazie , se lancio lo script completo senza aver riempito i Campi del form :

arning: Cannot modify header information - headers already sent by (output started at C:\Programmi\xampp\htdocs\LAB\MySQL\php NEWS\Corso PHP MySQL\5) Inserimento dei dati\Connessione DATABASE\config.php:9) in C:\Programmi\xampp\htdocs\LAB\MySQL\php NEWS\Corso PHP MySQL\5) Inserimento dei dati\Connessione DATABASE\Script Completo.php on line 43

Ho copiato lo script completo ma non capisco dove sta l'errore . :)
Effettua l'accesso o registrati per inserire un commento