Ограничить поиск латинскими буквами

9

Я хотел бы ограничить поиск символами, используемыми на английском языке + цифры. Причина в том, что, глядя на самые медленные запросы в журнале mysql, которые я нашел больше всего, они приходят из запросов на арабские, русские и китайские символы, поэтому я хотел бы пропустить их и вместо этого отобразить сообщение об ошибке.

Майкл Роджерс
источник
Если вы подробно
bosco
Я хочу, чтобы ошибка отображалась на странице поиска, под или над формой поиска.
Майкл Роджерс

Ответы:

10

Это решение фильтрует строки поиска путем применения регулярного выражения, которое соответствует только символам из сценариев Common и Latin Unicode.


Сопоставление латинских символов с регулярными выражениями

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

Это делается с помощью \pметасимвола, за которым следует идентификатор категории Юникод в фигурных скобках - так что [\p{Common}\p{Latin}]соответствует одному символу в латинских или общих сценариях - это включает знаки препинания, цифры и прочие символы.

Как указывает @Paul «Sparrow Hawk» Бирон , u флаг модификатора шаблона должен быть установлен в конце регулярного выражения, чтобы функции PHPRE PHP обрабатывали строку-тему как UTF-8кодировку Unicode.

Все вместе, то шаблон

/^[\p{Latin}\p{Common}]+$/u

будет соответствовать всей строке, состоящей из одного или нескольких символов в латинском и общем Unicode-скриптах.


Фильтрация строки поиска

Хорошее место , чтобы перехватить строку поиска является действие , как это срабатывает непосредственно перед WordPress выполняет запрос. С большей осторожностью это также может быть достигнуто с помощью фильтра .pre_get_postsrequest

function wpse261038_validate_search_characters( $query ) {
  // Leave admin, non-main query, and non-search queries alone
  if( is_admin() || !$query->is_main_query() || !$query->is_seach() )
    return;

  // Check if the search string contains only Latin/Common Unicode characters
  $match_result = preg_match( '/^[\p{Latin}\p{Common}]+$/u', $query->get( 's' ) );

  // If the search string only contains Latin/Common characters, let it continue
  if( 1 === $match_result )
    return;

  // If execution reaches this point, the search string contains non-Latin characters
  //TODO: Handle non-Latin search strings
  //TODO: Set up logic to display error message
}

add_action( 'pre_get_posts', 'wpse261038_validate_search_characters' );

Отвечая на запрещенные поиски

После того, как будет определено, что строка поиска содержит нелатинские символы, вы можете использовать ее WP_Query::set()для изменения запроса, изменив его именованные переменные запроса - таким образом влияя на SQL-запрос, который WordPress впоследствии создает и выполняет.

Наиболее важные переменные запроса, вероятно, следующие:

  • sпеременная запроса, соответствующая строке поиска. Установка этого параметра nullили пустой строки ( '') приведет к тому, что WordPress больше не будет обрабатывать запрос как поиск - часто это приводит к тому, что шаблон архива отображает все сообщения или первую страницу сайта, в зависимости от значений других запрос вари. ' 'Однако, если задать для него один пробел ( ), WordPress распознает его как поиск и попытается отобразить search.phpшаблон.
  • page_id может использоваться для направления пользователя на определенную страницу по вашему выбору.
  • post__inМожно ограничить запрос конкретным выбором сообщений. Устанавливая его в массив с невозможным идентификатором записи, он может служить мерой, гарантирующей, что запрос не даст абсолютно ничего .

Принимая во внимание вышесказанное, вы можете сделать следующее, чтобы ответить на неправильный поиск, загрузив search.phpшаблон без результатов:

function wpse261038_validate_search_characters( $query ) {
  // Leave admin, non-main query, and non-search queries alone
  if( is_admin() || !$query->is_main_query() || !$query->is_seach() )
    return;

  // Check if the search string contains only Latin/Common Unicode characters
  $match_result = preg_match( '/^[\p{Latin}\p{Common}]+$/u', $query->get( 's' ) );

  // If the search string only contains Latin/Common characters, let it continue
  if( 1 === $match_result )
    return;

  $query->set( 's', ' ' ); // Replace the non-latin search with an empty one
  $query->set( 'post__in', array(0) ); // Make sure no post is ever returned

  //TODO: Set up logic to display error message
}

add_action( 'pre_get_posts', 'wpse261038_validate_search_characters' );

Отображение ошибки

То, как вы на самом деле отображаете сообщение об ошибке, сильно зависит от вашего приложения и возможностей вашей темы - есть много способов, которыми это можно сделать. Если ваша тема вызывает get_search_form()в своем шаблоне поиска, простейшим решением, вероятно, является использование ловушки pre_get_search_formдействий для вывода вашей ошибки непосредственно над формой поиска:

function wpse261038_validate_search_characters( $query ) {
  // Leave admin, non-main query, and non-search queries alone
  if( is_admin() || !$query->is_main_query() || !$query->is_seach() )
    return;

  // Check if the search string contains only Latin/Common Unicode characters
  $match_result = preg_match( '/^[\p{Latin}\p{Common}]+$/u', $query->get( 's' ) );

  // If the search string only contains Latin/Common characters, let it continue
  if( 1 === $match_result )
    return;

  $query->set( 's', ' ' ); // Replace the non-latin search with an empty one
  $query->set( 'post__in', array(0) ); // Make sure no post is ever returned

  add_action( 'pre_get_search_form', 'wpse261038_display_search_error' );
}

add_action( 'pre_get_posts', 'wpse261038_validate_search_characters' );

function wpse261038_display_search_error() {
  echo '<div class="notice notice-error"><p>Your search could not be completed as it contains characters from non-Latin alphabets.<p></div>';
}

Некоторые другие возможности для отображения сообщения об ошибке включают в себя:

  • Если ваш сайт использует JavaScript, который может отображать «флэш» или «модальные» сообщения (или вы добавляете такие возможности самостоятельно), добавьте к нему логику для отображения сообщений при загрузке страницы, когда задана определенная переменная, затем добавьте wp_enqueue_scriptловушку с $priorityбольшим, чем тот, который ставит в очередь этот JavaScript, и используйте wp_localize_script()для установки этой переменной, чтобы включить ваше сообщение об ошибке.
  • Используется wp_redirect()для отправки пользователя по выбранному вами URL-адресу (этот метод требует дополнительной загрузки страницы).
  • Установите переменную PHP или вызовите метод, который сообщит вашей теме / плагину об ошибке так, что он может отобразить ее при необходимости.
  • Установите sпеременную запроса ''вместо ' 'и используйте page_idвместо post__in, чтобы вернуть страницу по вашему выбору.
  • Используйте loop_startловушку, чтобы WP_Postвставить поддельный объект, содержащий вашу ошибку, в результаты запроса - это, безусловно, уродливый хак, который может не соответствовать вашей конкретной теме, но имеет потенциально желательный побочный эффект - подавление сообщения «Нет результатов».
  • Используйте template_includeловушку фильтра, чтобы заменить шаблон поиска на собственный в вашей теме или плагине, который отображает вашу ошибку.

Не изучая обсуждаемую тему, сложно определить, по какому маршруту следует идти.

Bosco
источник
2

Вы могли бы сделать это, вставив функцию проверки в PHP, чтобы проверить ввод с помощью регулярного выражения, такого как ^[a-zA-Z0-9,.!?' ]*

Так это будет выглядеть так:

if ( preg_match( "^[a-zA-Z0-9,.!?'" ]*", {search variable} ) ) {
   // Success
} else {
   // Fail
}

RexEx я использовал для всех персонажей A-Z, a-z, 0-9, а также ,, ., !, ?, ', ", и (пробел).

Cedon
источник
2

РЕДАКТИРОВАТЬ: Это решение не рекомендуется

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

Пожалуйста, смотрите мой другой ответ для гораздо более простого и надежного решения.


Одним из способов предотвращения поиска с использованием нелатинских алфавитов является использование функции PHPmb_detect_encoding() для проверки соответствия строки поиска одной из пользовательских выборок кодировок символов. Хорошее место , чтобы сделать это действие , как он стреляет прямо перед запрос выполняется.pre_get_posts

То, что вы фактически делаете после того, как определили, что поиск использует недопустимую кодировку, действительно зависит от приложения. Здесь я установил поисковый запрос на один пробел, чтобы гарантировать, что WordPress все еще интерпретирует запрос как поиск и, таким образом, все еще загружает search.phpшаблон (и не направляет пользователя на первую страницу, как это происходит, когда строка поиска пустая строка). Я также принимаю дополнительные меры предосторожности при установке 'post__in'массива с невозможным идентификатором записи, чтобы гарантировать, что абсолютно ничего не будет возвращено .

С другой стороны, вы можете рассмотреть возможность установки строки поиска nullи настройки page_id, чтобы направить пользователя на страницу с вашим пользовательским сообщением об ошибке.

function wpse261038_validate_search_query_encoding( $query ) {
  $valid_encodings = array( 'Windows-1252' );

  // Ignore admin, non-main query, and non-search queries
  if( is_admin() || !$query->is_main_query() || !$query->is_seach() )
    return;

  // Retrieve the encoding of the search string (if it's one listed in $valid_encodings)
  $search_encoding = mb_detect_encoding( $query->get( 's' ), $valid_encodings, true );

  // If the search encoding is one in $valid_encodings, leave the query as-is
  if( in_array( $search_encoding, $valid_encodings ) )
    return;

  // If it wasn't, sabotage the search query
  $query->set( 's', ' ' );
  $query->set( 'post__in', array(0) );

  // Set up your error message logic here somehow, perhaps one of the following:
  // - Add a template_include filter to load a custom error template
  // - Add a wp_enqueue_scripts hook with a greater priority than your theme/plugin's, and
  //     use wp_localize_script() in the hook to pass an error message for your JavaScript
  //     to display
  // - Perform a wp_redirect() to send the user to the URL of your choice
  // - Set a variable with an error message which your theme or plugin can display
}

add_action( 'pre_get_posts', 'wpse261038_validate_search_query_encoding' );

Выбор кодировки

Я написал тест покрытия, сравнивающий несколько фиктивных строк в разных алфавитах со всеми кодировками по умолчанию, поддерживаемыми PHP . Он не идеален ни с одной натяжкой (я понятия не имею, насколько реалистичны мои фиктивные строки, и кажется, что он задыхается от японского обнаружения), но это несколько полезно для определения кандидатов. Вы можете увидеть это в действии здесь .

После изучения потенциальных кодировок символов, отмеченных этим тестом, кажется, что Windows-1252это идеальный выбор для ваших нужд, охватывающий латинский алфавит, а также акценты для распространенных латинских языков.

Выбор ISO-8859наборов символов должен быть другим жизнеспособным выбором, однако по причинам, которые я не могу обернуть головой, mb_функции, кажется, не различают ISO-8859различные наборы символов, несмотря на перечисление их в качестве отдельных кодировок.

Чтобы разрешить некоторые другие общие символы, вы также можете рассмотреть возможность добавления HTML-ENTITIES.

Bosco
источник
Кажется, что механизм, с помощью которого работают функции mbstring, не способен различать ISO-8859кодировки .
Bosco
Я узнал, что мой связанный тест неточен и вводит в заблуждение - функции mbstring работают исходя из посылки байтовых последовательностей, поэтому, хотя кодирование может использовать байтовые последовательности, которые могут поддерживать перечисленные алфавиты, на самом деле это не означает, что кодирование фактически поддерживает те персонажи. Таким образом, фильтрация алфавитов строк путем тестирования кодировок не является надежным механизмом . Пожалуйста, рассмотрите мой другой ответ вместо.
Bosco
1

Как я пытался объяснить @MichaelRogers, когда он отправил похожий вопрос несколько дней назад, знание набора символов (или сценария), используемого в строке, НЕ достаточно для определения языка этой строки.

Таким образом, хотя метод, описанный @bosco , удалит строки из русского языка и т. Д. (С двумя приведенными ниже исправлениями), он НЕ ограничит ваш поиск английским языком.

Чтобы увидеть это, попробуйте:

$strings = array (
    'I\'m sorry',                   // English
    'Je suis désolé',               // French
    'Es tut mir Leid',              // German
    'Lorem ipsum dolor sit amet',   // Lorem ipsum
    'أنا سعيد',                     // Arabic
    'я счастлив',                   // Russian
    '我很高兴',                     // Chinese (Simplified)
    '我很高興',                     // Chinese (Traditional)
    ) ;
foreach ($strings as $s) {
    if (preg_match ('/^[\p{Latin}\p{Common}]+$/u', $s) === 1) {
        echo "$s: matches latin+common\n" ;
        }
    else {
        echo "$s: does not match latin+common\n" ;
        }
    }

[ примечание: 2 исправления, упомянутые выше к тому, что предоставил @bosco:

  1. шаблон заключен в строку (требуется синтаксически правильный PHP)
  2. добавлен /uмодификатор (требуется для обработки шаблона и субъекта в кодировке UTF-8, см. PHP: Модификаторы шаблонов Regex ]

который будет производить:

I'm sorry: matches latin+common
Je suis désolé: matches latin+common
Es tut mir Leid: matches latin+common
Lorem ipsum dolor sit amet: matches latin+common
أنا سعيد: does not match latin+common
я счастлив: does not match latin+common
我很高兴: does not match latin+common
我很高興: does not match latin+common

[ примечание: я говорю по-английски, по-французски и немного по-немецки (и немного с Lorem ipsum :-), но полагаюсь на Google Translate для арабского, русского и китайского]

Как видите, использование латинского скрипта НЕ гарантирует, что у вас есть английский.

В StackOverflow есть несколько потоков (например, « Определить язык из строки в PHP» ), которые предоставляют дополнительную информацию по этому вопросу.

Пол "Воробей Ястреб" Бирон
источник
Позвольте мне оставить дружескую, педантичную заметку: Lorem ipsum - это не язык, говорить, что кто-то говорит «lorem ipsum», все равно, что говорить, что кто-то говорит «hello world» :) Язык Lorem ipsum - старый латинский , и нет, «lorem» ipsum " не означает " привет мир " :) На самом деле это опечатка для " dolorem ipsum ", что означает " сама боль " или что-то в этом роде.
gmazzap
@ gmazzap Я знаю, это была шутка (отсюда и ":-)"). Я включил Lorem Ipsum , чтобы укрепить точку, проверяя скрипт делает не проверить язык.
Пол «Воробей Ястреб» Бирон
и чтобы быть еще более педантичным, как говорится на lipsum.com , «Lorem Ipsum происходит из разделов 1.10.32 и 1.10.33« De Finibus Bonorum et Malorum »(Крайности добра и зла) Цицерона, написанного в 45 ДО Н.Э." Но он также имеет различные «рандомизации», чтобы сделать его бессмысленным для носителей латинского языка, так что на самом деле это не «старый латинский», а полностью выдуманный «язык».
Пол «Воробей Ястреб» Biron
Ах, хорошо ловит @ Paul'SparrowHawk'Biron! Я обновлю свой ответ, чтобы исправить регулярное выражение и уточнить, что именно делает мое решение.
Bosco
1
Мне все равно, если человек печатает на испанском языке. Это не обязательно должен быть строго английский язык. Я сказал, что символы используются на английском языке, поэтому от А до Я (заглавными буквами и без заглавных букв) + цифры. Если другие языки используют одни и те же символы, тогда я в порядке. Чего я не хочу допустить, так это кириллицы, кандзи, арабских букв (не знаю названия) и всего, что не Aa-Zz + 0-9. Язык не имеет значения.
Майкл Роджерс