0

Здравствуйте!

Никак не могу разобраться с циклом foreach. Необходимо перебирать массив, в котором слова поискового запроса, и выводить полученные результаты на страницу. Но после всей обработки поискового запроса функция count() мне выдаёт нулевое значение массива, в котором должны быть извлечены данные, соответствующие поисковому запросу.

Код обработчика такой:

if (isset($submit)) {
    if (empty($query) or strlen($query) < 6) {
        echo "<div class='search_title_attention'>Поисковый запрос не введён, либо он менее 3-х символов кириллицы<br>или менее 6-ти символов латиницы!</div>";
    } else { // Начало вывода
        print_r($query);
        $words = clearQuery($query);
        print_r($words);
        if ($select == 'all') {
        searchArticles($words);
        $count = count($array);
        echo $count;
    }
}

} else { echo "<div class='search_title_attention'>Вы обратились к файлу без необходимых параметров!</div>"; }

Функции, которые используются:

/*Функция обрезки окончаний слов*/

function dropBackWords($word) { $reg = "/(а|я|о|е|ь|и|ы|ая|яя|ое|ее|ый|ать|ять|еть|уть|у|ю|ем|ешь|ете|ет|ут|ют|ал|ял|ала|яла|али|яли|ул|ула|ули)(\s|$)/ui"; //данная регулярная функция будет искать совпадения окончаний $word = preg_replace($reg,'',$word); return $word; }

/Функция уничтожения стоп-слов/

function stopWords($query) { //тут мы обрабатываем весь поисковый запрос $reg = "/(^|\s)(а|без|более|бы|был|была|были|было|быть|в|вам|вас|весь|во|вот|все|всего|всех|вы|где|да|даже|для|до|его|ее|если|есть|еще|же|за|здесь|и|из|или|им|их|к|как|ко|когда|кто|ли|либо|мне|может|мы|на|надо|наш|не|него|нее|нет|ни|них|но|ну|о|об|однако|он|она|они|оно|от|очень|по|под|при|с|со|так|также|такой|там|те|тем|то|того|тоже|той|только|том|ты|у|уже|хотя|чего|чей|чем|что|чтобы|чье|чья|эта|эти|это|я)($|\s)/ui"; //данная регулярка отрежет все стоп-слова отбитые пробелами $query = preg_replace($reg,'',$query); return $query; }

/Функция подготовки поискового запроса/

function clearQuery($query) { $query = stripcslashes($query); $query = htmlspecialchars($query); $words = explode(" ",$query); $words = stopWords($words); $i = 0; $keywords = "";

foreach ($words as $word) {
    $word = trim($word);
    $regv = '/^([а-я0-9]+)$/i';
    $count = (preg_match($regv, $word)) ? 6 : 3;
    if (strlen($word)&lt;$count) {
        unset($word);
    } else {
        if (strlen($word)&gt;8) {
            $keywords[$i]=dropBackWords($word);
            $i++;
        } else {
            $keywords[$i]=$word;
            $i++;
        }
    }
}
return $keywords;

}

function dataToArray($data) { $array = array(); while (($row = $data->fetch()) !=false) $array[] = $row; return $array; }

function searchArticles($words) { $query_search = ""; foreach ($words as $key => $value) { if (isset($words[$key - 1])) { $query_search .= " OR "; $query_search .= "(title LIKE '%' . $value . '%' OR text LIKE '%' . $value . '%')"; } }

try {
    $pdo = new PDO("mysql:host=xxx; dbname=xxxx", "xxx", "xxx");
    $pdo-&gt;setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    echo "&lt;p&gt;Запрос на выборку данных из бызы не прошёл. Напишите об этом администратору &lt;a href='mailto:admin@torawhite.ru'&gt;admin@torawhite.ru&lt;/a&gt;.&lt;/p&gt;&lt;br&gt;ERROR: " . $e-&gt;getMessage();
    exit;
}

$sql = 'SELECT * FROM male_articles WHERE :query_search';
$data = $pdo-&gt;prepare($sql);
$data-&gt;bindParam(':query_search', $query_search);
$data-&gt;execute();

return dataToArray($data);

}

Ещё прошу развеять сомнения насчёт подключения к базе данных - я так понимаю, что подключение нужно обязательно использовать в функции, обозначить подключение вначале самой страницы результатов не даст?

 echo $count;

Выводит 0, а должен найти запись. В поисковый запрос ввожу только то, что точно есть в базе данных.

Torawhite
  • 271
  • 2
  • 11

1 Answers1

0

я так понимаю, что подключение нужно обязательно использовать в функции

Необязательно. Для простенького примера/наброска подойдёт и лапша-код, к примеру, чтобы разместить его здесь.

В Вашем же примере используется процедурный стиль, а потому: функции в PHP имеют локальную область видимости, т.е. переменные извне недоступны (в том же Си или JS иначе).

Замечания:

  • Используйте полнотекстовый поиск, который в MySQL доступен из коробки (для InnoDB с MySQL 5.6). Хоть какая-то релевантность будет.
  • Вместо Вашего набора функций по нахождению базовой формы слова, используйте phpmorphy (копия на github)
  • Если не ограничены рамками shared-хостинга, то настоятельно рекомендую использовать специализированные поисковые движки: Sphinx или elasticsearch. Оба инструмента всё вышеперечисленное умеют (быстрая индексация, поиск с учётом морфологии для многих языков) + различные плюшки: подсветка (highlight) ключевых слов и т.д.

API/ORM последних можно встретить в различных фреймворках.

Немного ещё по Sphinx-у:

Если сумеете его всё же установить/настроить, то предлагаю воспользоваться моей реализацией ORM (это актуальный форк расширения Yii2 оформленный в виде независимого модуля).

UPDATE #1

Теперь по Вашему коду:

У Вас ошибка в понимании, когда использовать двойные и одинарные кавычки (в смешали оба варианта): "(title LIKE '%' . $value . '%' OR text LIKE '%' . $value . '%')"

Должно быть:

$query_search .= "(title LIKE '%{$value}%' OR text LIKE '%{$value}%')";

Вы неверно используете параметризованные запросы. В запрос попадают небезопасные данные. Вы кладёте конструкцию запроса (условия после WHERE) в параметр, т.е.

$data->bindParam(':query_search', "OR (title LIKE '%жил%' OR text LIKE '%пёс%')");

Как правильно:

function calculateWhere($words) {
    $query = $params = array();
    $i = 1;
    foreach ($words as $value) {
        $placeholder = ":plh{$i}";
        $params[$placeholder] = "%{$value}%";
        $query[] = "(title LIKE {$placeholder} OR text LIKE {$placeholder})";
        ++$i;
    }

    return array(implode(' OR ', $query), $params);
}

function searchArticles($where, $params) {
    try {
        /*
         Все манипуляции с кодировкой пробуйте на простом запросе, к примеру,
         "SELECT * FROM male_articles LIMIT 10". необходимо добиться отсутствия 
         абракадабры в кириллице на выходе. Уже потом можно приступить 
         к работе с LIKE
        */

        // вначале попробуйте без указания кодировки "charset", потом charset=utf8 или же charset=cp1251
        $pdo = new PDO("mysql:host=xxx; dbname=xxxx;charset=utf8", "xxx", "xxx");
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        /*
         Возможно, дополнительно потребуется добавить
         $encoding = 'cp1251'; // or utf8
         $pdo->exec("SET NAMES {$encoding}");
         $pdo->exec("SET CHARACTER SET {$encoding}");

         На текущий момент времени подобное не рекомендуется
        */

        $query = "SELECT * FROM male_articles WHERE {$where}";
        $sth = $pdo->prepare($query);
        $sth->execute($params);
        $sth->setFetchMode(PDO::FETCH_ASSOC);

        return $sth->fetchAll();
    } catch (PDOException $e) {
        echo "<p>Запрос на выборку данных из бызы не прошёл. Напишите об этом администратору <a href='mailto:admin@torawhite.ru'>admin@torawhite.ru</a>.</p><br>ERROR: " . $e->getMessage();
        exit;
    }
}

$query = 'жил был пёс';
$words = clearQuery($query);

// получаем условную часть запроса, которая идёт после WHERE и подготовленные параметры
list($where, $params) = calculateWhere($words);

// собственно, результат запроса
var_dump(searchArticles($where, $params));

UPDATE #2

...столько и файлов index.php

Нет, конечно:)

Вам необходимо почитать про MVC

  • Организовать единую точку входа. Конфиг для apache. Или хабр-статья
  • В index.php (та самая точка) организуем роутинг по урлу. Под капотом ReqExp(регулярные выражения) для парсинга урла и иные правила (смотрит, в request заголоки), к примеру, Ajax запрос или нет.
  • Последний направляет нас в соответствии с этими правилами в Контроллер (PostController) с action (content),т.е. (new PostController)->content(). Контроллер является связующим звеном между моделью и вьюхой. Как правило, должен содержать минимум бизнес-логики (существует порочная практика делать "толстые тупые уродливые контроллеры"). Пример контроллера
  • Получаем данные из модели. Моделью может быть любой источник данных: БД, массив, файл, html-форма. Модель может иметь встроенный валидатор и санитизатор данных, к примеру, в виде правил (статья на русском)

По поводу, как обезопасить себя (SQL injection, XSS) я недавно писал

  • Отправляем данный во вьюху. В качестве вьюхи может выступать либо некий шаблонизатор (Smarty, Twig,...), либо нативный php. Кхм, можно воспользоваться моим Rock Template: использует, как нативный php, так MODx-подобный синтаксис.
  • PROFIT

P.S. Но для начала позаботьтесь, чтобы у Вас была версия PHP не ниже 5.3, а лучше 5.4 и выше, ибо подавляющее большинство инструментов используют пакетный менеджер composer

romeo
  • 5,078
  • @Torawhite

    как минимум виртуальный выделенный сервер

    Недавно писал про возможные варианты за недорого. Вполне возможно. что Вы платите больше 149 руб. в месяц.

    Для использования phpmorphy не требуется выделенный сервер (словари в бинарниках). Так что, полнотекстовый поиск MySQL + phpmorphy вполне жизнеспособная связка на shared-хостинге.

    – romeo Feb 18 '15 at 09:44
  • @romeo с версией php попробую разобраться. А кодировку я указал AddDefaultCharset utf-8 в .htaccess. В самом начале выводились абракадабры. Читаю (http://habrahabr.ru/post/244561/) еще про phpmorphy, как Вы посоветовали. Смотрю, там кода больше раза в три. – Torawhite Feb 18 '15 at 12:09
  • @romeo У меня к Вам вопрос есть, немного отвлеченный от темы - я так понимаю, есть два метода вывода содержимого из базы в файл-просмотрщик. Тот же попов учил, например, передавать переменную id через адресную строку, файл её принимает и выводит нужные данные, так и у меня. Но, например, на хабре и других сайтах вижу папку (http://habrahabr.ru/post/244561/) То есть тут сколько статей столько и файлов index.php или тут какая-то другая реализация? – Torawhite Feb 18 '15 at 12:19
  • @Torawhite

    А кодировку я указал AddDefaultCharset utf-8 в .htaccess

    Иногда этого бывает недостатчно. Вы простом запросе SELECT * FROM male_articles LIMIT 10 получаете валидную кириллицу или вопросы/абракадабру?

    У Вас всё получилось? Я писал Вам код не с чистого листа, а с апробацией у себя. Если по-прежнему выводится пустой массив, то забейте в базу тестовые данные на латинице и вместо поискового запроса жил был пёс вбейте на латинице. Если получили результат, то манипулируйте с кодировками. Да, и смотрите в ответ, я добавил комментарии к коду.

    – romeo Feb 18 '15 at 12:36
  • Сейчас все работает http://torawhite.ru/magazine/search_view.php?submit=&select=all&query=%D0%B3%D0%B5%D1%88%D0%B5+%D0%BC%D0%B0%D0%B9%D0%BA%D0%BB+%D1%80%D0%BE%D1%83%D1%87 Выводится кириллица, посмотрите сами. Сейчас попробую сделать вывод SELECT * FROM male_articles LIMIT 10 Попробовал, выводится без проблем с кодиковкой – Torawhite Feb 18 '15 at 12:43
  • @Torawhite

    ...столько и файлов index.php

    Дополнил ответ.

    Да, и мы с Вами нарушаем регламент хэшкода. Вопросы должны быть атомарны: один вопрос - один ответ.

    И если вам дан исчерпывающий ответ, отметьте его как верный (нажмите на галку рядом с выбранным ответом) + можно ещё лайк.

    P.S. Напишите мне на почту. Мне кое-что интересно.

    – romeo Feb 18 '15 at 14:52
  • @Torawhite У меня есть абстракция над phpmorphy (умеет возвращать базовые формы слова, словоформы слова и подсветку ключевых слов). Пока она часть моего фреймворка. На данный момент все компоненты преобразую в модули, чтобы работали независимо от фреймворка. phpmorphy на очереди. Будет и документация по использованию. Единственное,все мои инструменты используют PHP 5.4. Я могу сделать исключение для phpmorphy понизив до 5.3. – romeo Feb 18 '15 at 14:57
  • @romeo лайк не даёт ставить, говорит, "не дорос" @romeo по ссылке Ваш профиль, не могу там найти e-mail, вот мой torawhite@yandex.ru – Torawhite Feb 18 '15 at 15:31