etiquetas - inicio y fin en php
¿Apoyo correctamente UTF-8 en mis aplicaciones PHP? (5)
Me gustaría asegurarme de que todo lo que sé sobre UTF-8 sea correcto. He intentado utilizar UTF-8 desde hace un tiempo, pero sigo tropezando con más y más errores y otras cosas raras que hacen que parezca casi imposible tener un sitio 100% UTF-8. Siempre hay un gotcha en algún lugar que parece que extraño. Quizás alguien aquí puede corregir mi lista o aceptarla para no perderme nada importante.
Base de datos
Cada sitio debe almacenar sus datos en alguna parte. No importa cuáles sean sus configuraciones de PHP, también debe configurar la base de datos. Si no puede acceder a los archivos de configuración, asegúrese de " ESTABLECER NOMBRES ''utf8'' " tan pronto como se conecte. Además, asegúrese de usar utf8_ unicode_ci en todas sus tablas. Esto supone que MySQL para una base de datos, tendrá que cambiar para otros.
Regex
Hago MUCHAS expresiones regulares que son más complejas que su reemplazo de búsqueda promedio. Tengo que recordar usar el modificador "/ u" para que PCRE no corrompa mis cadenas . Sin embargo, incluso entonces todavía hay problemas aparentemente .
Funciones de cadena
Todas las funciones de cadena predeterminadas (strlen (), strpos (), etc.) deben reemplazarse con funciones de cadenas de múltiples bytes que miren el carácter en lugar del byte.
Encabezados Debe asegurarse de que su servidor devuelva el encabezado correcto para que el navegador sepa qué juego de caracteres está tratando de usar (al igual que debe decirle a MySQL).
encabezado (''Content-Type: text / html; charset = utf-8'');
También es una buena idea colocar la etiqueta <meta> correcta en el encabezado de la página. Aunque el encabezado real anulará esto, deberían diferir.
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
Preguntas
¿Debo convertir todo lo que recibo del agente de usuario (formularios HTML y URI) a UTF-8 cuando se carga la página o si puedo dejar las cadenas / valores tal como están y aún así ejecutarlas sin problemas? ?
Si necesito convertir todo a UTF-8, ¿qué pasos debo seguir? mb_detect_encoding parece estar diseñado para esto, pero sigo viendo que la gente se queja de que no siempre funciona. mb_check_encoding también parece tener problemas para mb_check_encoding una buena cadena UTF-8 de una cadena incorrecta.
¿Almacena PHP cadenas en la memoria de manera diferente dependiendo de qué codificación está utilizando (como tipos de archivos) o todavía se almacena como una picadura regular con algunos de los caracteres que se interpretan de manera diferente (como & amp; vs & en HTML). chazomaticus responde a esta pregunta:
En PHP (hasta PHP5, de todos modos), las cadenas son solo secuencias de bytes. No hay un juego de caracteres implícito o explícito asociado a ellos; eso es algo de lo que el programador debe estar pendiente.
Si se da una cadena que no sea UTF-8 a una función mb_ * ¿alguna vez causará un problema?
Si una cadena UTF está codificada incorrectamente, ¿algo va mal (como un error de análisis en expresiones regulares?) O ¿simplemente marcará una entidad como mala (html)? ¿Hay alguna posibilidad de que las cadenas codificadas incorrectamente den como resultado que la función devuelva FALSE porque la cadena es incorrecta?
He oído que debe marcar sus formularios como UTF-8 también (accept-charset = "UTF-8") pero no estoy seguro de cuál es el beneficio.
¿Se escribió UTF-16 para abordar un límite en UTF-8? Al igual que UTF-8 se quedó sin espacio para los personajes? (Y2 (UTF) k?)
Funciones
Estas son algunas de las funciones de PHP personalizadas que he encontrado, pero no tengo ninguna forma de verificar que realmente funcionen. Quizás alguien tenga un ejemplo que pueda usar. Primero es convertToUTF8() y luego seems_utf8 de wordpress.
function seems_utf8($str) {
$length = strlen($str);
for ($i=0; $i < $length; $i++) {
$c = ord($str[$i]);
if ($c < 0x80) $n = 0; # 0bbbbbbb
elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
else return false; # Does not match any model
for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
return false;
}
}
return true;
}
function is_utf8($str) {
$c=0; $b=0;
$bits=0;
$len=strlen($str);
for($i=0; $i<$len; $i++){
$c=ord($str[$i]);
if($c > 128){
if(($c >= 254)) return false;
elseif($c >= 252) $bits=6;
elseif($c >= 248) $bits=5;
elseif($c >= 240) $bits=4;
elseif($c >= 224) $bits=3;
elseif($c >= 192) $bits=2;
else return false;
if(($i+$bits) > $len) return false;
while($bits > 1){
$i++;
$b=ord($str[$i]);
if($b < 128 || $b > 191) return false;
$bits--;
}
}
}
return true;
}
Si alguien está interesado, encontré una gran página de ejemplo para usar cuando se prueba UTf-8 .
¿Debo convertir todo lo que recibo del agente de usuario (formularios HTML y URI) a UTF-8 cuando se carga la página?
No. El agente de usuario debe enviar datos en formato UTF-8; si no, estás perdiendo el beneficio de Unicode.
La forma de garantizar que un usuario-agente envíe en formato UTF-8 es servir la página que contiene el formulario que está enviando en codificación UTF-8. Use el encabezado Content-Type (y meta http-equiv también si desea guardar el formulario y trabajar de forma independiente).
He oído que debe marcar sus formularios como UTF-8 también (accept-charset = "UTF-8")
No lo hagas Fue una buena idea en el estándar HTML, pero IE nunca lo hizo bien. Se suponía que debía establecer una lista exclusiva de conjuntos de caracteres permitidos, pero IE lo trata como una lista de conjuntos adicionales para probar, por campo. Entonces, si tiene una página ISO-8859-1 y un formulario "accept-charset =" UTF-8 "", IE primero tratará de codificar un campo como ISO-8859-1, y si hay un non-8859-1 personaje allí, entonces recurrirá a UTF-8.
Pero dado que IE no le dice si usó ISO-8859-1 o UTF-8, eso no le sirve de nada. Tendría que adivinar, para cada campo por separado, qué codificación estaba en uso. Inútil. Omita el atributo y sirva sus páginas como UTF-8; eso es lo mejor que puedes hacer en este momento.
Si una cadena UTF está codificada incorrectamente, algo va mal
Si permites que esa secuencia llegue al navegador, podrías tener problemas. Hay ''secuencias demasiado largas'' que codifican un punto de código de bajo numeración en una secuencia más larga de bytes de la necesaria. Esto significa que si está filtrando ''<'' al buscar ese carácter ASCII en una secuencia de bytes, podría perder uno, y dejar que un elemento del guión entre en lo que usted pensó que era texto seguro.
Las secuencias excesivas fueron prohibidas en los primeros días de Unicode, pero a Microsoft le llevó mucho tiempo juntar su mierda: IE interpretaría la secuencia de bytes ''/ xC0 / xBC'' como ''<'' hasta IE6 Service Pack 1. Opera también se equivocó hasta (sobre, creo,) la versión 7. Afortunadamente, estos navegadores antiguos se están extinguiendo, pero aún vale la pena filtrar secuencias excesivamente largas en caso de que esos navegadores sigan siendo actuales (o los nuevos navegadores idiotas cometan el mismo error en el futuro ) Puedes hacer esto y arreglar otras secuencias malas con una expresión regular que solo permita el paso correcto de UTF-8, como esta de W3.
Si está utilizando funciones mb_ en PHP, puede estar aislado de estos problemas. No puedo decir con certeza ya que mb_ * era frágil inutilizable cuando aún escribía PHP.
En cualquier caso, este es también un buen momento para eliminar los caracteres de control, que son una gran fuente de errores generalmente desconocida. Quitaría los caracteres 9 y 13 de la cadena enviada además de los otros que elimina la expresión regular W3; también vale la pena eliminar las líneas nuevas para cadenas que sabes que no se supone que son cuadros de texto de líneas múltiples.
¿Se escribió UTF-16 para abordar un límite en UTF-8?
No, UTF-16 es una codificación de dos bytes por punto de código que se utiliza para facilitar la indexación de cadenas Unicode en la memoria (desde los días en que todo Unicode cabía en dos bytes, sistemas como Windows y Java todavía lo hacen de esa manera) ) A diferencia de UTF-8, no es compatible con ASCII y es de poca utilidad en la Web. Pero de vez en cuando lo encuentras en archivos guardados, generalmente guardados por usuarios de Windows que han sido engañados por la descripción de Windows de UTF-16LE como "Unicode" en los menús de Guardar como.
seems_utf8
¡Esto es muy ineficiente en comparación con la expresión regular!
Además, asegúrese de usar utf8_unicode_ci en todas sus tablas.
De hecho, puedes salirte con la tuya sin esto, tratando a MySQL como una tienda para nada más que bytes y solo interpretándolos como UTF-8 en tu script. La ventaja de usar utf8_unicode_ci es que recopilará (clasificará y hará comparaciones insensibles a mayúsculas y minúsculas) con el conocimiento sobre caracteres que no sean ASCII, por ejemplo. ''ŕ'' y ''Ŕ'' son el mismo personaje. Si utiliza una intercalación que no sea UTF8, debe ajustarse a la concordancia binaria (sensible a mayúsculas y minúsculas).
Cualquiera que elija, hágalo de manera consistente: use el mismo conjunto de caracteres para sus tablas como lo hace para su conexión. Lo que desea evitar es una conversión de conjunto de caracteres con pérdida entre sus scripts y la base de datos.
La mayor parte de lo que estás haciendo ahora debería ser correcto.
Algunas notas: cualquier colación utf_*
en MySQL almacenaría sus datos correctamente como UTF-8, la única diferencia entre ellos es la intercalación (orden alfabético) aplicada al ordenar.
Puede indicarle a Apache y PHP que emitan los encabezados de conjunto de caracteres correctos AddDefaultCharset utf-8
en httpd.conf / .htaccess y default_charset = "utf-8"
en php.ini, respectivamente.
Puede decirle a la extensión mbstring que se encargue de las funciones de cadena. Esto funciona para mí:
mbstring.internal_encoding=utf-8
mbstring.http_output=UTF-8
mbstring.encoding_translation=On
mbstring.func_overload=6
(esto deja intacta la función de mail(
) - Encontré configurarlo para 7 estragos jugados con mis encabezados de correo)
Para la conversión de juego de caracteres, consulte https://sourceforge.net/projects/phputf8/ .
A PHP no le importa en absoluto lo que hay en la variable, solo almacena y recupera ciegamente su contenido.
Obtendrá resultados inesperados si declara un mbstring.internal_encoding
y proporciona cadenas de función mb_ * en otra codificación. De todos modos, puede enviar de manera segura ASCII a las funciones de utf-8.
Si le preocupa que alguien publique material codificado incorrectamente a propósito, creo que debería considerar HTML Purifie para filtrar los datos GET / POST antes del procesamiento.
Accept-charset
ha estado en las especificaciones desde siempre, pero su soporte en el mundo real en navegadores es más o menos cero. El navegador utilizará de manera típica la codificación de la página que contiene el formulario.
UTF-16 no es el hermano mayor de UTF-8, solo sirve para un propósito diferente.
Para las entradas del usuario desde el formulario, agrego este atributo a las etiquetas de mi form
: accept-charset="utf-8"
. De esta manera, los datos que reciba siempre deberán estar codificados en utf-8.
UTF-8 está bien, y no tiene ningún límite que resuelva UTF-16. PHP doens''t cambia su forma de almacenar cadenas en la memoria (a diferencia de Python). Si todo el flujo de datos usa UTF-8 (los formularios web reciben datos UTF-8, las tablas usan codificación utf8 y está usando el SET NAMES utf8
, y los datos se almacenan sin alteración (sin conversión de juego de caracteres), eso debería estar bien .
database / mysql: si está utilizando SET NAMES
y, por ejemplo, php/mysql , está dejando a la sombra a mysql_real_escape_string() sobre el cambio en la codificación de caracteres. Esto puede conducir a resultados incorrectos. Entonces, si confía en una función de escape como mysql_real_escape_string (porque no está usando declaraciones preparadas) SET NAMES
es una solución que no es óptima. Es por eso que se ha introducido mysql_set_charset() o por qué gentoo aplica un parche que agrega el parámetro de configuración mysql.connect_charset para php / mysql y php / mysqli.
El cliente generalmente no indica la codificación de los parámetros que envía. Si espera datos codificados en utf-8 y los trata como tales , puede haber errores de codificación (secuencias de bytes que no son válidas en utf-8). Por lo tanto, los datos pueden no mostrarse como se espera o un analizador puede abortar el análisis sintáctico. Pero al menos la entrada del usuario no puede "escapar" y hacer más daño, por ejemplo, en una instrucción sql en línea o salida html. Por ejemplo, tomar el script (guardado como iso-8859-1 o utf-8, no importa)
<?php
$s = ''abcxyz'';
var_dump(htmlspecialchars($s, ENT_QUOTES, ''utf-8''));
// adding the byte sequence for äöü in iso-8859-1
$s = ''abc''. chr(0xE4) . chr(0xF6) . chr(0xFC). ''xyz'';
var_dump(htmlspecialchars($s, ENT_QUOTES, ''utf-8''));
huellas dactilares
string(6) "abcxyz"
string(0) ""
E4F6FC no es una secuencia de bytes utf-8 válida, por lo tanto, htmlspecialchars devuelve una cadena vacía. Otras funciones pueden regresar? u otro personaje "especial". Pero al menos no "confundirán" a un personaje como un personaje de control malicioso, siempre y cuando todos se apeguen a la codificación "adecuada" (utf-8 en este caso).
accept-charset no garantiza que solo recibirá datos con esa codificación. Por lo que usted sabe, es posible que el cliente ni siquiera haya "usado" / analizado su documento html que contiene el elemento de formulario. Puede ser útil y no hay ninguna razón por la cual no deba establecer ese atributo. Pero no es "confiable".