Ovvero: "perché mi compaiono dei \ davanti agli apici sul database?"
Quando si devono inserire stringhe in un database bisogna fare attenzione a farne correttamente l'"escape", cioè a "neutralizzare" quei caratteri che possono provocare problemi alla query di inserimento.
Esempio: ipotizziamo di avere una tabella che contiene una colonna 'Cognome' e di dovere inserire il cognome D'Antoni.
Ipotizziamo altresì di lavorare su MySql, il database più utilizzato in "combutta" con php. Il problema tuttavia si presenta in termini molto simili con qualsiasi tipo di database.
Come ben sapete (se non lo sapete filate subito qui! ), per inserire una stringa in una istruzione sql bisogna racchiuderla tra apici. Ad esempio se il nostro cognome, invece di D'Antoni, fosse Antoni, l'istruzione sarebbe semplice:
codice:
...cognome = 'Antoni', ...
Ma cosa succede se facciamo questo?
codice:
...cognome = 'D'Antoni', ...
Semplice: il parser della query vede l'apice dopo la D e considera la stringa terminata. Quello che viene dopo porterà ad un errore, perché il linguaggio SQL non conosce la parola 'Antoni' che è rimasta fuori dagli apici.
Ci deve essere però un modo per inserire un apice in una stringa!
Ovviamente sì, ed è proprio l'escape di cui parlavamo.
codice:
...cognome = 'D\\'Antoni', ...
In questo caso il backslash, che è il "carattere di escape", fa sì che il carattere successivo venga considerato in quanto tale e non come carattere di chiusura della stringa, come sarebbe successo nel caso precedente.
Il parser della query quindi capisce che l'apice fa parte della stringa e prosegue fino all'apice successivo, che determinerà la chiusura della stringa stessa. Ovviamente nel database verrà memorizzato D'Antoni, senza il backslash, in quanto il parser, dopo averlo "utilizzato" per interpretare correttamente la stringa, lo ha eliminato. Quindi quando andremo a rileggere il dato nel database otterremo esattamente il valore che ci interessa.
Bene, ora passiamo a php. Come facciamo ad ottenere questo risultato, tenendo presente che quasi sempre i dati da mettere nel database sono contenuti in variabili di cui non conosciamo il contenuto nel momento in cui scriviamo il codice?
Le possibilità sono diverse.
La più tradizionale veniva fornita in automatico da php attraverso la direttiva magic_quotes_gpc del file php.ini.
Quando tale direttiva è attivata, tutti i dati provenienti da querystring, moduli e cookie vengono, di fatto, sottoposti alla funzione addslashes(), che aggiunge un '\\' davanti agli apici singoli e doppi, nonché allo stesso backslash e agli eventuali caratteri NUL (byte nulli).
Un altro modo di ottenere lo stesso risultato è, evidentemente, quello di applicare la funzione addslashes() vista sopra alle variabili che devono essere inserite nel database.
In realtà però il sistema migliore per fare l'escape che ci serve è quello di usare (quando disponibile, ovviamente) la funzione apposita dedicata al database che ci interessa. Nel caso di MySql si tratta di mysql_real_escape_string() o di mysqli_real_escape_string() , a seconda di quale versione della libreria stiamo utilizzando. Alle funzioni devono essere passati come parametri la stringa di cui ci serve l'escape e l'identificativo di connessione al database (che quindi dobbiamo avere già effettuato). Attenzione! L'ordine dei parametri è rovesciato fra le due funzioni (la connessione va come secondo per mysql_real_escape_string, come primo per mysqli_real_escape_string; se si usa mysqli nella notazione a oggetto, la connessione ovviamente non serve).
Tale funzione infatti tiene conto del character set attivo nella connessione, il che consente una maggiore precisione nell'identificazione dei caratteri di cui fare l'escape.
Quindi, riassumendo, la soluzione migliore è:
- magic_quotes_gpc disattivati
- invece di addslashes(), usare mysql_real_escape_string() o equivalente, quando disponibile
Ai più inesperti capita di trovarsi dei backslash nel database in corrispondenza di apici: se fate una SELECT sulla colonna che abbiamo visto prima e vi salta fuori D\\'Antoni, significa che l'operazione di escape è stata fatta due volte: infatti la funzione eseguita per seconda ha trovato il backslash già contenuto nella stringa e ha "creduto" che ne facesse parte, facendo quindi l'escape in modo che tale backslash non andasse perso nell'inserimento al db.
Come può accadere questo?
Il caso più frequente è sicuramente quello di un addslashes() (o altra funzione di escape come visto sopra) che è stata eseguita su una stringa di input (querystring, modulo o cookie) in presenza di magic_quotes_gpc attivo, il che porta la stringa ad essere processata due volte con il risultato che abbiamo appena detto.
Il suggerimento già visto è quello di tenere disattivato magic_quotes_gpc. Tuttavia, per poter trattare i dati in maniera indipendente da questo, conviene avere una funzione che sia in grado di tenere conto dell'impostazione suddetta.
Vediamo un breve esempio:
Codice PHP:
function strip_magic ($value)
{
$value = (get_magic_quotes_gpc()) ? stripslashes($value) : $value;
return $value;
}
$campo = strip_magic($_POST['campo']); //ATTENZIONE: i campi di input vanno validati attentamente!!
$campoPerDb = mysql_real_escape_string($campo,$conn);
In questo piccolo esempio abbiamo definito una funzione (strip_magic()) che verifica se magic_quotes_gpc è attivato e, in caso affermativo, applica la funzione stripslashes() che fa l'opposto di addslashes() e quindi ne neutralizza l'effetto (in pratica rimuove dalla stringa gli eventuali backslash di escape).
Dovendo trattare quindi un campo di input per inserirlo nel db, procederemo seguendo due fasi:
1) neutralizzazione dell'eventuale magic_quotes_gpc (nell'esempio abbiamo usato direttamente il campo in input da un modulo, ma ricordatevi che non bisogna MAI fidarsi dell'input, bensì controllare sempre che contenga ciò che ci aspettiamo)
2) applicazione della funzione di escape per neutralizzare eventuali apici
Le due fasi vanno tenute ben distinte, perché non è detto che vadano sempre in coppia: infatti se abbiamo un dato in input che non deve essere memorizzato su database dovremo eseguire la prima ma non la seconda; nel caso invece in cui un dato vada memorizzato sul db ma non provenga dall'input utente (ad es. potrebbe essere appena stato letto da un altro db) allora dovremo eseguira la seconda e non la prima. E' infatti importante ricordare che la funzione stripslashes() deve essere eseguita solo su stringhe alle quali sia stata precedentemente applicata addslashes(); in caso contrario rischiamo di alterare la stringa originaria.
E per stasera è tutto, la linea allo studio
(se notate incongruenze fatemele notare perché il forum è un po' str@nzo e tende a mangiarsi i backslash... )