PHP e AJAX: come gestire le lettere accentate

Sto sviluppando un sito (un gran bel sito u.u) nel quale faccio una richiesta asincrona tramite AJAX a uno script PHP, il quale mi restituisce del codice HTML.
Ma se devo visualizzare delle lettere accentate… viene un gran guazzabuglio di strane à e simboli esoterici del genere che sembra che stia scrivendo in lappone.

A cosa è dovuto questo problema?
Alla codifica dei caratteri, o meglio al charset impostato per la pagina.
Il charset è l’insieme dei caratteri che scelgo di far visualizzare al browser. In base a questa informazione, il browser codifica la richiesta e trasforma quindi i byte nei caratteri corrispondenti.
Il charset più comune è l’ASCII, che prevede 128 caratteri, tra numeri, lettere, simboli e cartteri di utilità (tipo il carattere di new line). Tuttavia non rappresenta tutti i caratteri che esistono, considerando le centinaia di lettere diverse che ci sono in lingue orientali o anche solo in quelle nordiche o germaniche.
Pe rappresentare anche questi caratteri quindi si usa un charset chiamato ISO-8859-1, Latin 1 per gli amici, che prevede 256 simboli e comprende tutte le lettere “strane” degli alfabeti europei, oltre a qualche altro simbolo tipo lo ¥ o l’€.

Per impostare il charset della propria pagina web ci sono due modi:

  • Tramite un tag <meta> nell’head della pagina
  • Impostado l’header della richiesta HTTP

La prima opzione è indicata specialmente per pagine statiche o per script che producono un’intera pagina, mentre la seconda è l’ideale per script che producono un output per una richiesta AJAX, e quindi non possono inserire tag nell’head della pagina.

Quindi, per visualizzare correttamente i caratteri accentati, nella pagina principale che esegue la richiesta AJAX imposteremo il charset a ISO-8859-1 inserendo nell’head:
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
Mentre nella pagina PHP richiamata con AJAX imposteremo il charset nell’altro modo, ossia:
header("Content-type: text/html; charset=ISO-8859-1");
Ricordandoci che è possibile utilizzare la funzione header() solo prima di aver generato qualsiasi output con echo, print o simili, o verrà generato un errore.

Nello script AJAX sarà importante ricordarsi di codificare e decodificare i dati (siano essi GET o POST) inviati allo script con le funzioni encodeURIComponent e decodeURIComponent

data = encodeURIComponent("dati da inviare");
//...
//invio dei dati con AJAX
//...
document.getElementById("contenitore").appendChild(decodeURIComponent(response));

Ma siccome queste funzioni codificano l’output con il charset UTF-8, nello script PHP dovrò usare una funzione strategica: utf8_decode().
Passando i valori inviati in questa funzione, riconverto il tutto in ISO-8859-1, senza quindi vaere strane sorprese.

Dimenticavo:
Nel caso i dati vengano memorizzati in un database, anch’esso dovrà essere impostato con il charset Latin 1 (ISO-8859-1).
In poche parole, pagina, script e database devono avere lo stesso charset, qualunque esso sia.

Spero di essere stato utile, visto che il problema mi ha dato non poche grane…

  • Luca Meloni

    Ottimo articolo, tuttavia non riesco a risolvere:

    – Ho impostato il giusto content-type su ogni pagina HTML;
    – Ho impostato il content type in PHP con la funzione header prima di impostare la location;
    – Ho codificato gli indirizzi e i parametri in JS tramite encodeURIComponent;
    – Ho decodificato il responseText in ogni richiesta AJAX tramite decodeURIComponent;
    – Ho decodificato ogni testo recuperato con MySQL tramite utf8_decode.

    Qualche idea riguardo a come risolvere?

    • Qwertj

      la funzione utf8_decode() va usata sui parametri inviati dallo JS allo script chiamato con AJAX, tipo
      $id = utf8_decode($_GET[‘id’]);
      MySQL devi impostarlo su charset latin1, collation latin1_swedish_ci, e quindi non avrai bisogno di conversioni tra i due charset

      sto per scrivere un articolo su come gestire invece i charset UTF-8, che permette di supportare siti multilingua e specialmente più standardizzati (visto che UTF-8 sarebbe raccomandato dal W3C)