Безпечний і зручний пошук

Дмитро Лебедєв, Клуб PHP-розробників

Головне, з чим стикаєшся при написанні скрипта для пошуку – те, що все здається простим, але обсяг коду швидко наростає.

Обробка рядки

Насамперед треба порізати ручками рядок.

$search = substr($search, 0, 64);

64 символів користувачеві буде досить для пошуку. Тепер розпеченим залізом випалимо все "ненормальні" символи.

$search = preg_replace(“/[^(w)|(x7F-xFF)|(s)]/”, ” “, $search);

По ідеї, не можна давати користувачеві можливості шукати за надто коротким словами – крім усього іншого, це сильно завантажує сервер. Отже, дозволимо шукати тільки за словами, які довше двох літер (якщо обмеження більше, треба замінити "{1,2}" на "{1, к-ть символів}").

$ Good = trim (preg_replace ("/ s ([^ s] {1,2}) s /", "", ereg_replace ("[] +", "", "$ search ")));

А після заміни поганих слів – треба стиснути подвійні пробіли (вони були зроблені спеціально для коректного пошуку коротких слів).

$good = ereg_eplace(“[ ]+”, ” “, $good);

Логіка

Припустимо, ми хочемо надати користувачеві можливість вибирати логіку пошуку – шукати всі слова або тільки одне з декількох. Якщо ви хочете зробити як в Яндексі [2] [1] – два амперсанд означають "І" (Слово1 & & слово2 & & слово3) або якось ще, то я не порадник. Шаманство з рядками на невеликому сайті imho не виправдовує витраченого часу. Тому форму для пошуку малюємо так:

А в пошуковому скрипті зайвий раз перевіряємо, що користувач ввів:

if ($logic!=”AND” && $logic!=”OR”)

  $logic = “OR”;

Як буде використовуватися логіка – нижче.

Релевантність

Напевно, в тому ж Яндексі [2] [1] всі бачили ссилочку "сортувати за релевантністю". Це воно і є. Сортування результатів за кількістю збігів слів.

Почасти, до речі, така сортування знімає проблему обробки логіки пошуку. Але з БД MySQL робити таку сортування дуже складно. Треба спершу вибрати, де є всі слова, потім записи, де різні слова (виключивши попередні). Якщо у вас посторінковий висновок – то взагалі справа труба!

Статистика пошуку

Непогано буде відразу інформувати користувача, скільки він знайшов рядків таблиці. Для цього робиться додатковий запит до бази:

$ Query = "SELECT id FROM table WHERE field LIKE '%". str_replace ("", "% 'OR field LIKE'%", $ good). "%'";

Для статистики по окремим словам можна зробити наступне:

$word = explode(” “, $search);

while (list($k, $v) = each($word)) {

  if (strlen($v)>2)

$ Stat []="$ v: ". mysql_num_rows (mysql_query ("SELECT id FROM table WHERE field LIKE '% $ v %'"));

  else

$ Stat []="$ v: <font color=#cc0000> короткий </ font> ";

  };

$ Word_stats = "Статистика слів:". implode ("", $ stat). "<br>";

unset($stat);

Посторінковий вивід результатів

Ну, коли у нас є макет для пошуку і кількість рядків результату пошуку, зробити посторінковий пошук – пари дрібниць. Перевіряємо змінну $ page (не менше 0, не більше $ results_amount / $ rows_in_page).

У запит, який підраховує кількість рядків (дивися вище), пишемо потрібні нам поля та поля для сортування. А потім дописуємо

if ($page==0)

  $request .= “LIMIT $rows_in_page”;

else

$ Request .= "LIMIT". $ Page * $ rows_in_page. ",". $ Rows_in_page;

(Синтаксис: LIMIT <кількість рядків> або LIMIT <кількість рядків відступу>, <кількість рядків>)

В результаті виконання подібного запиту ми отримаємо саме ті самі рядки, які треба виводити на сторінці.

Для навігації можна або малювати посилання на наступну і попередню сторінки, або, що складніше, робити панель навігації на кілька сторінок.

if ($page>0)

print ("<a href=search.php?search=". rawurlencode($good). "&page=". ($page-1). "> попередня сторінка </ a>");

if ($page<$results_amount/$rows_in_page)
print ("<a href=search.php?search=". rawurlencode($good). "&page=". ($page+1). "> наступна сторінка </ a>");

Підсвітка

Щоб підсвічувати світлом або жирним шрифтом шукані слова в тексті, треба зробити всього лише наступне:

$highlight = “(“. str_replace(” “, “|”, $good). “)”;

Прогалини (а вони у нас між словами стоять поодинці, і ніде подвійний пробіл не зустрічається, до того ж з кінців рядка ми їх теж вирізали) досить замінити на вертикальну риску – роздільник варіантів в регулярних виразах. "Погані" слова ми не підсвічуються, тому що в базі їх не шукаємо:).

У коді, який виводить текст пишемо:

$ Row ["text"] = ereg_replace ($ highlight, "<font color=#cc0000>1</font>”, $row[“text”]);

Після написання випуску я кинувся, було, писати і собі "підсвічування". Не тут-то було! У мене в тексті зустрічаються теги HTML, тому довелося багато подумати … Вийшла ось така річ (рядок зі словами для підсвічування є):

$text = eregi_replace(“>([^<]*)$words”, “>1<font color=#cc0000>2</font>3<“, $text);

Доводиться дивитися, чи немає в тезі чи це слово. Проте тут постає проблема ресурсоємності такої заміни (мій K6-266 над текстом у 5 кілобайт думав цілих сім секунд). Сумно.

Підсумок

Застосовуючи такі прийоми, можна, по-перше, обмежити свободу дій користувача і не дати йому а) довідатися програмну структуру сайту б) викликати перевантаження сервера (наприклад, відправивши мегабайт тексту, що складається з слів завдовжки в три літери (фраза вийшла двозначна, але переписувати не буду:), щоб скрипт 250 тисяч разів лазив в базу) в) побачити повідомлення про помилку в результаті попадання в рядок спецсимволів мови запитів. По-друге, деякий зручність для користувача – посторінковий висновок і підсвічування.

Посилання, використані у випуску:

1) http://www.yandex.ru
2) http://www.yandex.ru

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*