如何在PHP上的HTML链接中包含用户提及?

时间:2011-03-05 18:47:50

标签: php regex parsing

我在评论网application上工作,我想解析用户提及(@user)作为链接。以下是我到目前为止的情况:

$text = "@user is not @user1 but @user3 is @user4";

$pattern = "/\@(\w+)/";
preg_match_all($pattern,$text,$matches);

if($matches){   

    $sql = "SELECT * 
            FROM users 
            WHERE username IN ('" .implode("','",$matches[1]). "')
            ORDER BY LENGTH(username) DESC";
    $users = $this->getQuery($sql);

    foreach($users as $i=>$u){
        $text = str_replace("@{$u['username']}",
        "<a href='#' class='ct-userLink' rel='{$u['user_id']}'>@{$u['username']}</a> ", $text);
    }

    $echo $text;
}

问题是用户链接正在重叠:

<a rel="11327" class="ct-userLink" href="#">
    <a rel="21327" class="ct-userLink" href="#">@user</a>1
</a>

如何避免链接重叠?

回答更新

由于选择了答案,这就是我的新foreach循环的样子:

foreach($users as $i=>$u){
    $text = preg_replace("/@".$u['username']."\b/",
    "<a href='#' title='{$u['user_id']}'>@{$u['username']}</a> ", $text);
}

6 个答案:

答案 0 :(得分:1)

问题似乎是某些用户名可以包含其他用户名。因此,您可以使用user1正确替换<a>user1</a>。然后,user匹配并替换为<a><a>user</a>1</a>。我的建议是将你的字符串替换更改为带有字边界的正则表达式,\ b,这是在用户名之后所必需的。

答案 1 :(得分:0)

你可以选择一个自定义的str替换功能,它在第一次替换时就会停止..比如......

function str_replace_once($needle , $replace , $haystack){       
        $pos = strpos($haystack, $needle);
    if ($pos === false) {
        // Nothing found
    return $haystack;
    }
    return substr_replace($haystack, $replace, $pos, strlen($needle));
}  

并使用它:

 foreach($users as $i=>$u){
        $text = str_replace_once("@{$u['username']}",
        "<a href='#' class='ct-userLink' rel='{$u['user_id']}'>@{$u['username']}</a> ", $text);
    }

答案 2 :(得分:0)

Twitter小部件有JavaScript代码来执行此操作。我在WordPress plugin中将它移植到PHP。这是相关部分:

function format_tweet($tweet) {
    // add @reply links
    $tweet_text = preg_replace("/\B[@@]([a-zA-Z0-9_]{1,20})/",
              "@<a class='atreply' href='http://twitter.com/$1'>$1</a>",
              $tweet);

    // make other links clickable
    $matches = array();
    $link_info = preg_match_all("/\b(((https*\:\/\/)|www\.)[^\"\']+?)(([!?,.\)]+)?(\s|$))/",
                 $tweet_text, $matches, PREG_SET_ORDER);

    if ($link_info) {
      foreach ($matches as $match) {
        $http = preg_match("/w/", $match[2]) ? 'http://' : '';
        $tweet_text = str_replace($match[0],
            "<a href='" . $http . $match[1] . "'>" . $match[1] . "</a>" . $match[4],
        $tweet_text);
      }
    }
    return $tweet_text;
}

答案 3 :(得分:0)

而不是解析'@user'解析'@user'(最后有空格)或'@user'甚至避免错误解析电子邮件地址(例如:mailaddress@user.com)'@user :'也应该被允许。如果用户名没有空格,这只会起作用......

答案 4 :(得分:0)

您不应该一次替换某个用户提及但一次全部替换。您可以使用preg_split来执行此操作:

// split text at mention while retaining user name
$parts = preg_split("/@(\w+)/", $text, -1, PREG_SPLIT_DELIM_CAPTURE);
$n = count($parts);

// $n is always an odd number; 1 means no match found
if ($n > 1) {
    // collect user names
    $users = array();
    for ($i=1; $i<$n; $i+=2) {
        $users[$parts[$i]] = '';
    }

    // get corresponding user information
    $sql = "SELECT * 
            FROM users 
            WHERE username IN ('" .implode("','", array_keys($users)). "')";
    $users = array();
    foreach ($this->getQuery($sql) as $user) {
        $users[$user['username']] = $user;
    }

    // replace mentions
    for ($i=1; $i<$n; $i+=2) {
        $u = $users[$parts[$i]];
        $parts[$i] = "<a href='#' class='ct-userLink' rel='{$u['user_id']}'>@{$u['username']}</a>";
    }

    // put everything back together
    $text = implode('', $parts);
}

答案 5 :(得分:0)

我喜欢解析'@user'的dnl解决方案,但可能不适合你。

无论如何,您是否尝试使用strip_tags函数删除锚标签?这样你就可以得到没有链接的字符串了,你可以解析它再次构建链接。

strip_tags