记一次@mention的改造
早在[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
'; } ?> [/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 条回复