Locale e como isso pode afetar o retorno da função iconv
Autor: Ricardo Soares de Lima | Publicado: 2013-06-30T11:25:00-07:00

Execute o trecho de código abaixo na linha de comando o observe o resultado:

ini_set('display_errors', 1);
error_reporting(E_ALL);

header('Content-Type: text/plain; charset=utf-8');
$utf8_word = 'GOIÂNIA';
$ascii_word = iconv('UTF-8', 'US-ASCII//TRANSLIT', $utf8_word);
echo $ascii_word, "\n";
echo md5($ascii_word);
echo "\n";


No meu sistema(PHP 5.3.6-13ubuntu3.10 with Suhosin-Patch (cli)) a saída foi:

GOIANIA
1d92cfdaed61eba2c8dea6e670df9f38

Certo, agora executando o mesmo script através do browser e do servidor web a saída é a seguinte:

GOI?NIA
848aa917b7d132a581c0d9f91bc1b03f

Humm... Por que o mesmo script tem saídas diferentes quando executado na linha de comando e quando executado no servidor web?

Mas o que este script faz mesmo? Ele simplesmente usa a função iconv para converter uma string codificada com UTF-8(GOIÂNIA) para codificação US-ASCII, o nosso velho conhecido ASCII, usando transliteração o que faz com que quando um caractere não possa ser representado na codificação alvo a função tenta usar um caractere da codificação alvo que seja parecido. Mas ainda não sabemos poque o script tem saídas diferentes em ambientes diferentes.

Acontece que a função iconv é afetada pelo locale do ambiente, usando a função setlocale podemos verificar qual locale está sendo usado:

$current_locale = setlocale(LC_ALL, "0");
echo "Current locale:$current_locale", "\n";


No servidor web a resposta é "C" na linha de comando é "LC_CTYPE=pt_BR.UTF-8;LC_NUMERIC=C;LC_TIME=C;LC_COLLATE=C;LC_MONETARY=C;LC_MESSAGES=C;LC_PAPER=C;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=C;LC_IDENTIFICATION=C", que é o locale configurado no meu sistema operacional. Ou seja enquando no servidor web o locale é C na linha de comando é utf-8. Então é só ajustarmos o locale antes de chamar a função iconv para que ela tenha o mesmo comportamento em qualquer ambiente.

ini_set('display_errors', 1);
error_reporting(E_ALL);

setlocale(LC_ALL, 'pt_BR.UTF-8');
header('Content-Type: text/plain; charset=utf-8');
$utf8_word = 'GOIÂNIA';
$ascii_word = iconv('UTF-8', 'US-ASCII//TRANSLIT', $utf8_word);
echo $ascii_word, "\n";
echo md5($ascii_word);
echo "\n";


Mas existem algumas considerações a serem feitas, o locale configurado através da função setlocale deve estar instalado no servidor onde o código será executado, caso contrário a função setlocale simplesmente retorna FALSE e o resultado pode ser diferente para cada ambiente quando demonstrado no início do texto. Outra consideração é que o efeito da função setlocale é "por processo". Na documentação da função existe um "warning" &nbsp;explicando que quando existirem vários scripts rodando em diferentes threads no servidor(algo muito comum se seu servidor tem várias requisições ao mesmo tempo) uma chamada a setlocale para mudar o locale vai afetar todos os scripts em execução no momento mesmo que o script em si não tenha chamado setlocale.

Espero que este texto possa ajudar as pessoas pegas de surpresa pelos resultados da função iconv. Até a próxima.

Referências:
http://stackoverflow.com/questions/9771886/what-factors-influence-a-successful-iconv-translit-conversion