Результаты PHP: эффективные решения для вывода поисковых данных

Раздел: Функциональность -> Поиск

Результаты PHP: реализация поиска и вывода данных

Наиболее эффективное решение для вывода результатов поиска на PHP подразумевает использование подготовленных запросов PDO с пагинацией и кэшированием. Это обеспечивает безопасность, производительность и гибкость.

Как организовать защищённый поиск с постраничным выводом?

Пример структуры:


// config.php
$host = 'localhost';
$db   = 'search_db';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';

$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$opt = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false,
];
$pdo = new PDO($dsn, $user, $pass, $opt);
  

// search.php
$query = isset($_GET['q']) ? trim($_GET['q']) : '';
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$perPage = 10;
$offset = ($page - 1) * $perPage;

if ($query !== '') {
    $stmt = $pdo->prepare('SELECT * FROM articles WHERE title LIKE :q OR content LIKE :q2 ORDER BY created_at DESC LIMIT :lim OFFSET :off');
    $likeQuery = '%' . $query . '%';
    $stmt->bindParam(':q', $likeQuery, PDO::PARAM_STR);
    $stmt->bindParam(':q2', $likeQuery, PDO::PARAM_STR);
    $stmt->bindParam(':lim', $perPage, PDO::PARAM_INT);
    $stmt->bindParam(':off', $offset, PDO::PARAM_INT);
    $stmt->execute();
    $results = $stmt->fetchAll();

    // Подсчёт общего количества для пагинации
    $countStmt = $pdo->prepare('SELECT COUNT(*) FROM articles WHERE title LIKE :q OR content LIKE :q2');
    $countStmt->execute([':q' => $likeQuery, ':q2' => $likeQuery]);
    $total = $countStmt->fetchColumn();
    $totalPages = ceil($total / $perPage);
}
  

Типичная ошибка: использование mysql_* функций, которые удалены в PHP 7. Решение - переход на PDO или mysqli.

Другая проблема - SQL-инъекции при конкатенации строк. Подготовленные запросы исключают эту уязвимость.

Как выполнить поиск по файлам на сервере с помощью PHP?

Используется рекурсивный обход директорий и поиск подстроки в содержимом файлов.


function searchFiles($dir, $needle) {
    $results = [];
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)
    );
    foreach ($iterator as $file) {
        if ($file->isFile() && $file->getExtension() === 'txt') {
            $content = file_get_contents($file->getPathname());
            if (stripos($content, $needle) !== false) {
                $results[] = $file->getPathname();
            }
        }
    }
    return $results;
}
  

Ошибка: при большом количестве файлов скрипт может превысить лимит времени выполнения. Решение - ограничить глубину или использовать генераторы.

Как реализовать полнотекстовый поиск через MySQL FULLTEXT?

Для больших объёмов текста лучше использовать полнотекстовые индексы.


ALTER TABLE articles ADD FULLTEXT(title, content);
  

$stmt = $pdo->prepare('SELECT *, MATCH(title, content) AGAINST(:q IN BOOLEAN MODE) AS relevance FROM articles WHERE MATCH(title, content) AGAINST(:q2 IN BOOLEAN MODE) ORDER BY relevance DESC');
$stmt->execute([':q' => $query, ':q2' => $query]);
$results = $stmt->fetchAll();
  

Проблема: минимальная длина слова для индекса (по умолчанию 4 символа). Изменяется через параметр ft_min_word_len. Необходим ребилд индекса.

Как сделать поиск с использованием регулярных выражений (regex)?

Применяется для сложных шаблонов, например, поиск email или ссылок.


$pattern = '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/';
preg_match_all($pattern, $content, $matches);
print_r($matches[0]);
  

Ошибка: неверный синтаксис регулярного выражения приводит к предупреждениям. Нужно проверять корректность через preg_match с заведомо валидным шаблоном.

Как организовать AJAX-поиск с динамической подгрузкой результатов?

Клиент отправляет запрос к PHP-скрипту, который возвращает JSON.


// search_ajax.php
header('Content-Type: application/json');
$query = $_GET['term'] ?? '';
$results = [];
if (strlen($query) > 2) {
    $stmt = $pdo->prepare('SELECT id, title FROM articles WHERE title LIKE :q LIMIT 5');
    $stmt->execute([':q' => '%' . $query . '%']);
    $results = $stmt->fetchAll();
}
echo json_encode($results);
  

Проблема: частые запросы при вводе - необходимо добавить debounce на стороне клиента. Нагрузка на сервер возможна при большом количестве пользователей.

Расширенные примеры работы с результатами PHP

Пример 1: Поиск с пагинацией и подсветкой совпадений

Пример

function highlight($text, $query) {
    return preg_replace('/(' . preg_quote($query, '/') . ')/iu', '$1', $text);
}

// в цикле вывода
foreach ($results as $row) {
    $title = highlight($row['title'], $query);
    $content = highlight(substr($row['content'], 0, 200), $query);
    echo "<article><h3>$title</h3><p>$content...</p></article>";
}
Результат: заголовок "Как начать с PHP" при поиске "php" превращается в "Как начать с PHP".

Пример 2: Поиск по нескольким таблицам с UNION

Пример

$stmt = $pdo->prepare('
    SELECT 'post' AS type, id, title, created_at FROM posts WHERE title LIKE :q
    UNION
    SELECT 'comment' AS type, id, content AS title, created_at FROM comments WHERE content LIKE :q
    ORDER BY created_at DESC
    LIMIT 20
');
$stmt->execute([':q' => '%' . $query . '%']);
$combined = $stmt->fetchAll();
Результат: массив записей с полем type, по которому можно определить источник.

Пример 3: Использование Elasticsearch через PHP (официальный клиент)

Пример

require 'vendor/autoload.php';
$client = Elastic\Elasticsearch\ClientBuilder::create()->build();
$params = [
    'index' => 'articles',
    'body'  => [
        'query' => [
            'multi_match' => [
                'query'  => $query,
                'fields' => ['title^3', 'content']
            ]
        ],
        'highlight' => [
            'fields' => [
                'content' => ['fragment_size' => 150, 'number_of_fragments' => 1]
            ]
        ]
    ]
];
$response = $client->search($params);
$hits = $response['hits']['hits'];
foreach ($hits as $hit) {
    echo $hit['_source']['title'];
    echo $hit['highlight']['content'][0] ?? '';
}
Выводятся заголовки и подсвеченные фрагменты контента. Elasticsearch требует установки и индексации.

Пример 4: Поиск с учётом опечаток (soundex)

Пример

// Поиск похожих по звучанию имён
$qSoundex = soundex($query);
$stmt = $pdo->prepare('SELECT * FROM users WHERE soundex(name) = :s');
$stmt->execute([':s' => $qSoundex]);
$users = $stmt->fetchAll();
При поиске "Jon" найдутся "John", "Jon" - функция soundex даёт одинаковый код.

Пример 5: Результаты поиска в формате JSON для SPA

Пример

// api/search.php
$query = $_GET['q'] ?? '';
if (strlen($query) < 2) {
    http_response_code(400);
    echo json_encode(['error' => 'Запрос слишком короткий']);
    exit;
}
$stmt = $pdo->prepare('SELECT id, title, excerpt FROM articles WHERE MATCH(title, content) AGAINST(:q IN NATURAL LANGUAGE MODE) LIMIT 20');
$stmt->execute([':q' => $query]);
$data = $stmt->fetchAll();
echo json_encode(['data' => $data, 'total' => count($data)]);
Ответ: {"data":[{"id":1,"title":"PHP примеры","excerpt":"..."}],"total":1}

Результаты PHP - comments

En
Results php (php)