giovedì 9 maggio 2013

PHP e stringhe con caratteri strani ossia il charset UTF-8 e dintorni nel PHP (Parte 4/4)


Il carattere sostitutivo UTF-8
per le codifiche errate
Ora che abbiamo conosciuto UTF-8, e visto come agisce tanto all'interno di semplici pagine HTML quanto all'interno di script PHP (quindi in un scambio dati tra Browser, Web Server, motore PHP), non resta che vedere come si comporta nello scambio dati tra PHP e MySQL.

Iniziamo con il chiarire alcuni concetti relativi a MySQL. Il DB utilizza per la memorizzazione dei testi un charset (il nostro UTF-8 ad esempio) atto ad individuare la codifica del testo, ed una collation (collazione in italiano) con cui si definiscono una serie di regole per il confronto dei caratteri nell'ambito del charset. Ciò al fine di stabilire quale carattere  precede un'altro nelle operazioni di ordinamento e ricerca con operatori di confronto. Ciò significa anche che il charset e la collazione sono legati fra di loro ossia stabilito il charset da utilizzare, si sceglie la collazione più idonea al proprio fra quelle disponibili per il charset scelto. Ad esempio MySQL mette a disposizione per UTF-8 diverse collazioni.

In MySQL il charset agisce su 4 livelli:
  1. Server
  2. Base dati
  3. Tabella
  4. Colonna
Andando a coinvolgere non solo la memorizzazione del dato, ma anche la comunicazione tra client (motore PHP) e server (MySQL). Il nostro obiettivo principale è quindi fare in modo che PHP e MySQL parlino "la stessa lingua" ossia facciano uso della stessa codifica caratteri affinché non si abbiano risultati inattesi.

Se vogliamo fare in modo che PHP e MySQL scambino dati con una certa codifica è possibile, dopo essersi connessi al DB Server, utilizzare il comando SQL set names 'utf8' qualora si intenda utilizzare UTF-8. Problema risolto quindi!?! Utilizzando la buona vecchia sintassi mysql_ di php (sebbene deprecata a partire dalla versione 5.5 di PHP nonostante tale versione non sia ancora in fase di test), potremmo scrivere:

mysql_query("set names 'utf8'");

ma solo dopo aver eseguito con successo una connessione a una base dati.

C'è un però. La documentazione PHP mette in guardia sull'utilizzo del metodo su indicato. Infatti se da un lato la comunicazione client server avviene correttamente in UTF-8, la funzioni di PHP dei vari driver (mysql, mysqli e pdo) ignorano che questa sia la codifica utilizzata. La conseguenza è che le funzioni di escaping, come mysql_real_escape(), continuano ad agire ignorando tale circostanza.

Con PHP5 si ha la disponibilità di un'apposita funzione atta ad impostare la comunicazione con codifica UTF-8 tra PHP e MySQL. E' la funzione mysql_set_charset('utf8',$DBconn);. analogamente avremo la mysqli::set_charset('utf8'); o mysqli_set_charset($mysqli_conn, 'utf8'); per mysqli. Fa eccezione PDO, per il quale occorre aggiungere nella stringa di configurazione il parametro charset=utf8.

Bene! Il caos impazza ma un esempio può chiarire ogni cosa.

Esempio con driver mysql (deprecato da PHP 5.5)
$link = mysql_connect('localhost','user','password') or die(mysql_error());
mysql_select_db('nome_db', $link);
if(function_exists('mysql_set_charset'))
    mysql_set_charset('utf8',$link);
else
    mysql_query("set names 'utf8'");

Esempio con driver mysqli
$mysqli = new mysqli('localhost', 'user', 'password', 'nome_db');
if(mysqli->connect_error())
    die(mysqli->connect_error());
$mysqli->set_charset('utf8');

Esempio con PDO per PHP 5.3.6 e successivo
$dsn = 'mysql:host=localhost;dbname=nome_db;charset=utf8';
$pdo = new pdo($dsn, 'user', 'password');

Esempio con PDO per PHP precedente alla 5.3.6 perché per tali versioni il parametro charset della stringa dsn è ignorato

$dsn = 'mysql:host=localhost;dbname=nome_db;charset=utf8';
$opzioni = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'set names utf8');
$pdo = new pdo($dsn, 'user', 'password',$opzioni);

Questi sono degli esempi base per stabilire una connessione ad un DB MySQL facendo in modo che i dati tra PHP e MySQL siano scambiati in modo consistente, adottando la codifica UTF-8. Ne consegue che la propria stringa di connessione potrebbe differire da quella degli esempi.

Riepilogando possiamo quindi affermare che UTF-8 è il charset di internet, adottato dal W3C in tutti i propri esempi, permettendo la codifica dei caratteri di quasi ogni linguaggio esistente. Per utilizzare UTF-8 però occorre fare in modo che:

  1. I propri script PHP siano salvati in file codificati UTF-8 senza BOM
  2. Il codice HTML prodotto possa essere riconosciuto dal browser come codificato UTF-8 tramite il tag meta di HTML o tramite un'istruzione header() di PHP
  3. Utilizzando una base dati, adottare per le tabelle al codifica UTF-8 e accertarsi che il flusso dati tra server (MySQL) e client (PHP) sia codificato UTF-8
In questo modo addio ai caratteri strani nei propri progetti!