PHP упоминает систему с именами пользователей через пробел

#php #html

#php #HTML

Вопрос:

Я хотел знать , возможно ли создать систему упоминания PHP с именами пользователей через пробел ? Я попробовал это

 preg_replace_callback('#@([a-zA-Z0-9] )#', 'mentionUser', htmlspecialchars_decode($r['content']))
  

Моя функция:

     function mentionUser($matches) {
    global $db;
    $req = $db->prepare('SELECT id FROM members WHERE username = ?');
    $req->execute(array($matches[1]));

    if($req->rowCount() == 1) {
        $idUser = $req->fetch()['id'];
        return '<a class="mention" href="members/profile.php?id='.$idUser.'">'.$matches[0].'</a>'; 
    }
    return $matches[0]; 
  

Это работает, но не для имен пользователей с пробелом…
Я попытался добавить s , это работает, но не очень хорошо, preg_replace_callback определяет имя пользователя и другие части сообщения, поэтому упоминание не появляется…
Есть ли какое-нибудь решение?
Спасибо!

Комментарии:

1. Можете ли вы str_replace() для каждого [SPACE] указать заполнитель типа , а затем str_replace вернуться обратно, когда вернете разметку?

2. Вы хотите ограничить ее только одним пробелом, поэтому я думаю, @(w (?:sw )?) это то, что вы ищете? Я переключился на символьный класс w , который такой же, как у вас, но также допускает подчеркивание, вы могли бы легко поменять местами обратно

3. Решение Криса Хааса работает, но есть проблема, если есть «@Mention [некоторый текст]», упоминание не работает…

4. Я сказал это для его комментария, а не для его ответа, потому что ответ работает так, как я хотел

Ответ №1:

Я знаю, вы сказали, что просто удалили возможность добавлять пробел, но я все равно хотел опубликовать решение. Для ясности, я не обязательно думаю, что вам следует использовать этот код, потому что, вероятно, так проще упростить задачу, но я думаю, что он все равно должен работать.

Ваша основная проблема заключается в том, что почти каждое упоминание требует двух поисковых запросов, потому что @bob johnson went to the store это может быть либо bob , либо bob johnson , и нет способа определить это, не обращаясь к базам данных. К счастью, кэширование значительно уменьшит эту проблему.

Ниже приведен некоторый код, который обычно делает то, что вы ищете. Я создал поддельную базу данных, используя только массив для наглядности и воспроизводимости. Надеюсь, встроенные комментарии к коду должны иметь смысл.

 function mentionUser($matches)
{
    // This is our "database" of users
    $users = [
        'bob johnson',
        'edward',
    ];
    
    // First, grab the full match which might be 'name' or 'name name'
    $fullMatch = $matches['username'];

    // Create a search array where the key is the search term and the value is whether or not
    // the search term is a subset of the value found in the regex
    $names = [$fullMatch => false];
    
    // Next split on the space. If there isn't one, we'll have an array with just a single item
    $maybeTwoParts = explode(' ', $fullMatch);
    
    // Basically, if the string contained a space, also search only for the first item before the space,
    // and flag that we're using a subset
    if (count($maybeTwoParts) > 1) {
        $names[array_shift($maybeTwoParts)] = true;
    }

    foreach ($names as $name => $isSubset) {
        
        // Search our "database"
        if (in_array($name, $users, true)) {
            
            // If it was found, wrap in HTML
            $ret = '<span>@' . $name . '</span>';
            
            // If we're in a subset, we need to append back on the remaining string, joined with a space
            if ($isSubset) {
                $ret .= ' ' . array_shift($maybeTwoParts);
            }

            return $ret;
        }
    }
    
    // Nothing was found, return what was passed in
    return '@' . $fullMatch;
}

// Our search pattern with an explicitly named capture
$pattern = '#@(?<username>w (?:sw )?)#';

// Three tests
assert('hello <span>@bob johnson</span> test' === preg_replace_callback($pattern, 'mentionUser', 'hello @bob johnson test'));
assert('hello <span>@edward</span> test' === preg_replace_callback($pattern, 'mentionUser', 'hello @edward test'));
assert('hello @sally smith test' === preg_replace_callback($pattern, 'mentionUser', 'hello @sally smith test'));
  

Комментарии:

1. С помощью этого можно обновить упоминание, если пользователь изменит свое имя, пожалуйста?

2. Как только вы найдете @-упоминание, вы должны пометить его чем-то вроде data атрибута идентификатора пользователя, например, <a data-user-id="1234">user</a> поскольку вы это уже знаете. При рендеринге страницы вам нужно будет решить, какая логика работает для вашего бизнес-примера. Например, если я изменю свое отображаемое имя на этом сайте сегодня, должны ли измениться и мои упоминания за более чем 10 лет? Пока ссылка все еще находит меня, лично меня устраивает, что мое имя для упоминания остается старым. Для меня @-упоминание — это момент времени и исторический, на который не повлияют будущие изменения.

Ответ №2:

Попробуйте это регулярное выражение:

 /@[a-zA-Z0-9] ( *[a-zA-Z0-9] )*/g
  

Сначала он найдет знак at, а затем попытается найти одну или несколько букв или цифр. Она попытается найти ноль или более внутренних пробелов и ноль или более букв и цифр, следующих за этим.

Я предполагаю, что имя пользователя содержит только A-Za-z0-9 и пробел.

Комментарии:

1. Хммм, когда я пытаюсь это сделать, ответы не отображаются

2. @Lunyx Вам следует указать больше контекста, поскольку «ответы не отображаются» не дает мне никакого представления о том, что происходит в вашем коде.