记一次@mention的改造
tsuna 1378天前 · IP已记录 247

早在[url=https://www.fffdann.com/showthread.php?tid=238]这个帖子[/url]就有用户反馈社区的@mention功能有缺陷,在@中文用户名的用户时经常失败。今天下班早,决定解决下这个问题。 定位到官方原有的@mention的user_tagging_tag()核心方法,发现使用了PHP substr()方法提取用户名。在utf-8编码中,每个汉字占3字节,英文占1字节。因而出现@英文用户名的用户没问题,但@中文用户名的用户不成功。截取中文用户名位数不准确,导致提取@中文用户名失败。   [code]   function user_tagging_tag($msg) {    global $db;    $delimiter = ' ' . crypt($msg, $time) . ' ';    $tagged = array(); //array to hold tagged users, incase of multiple tagging        //break msg down to only spaces, then create array    $msgNoNewLines = str_replace("\\n", $delimiter, $msg); //change all newlines into spaces for parsing method    $msgParts = explode(' ', $msgNoNewLines);        for($i = 0; isset($msgParts[$i]); ++$i) {       //if it starts with @ and hasn't been tagged before   if(substr($msgParts[$i], 0, 1) == '@') {         for($j = 1; ($i + $j - 1) < sizeof($msgParts); $j++) {         $size = 0; $message = $msgParts[$i];         //get the current size and message         for($k = 0; ($k + $i) < sizeof($msgParts) && $k < $j; $k++) {            $size += strlen($msgParts[$i + $k]);    if($k > 0) {      $message .= ' ' . $msgParts[$i + $k];    } }             $search = substr($message, 1);     $search = $db->escape_string($search);         if(preg_match('/\W/', substr($message, -1))) //if the last character is non-word ie punctuation of some sort             {            $search2 = substr($message, 1, (strlen($message) - 2)); //get between @ and last char            $search2 = $db->escape_string($search2);         } else {            $search2 = "";         }         $query = $db->simple_select("users", "uid,username", "username='{$search}'");                  $user = $db->fetch_array($query); if($user) {    //put the url tags around it    $msgParts[$i] = '[url=/member.php?action=profile&uid=' . $user['uid'] . ']'. $msgParts[$i];                $msgParts[$i + $j - 1] = $msgParts[$i + $j - 1] . '[/url]';    if(!in_array($user['uid'], $tagged)) //if first tag in post, send pm            {       array_push($tagged, $user['uid']);   //send the pm   send_pm($user['username']);    } } else if($search2) { //no match try second search if exists    $query = $db->simple_select("users", "uid,username", "username='{$search2}'");    $user = $db->fetch_array($query);                  if($user) {       $msgParts[$i] = '[url=/member.php?action=profile&uid=' . $user['uid'] . ']'. $msgParts[$i];                   $msgParts[$i + $j - 1] = substr($msgParts[$i + $j - 1], 0, (strlen($msgParts[$i + $j - 1]) - 1)) . '[/url]' . substr($msgParts[$i + $j - 1], -1);              if(!in_array($user['uid'], $tagged)) //if first tag in post, send pm   {     array_push($tagged, $user['uid']); //send the pm    send_pm($user['username']);   }    } }  }   }    }        //put the message back together    $msg = implode(" ", $msgParts);    $msg = str_replace($delimiter, "\\n", $msg);    return $msg; } [/code] 一开始使用mb_substr()替换substr(),发现不行。使用substr()截断字符串的方式改起来有点麻烦,于是到网上找了下是否有其他更好的提取方法。 在stack overflow上看到一个比较好的@mention解决方案 [url=https://stackoverflow.com/questions/17817224/php-twitter-like-mentions-with-notifications]PHP Twitter-like mentions with notifications[/url] Steve Kinzey的回答 [quote] I have been thinking about this question for a couple hours and although I am somewhat of a novice, I believe I have a way that extracts the mentions and the alerts and stores them into two unidimensional arrays. [/quote]   [code] '; } //test to make sure that all messages are stored for ($k = 0; $k< $j; $k++){ echo $message[$k],'
'; } ?> [/code] 看来使用preg_match_all()函数是个更快捷的方式 :face-with-raised-eyebrow:mile-face-with-funny: 换上能匹配中文用户名的正则表达式,改造下用在user_tagging_tag()方法 [code] function user_tagging_tag($msg) {   global $db;   $delimiter = ' ' . crypt($msg, $time) . ' ';   //break msg down to only spaces, then create array   $msgNoNewLines = str_replace("\\n", $delimiter, $msg); //change all newlines into spaces for parsing method   $msgParts = explode(' ', $msgNoNewLines);   for($i = 0; isset($msgParts[$i]); ++$i) {     // -------------------------2021/01/06更新 start-------------------------     preg_match_all('/\B\@([a-zA-Z0-9\x80-\xff\-_]{2,50})/', $msgParts[$i], $result, PREG_PATTERN_ORDER);     for ($j = 0; $j < count($result[0]); $j++) {        $mention[$j]= $result[1][$j];     }   for ($k = 0; $k< $j; $k++) {   $search = $mention[$k];   $query = $db->simple_select("users", "uid,username", "username='{$search}'");   $user = $db->fetch_array($query);   if($user) {     //put the url tags around it     $tagged = '[url=/member.php?action=profile&uid=' . $user['uid'] . ']@'. $mention[$k] . '[/url]';     $msgParts[$i] = substr_replace('@'.$mention[$k], $tagged, $msgParts[$i]);         //send the pm     send_pm($user['username']);     }   } // -------------------------2021/01/06更新 end------------------------- } //put the message back together $msg = implode(" ", $msgParts); $msg = str_replace($delimiter, "\\n", $msg); return $msg; } [/code] 测试下,效果非常ok 狗头

8 条回复