From e5d06f531daa29e9b1da972d3eb35041b824344e Mon Sep 17 00:00:00 2001 From: Fil <fil@rezo.net> Date: Sat, 14 Mar 2015 14:05:14 +0000 Subject: [PATCH] gestion des emoji avec MySQL ================ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit le charset 'utf8' de MySQL ne contient pas l'ensemble de l'UTF-8, mais seulement les caractères codés sur 1, 2, ou 3 bytes; pour contourner ce problème, on pourrait adopter le charset 'utf8mb4', mais c'est difficile à faire, et ça implique de revoir la structure de la base (notamment les VARCHAR 255 ne passent plus, car 255*4 > 1000) la solution adoptée ici est d'échapper les caractères de 4 bytes sous leur forme unicode 💩 ; on le fait au niveau de req/mysql car ce problème touche mysql seulement pour activer ce fonctionnement, il faut pour l'instant ajouter dans mes_options la ligne suivante : define('_MYSQL_NOPLANES', true); au passage mes tests sur l'emoji on révélé deux bugs dans inc/charsets, corrigés ici références : http://seenthis.net/messages/350136 --- ecrire/inc/charsets.php | 31 ++++++++++++++++++++++++++++++- ecrire/inc/modifier.php | 18 ++++++++++++++---- ecrire/inc/utils.php | 2 +- ecrire/req/mysql.php | 11 +++++++++-- 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/ecrire/inc/charsets.php b/ecrire/inc/charsets.php index c07ec39803..d99891a0e3 100644 --- a/ecrire/inc/charsets.php +++ b/ecrire/inc/charsets.php @@ -654,6 +654,7 @@ function utf_32_to_unicode($source) { * Caractère utf8 si trouvé, '' sinon **/ function caractere_utf_8($num) { + $num = intval($num); if($num<128) return chr($num); if($num<2048) @@ -661,7 +662,7 @@ function caractere_utf_8($num) { if($num<65536) return chr(($num>>12)+224).chr((($num>>6)&63)+128).chr(($num&63)+128); if($num<1114112) - return chr($num>>18+240).chr((($num>>12)&63)+128).chr(($num>>6)&63+128). chr($num&63+128); + return chr(($num>>18)+240).chr((($num>>12)&63)+128).chr(($num>>6)&63+128). chr($num&63+128); return ''; } @@ -1106,4 +1107,32 @@ if (!isset($GLOBALS['meta']['pcre_u']) ); } + +/** + * Transforme une chaîne utf-8 en utf-8 sans "planes" + * ce qui permet de la donner à MySQL "utf8", qui n'est pas un utf-8 complet + * L'alternative serait d'utiliser utf8mb4 + * + * @param string $x + * La chaîne à transformer + * @return string + * La chaîne avec les caractères utf8 des hauts "planes" échappée + * en unicode : 💩 + */ +function utf8_noplanes($x) { + $regexp_utf8_4bytes = '/( + \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 +)/xS'; + if (preg_match_all($regexp_utf8_4bytes, $x, $z, PREG_PATTERN_ORDER)) { + foreach($z[0] as $k) { + $ku = utf_8_to_unicode($k); + $x = str_replace($k, $ku, $x); + } + } + return $x; +} + + ?> diff --git a/ecrire/inc/modifier.php b/ecrire/inc/modifier.php index 5139b345aa..fc3416dbe7 100644 --- a/ecrire/inc/modifier.php +++ b/ecrire/inc/modifier.php @@ -207,15 +207,25 @@ function objet_modifier_champs($objet, $id_objet, $options, $c=null, $serveur='' // si difference entre les champs, reperer les champs mal enregistres if ($moof != $champs) { $liste = array(); - foreach($moof as $k=>$v) + foreach($moof as $k=>$v) { if ($v !== $champs[$k] - // ne pas alerter si le champ est numerique est que les valeurs sont equivalentes - AND (!is_numeric($v) OR intval($v)!=intval($champs[$k])) - ) { + // ne pas alerter si le champ est numerique est que les valeurs sont equivalentes + AND (!is_numeric($v) OR intval($v)!=intval($champs[$k])) + ) { $liste[] = $k; $conflits[$k]['post'] = $champs[$k]; $conflits[$k]['save'] = $v; + + // cas specifique MySQL+emoji : si l'un est la + // conversion utf8_noplanes de l'autre alors c'est OK + if (defined('_MYSQL_NOPLANES') && _MYSQL_NOPLANES) { + include_spip('inc/charsets'); + if ($v == utf8_noplanes($champs[$k])) { + array_pop($liste); + } + } } + } // si un champ n'a pas ete correctement enregistre, loger et retourner une erreur // c'est un cas exceptionnel if (count($liste)){ diff --git a/ecrire/inc/utils.php b/ecrire/inc/utils.php index d4b2d29202..b998a2e586 100644 --- a/ecrire/inc/utils.php +++ b/ecrire/inc/utils.php @@ -922,7 +922,7 @@ function queue_sleep_time_to_next_job($force=null) { // http://doc.spip.org/@quote_amp function quote_amp($u) { return preg_replace( - "/&(?![a-z]{0,4}\w{2,3};|#x?[0-9a-f]{2,5};)/i", + "/&(?![a-z]{0,4}\w{2,3};|#x?[0-9a-f]{2,6};)/i", "&",$u); } diff --git a/ecrire/req/mysql.php b/ecrire/req/mysql.php index 321f685cbd..efb33c5e3d 100644 --- a/ecrire/req/mysql.php +++ b/ecrire/req/mysql.php @@ -118,8 +118,9 @@ $GLOBALS['spip_mysql_functions_1'] = array( //'iso-8859-6'=>array('charset'=>'latin1','collation'=>'latin1_swedish_ci'), 'iso-8859-9'=>array('charset'=>'latin5','collation'=>'latin5_turkish_ci'), //'iso-8859-15'=>array('charset'=>'latin1','collation'=>'latin1_swedish_ci'), -'utf-8'=>array('charset'=>'utf8','collation'=>'utf8_general_ci')) - ); +'utf-8'=>array('charset'=>'utf8','collation'=>'utf8_general_ci') + ) + ); /** @@ -488,6 +489,12 @@ function _mysql_traite_query($query, $db='', $prefixe='') { } $r = preg_replace(_SQL_PREFIXE_TABLE_MYSQL, '\1'.$pref, $query) . $suite; + // en option, remplacer les emoji (que mysql ne sait pas gérer) en 💩 + if (defined('_MYSQL_NOPLANES') && _MYSQL_NOPLANES) { + include_spip('inc/charsets'); + $r = utf8_noplanes($r); + } + #spip_log("_mysql_traite_query: " . substr($r,0, 50) . ".... $db, $prefixe", _LOG_DEBUG); return $r; } -- GitLab