PHP的多字节处理拾遗

在处理已一个PHPwind对于敏感词识别错误的问题中,发现了phpWind对于多字节处理存在很大的问题,例如admin\cache.php中如下代码:


$query = $db->query("SELECT * FROM pw_wordfb WHERE classid NOT IN ($classid) ORDER BY id");
while (@extract($db->fetch_array($query))) {
    if ($word) {
        $word = trim(preg_quote($word,'/'));
        //$word = preg_replace("/\\\{(\d+)\\\}/", ".{0,\\1}", $word);
        switch ($type) {
            case 1:
                $wordsfb[$word] = $wordreplace;
                break;
            case 2:
                $alarm[$word] = $wordreplace;
                break;
            case 3:
                $replace[$word] = $wordreplace;
                break;
            default:
                $replace[$word] = $wordreplace;
                break;
        }
    }
}

这里对于关键词作了$word = trim(preg_quote($word,'/'));处理,这条语句咋一眼看上去没有什么问题,但是别忘了,preg_quote是为单字节而设计的函数,因此当用他处理多字节串字符串的时候就有可能出错,为什么说有可能呢?那是因为这跟编码有关系,例如,在GBK编码下执行:


echo preg_quote('遊行');

他会莫名奇妙的输出運[行,这其实一点也不奇怪,这是因为遊行对应的字节码为DF 5B D0 D0,这其中5B对应相对于ASCII中的编码即为[,也就是说 preg_quote函数把DF 5B(遊)这个整体拆分开来处理了,把的第二个编码当成了单个的ascii码[处理,给他也加上了\反斜杠进行转义,于是原先的编码就变成了DF 5C 5B D0 D0,即運[行

其实仔细考虑下,多字节编码的问题其实是普遍存在的,例如我们常用的str_replace函数,还有spilt函数,这些常用函数其实都是存在多字节处理出错风险的。只是因为大部分情况下不大容易出错,因此我们就忽略这些风险。当然对于多字节的字符串的处理PHP官方库中还是有相对应的mb_api进行处理的,PHP多字节处理函数,但是发现这其中居然没有mb_str_replace()这个函数,这是我最急需的一个函数,在该页的comments中发现了一个宝贝。


function mb_str_replace($search, $replace, $subject) {

if(is_array($subject)) {
    $ret = array();
    foreach($subject as $key => $val) {
        $ret[$key] = mb_str_replace($search, $replace, $val);
    }
    return $ret;
}

foreach((array) $search as $key => $s) {
    if($s == '') {
        continue;
    }
    $r = !is_array($replace) ? $replace : (array_key_exists($key, $replace) ? $replace[$key] : '');
    $pos = mb_strpos($subject, $s);
    while($pos !== false) {
        $subject = mb_substr($subject, 0, $pos) . $r . mb_substr($subject, $pos + mb_strlen($s));
        $pos = mb_strpos($subject, $s, $pos + mb_strlen($r));
    }
}

return $subject;
}

该函数能比较完美的处理多字节的字符串替换需求。基于此也诞生了多字符串正则转义函数。


function mb_preg_quote($subject){
    $search = array('.','\\','+','*','?','[','^',']','$','(',')','{','}','=','!','<','>','|',':','-');
    $replace = array('\.','\\\\','\+','\*','\?','\[','\^','\]','\$','\(','\)','\{','\}','\=','\!','\<','\>','\|','\:','\-');
    return mb_str_replace($search,$replace,$subject);
}

当然,要想多字节字符串处理函数有效,需要确保两点

  1. 安装了mb扩展
  2. mb_internal_encoding()函数 — 设置/获取内部字符编码

你可能感兴趣的:(PHP,多字节)