Обмежити пошук на латинські символи


9

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


Якщо ви докладно описуєте, як ви хочете відобразити свою помилку, я модифікую свою відповідь, щоб включити її
bosco

Я хотів би, щоб помилка відображалася на сторінці пошуку, нижче або над формою пошуку.
Майкл Роджерс

Відповіді:


10

Це рішення фільтрує рядки пошуку, застосовуючи регулярний вираз, який відповідає лише символам із загального та латинського сценаріїв Unicode.


Збіг латинських символів з регулярними виразами

Мені просто спалахнув розум під час переповнення стека . Як виявляється, регулярні вирази мають механізм узгодження цілих категорій Unicode, включаючи значення, щоб вказати цілі "сценарії" Unicode , кожен відповідає групам символів, що використовуються в різних системах запису.

Це робиться за допомогою \pмета-символу, за яким слідує ідентифікатор категорії Unicode у фігурних дужках - таким чином, [\p{Common}\p{Latin}]відповідає одному символу в латинських або загальних сценаріях - це включає розділові знаки, цифри та інші символи.

Як зазначає @Paul 'Sparrow Hawk' Biron , u прапор модифікатора шаблону повинен бути встановлений в кінці регулярного виразу, щоб функції PCRE 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()для зміни запиту, змінивши його на ім'я vars vars - таким чином впливаючи на SQL-запит, WordPress згодом створює та виконує.

Напевно, найбільш відповідні змінні запиту:

  • s- змінна запиту, що відповідає пошуковому рядку. Якщо встановити його nullабо порожній рядок ( ''), це призведе до того, що WordPress більше не розглядає запит як пошук - часто це призводить до того, що в шаблоні архіву відображаються всі повідомлення або титульна сторінка сайту, залежно від значень іншого запит vars. Якщо встановити його в єдиному просторі ( ' '), це призведе до того, що 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гачок фільтра, щоб поміняти шаблон пошуку на спеціальний у вашій темі чи плагіні, який відображає вашу помилку.

Без вивчення теми, про яку йдеться, важко визначити, яким маршрутом слід скористатися.


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, а також ,, ., !, ?, ', ", і (пропуск).


2

EDIT: Це рішення не рекомендується

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

Будь ласка, дивіться мою іншу відповідь щодо куди простішого та надійнішого рішення.


Одним із способів запобігання пошуку за допомогою нелатинських алфавітів є використання функції PHP,mb_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.


Здається, механізм, за допомогою якого функціонують функції mbstring, не в змозі розмежовувати ISO-8859кодування .
bosco

Я дізнався, що мій зв'язаний тест є неточним та оманливим - функції mbstring працюють з передумовою послідовностей байтів, тому, хоча кодування може використовувати послідовності байтів, які можуть підтримувати перелічені алфавіти, це насправді не означає, що кодування насправді підтримує ці символів. Таким чином, фільтрація алфавітів рядків за допомогою тестування кодувань не є надійним механізмом . Розгляньте замість мене іншу відповідь.
bosco

1

Як я намагався пояснити @MichaelRogers, коли він розміщував подібне запитання кілька днів тому, знаючи, що набір символів (або сценарій), використовуваний у рядку, НЕ є достатнім для виявлення мови цього рядка.

Таким чином, хоча метод, описаний @bosco , видалить рядки російської мови тощо (з 2 виправленнями нижче), він НЕ обмежує ваші пошуки англійською мовою.

Щоб побачити це, спробуйте:

$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" ;
        }
    }

[ зауважте: два виправлення, згадані вище до тих, що надано @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" - це як сказати, що хтось говорить "привіт світ" :) Мова Lorem ipsum - стара латинська , і ні, "lorem ipsum " не означає " привіт світ " :) Насправді це друкарська помилка " dolorem ipsum ", що означає " біль себе " чи щось подібне.
gmazzap

@gmazzap Я знаю, це був жарт (звідси ":-)"). Я включив Lorem Ipsum , щоб зміцнити точку, перевіряючи скрипт робить НЕ перевірити мову.
Пол 'Горобець Яструб' Бірон

і щоб бути ще більш педантичним, як це говориться на lipum.com , "Lorem Ipsum походить з розділів 1.10.32 та 1.10.33" de Finibus Bonorum et Malorum "(Крайності добра і зла) Цицерона, написаного в 45 До н.е. ». Але він також має різні "рандомізації", щоб зробити його безглуздим для носія мови латині, тому це насправді не "стара латинська", а повністю складена "мова".
Пол 'Горобець Яструб' Бірон

Ах, приємно ловить @ Paul'SparrowHawk'Biron! Я оновлю свою відповідь, щоб виправити регулярний вираз і уточнити, що саме робить моє рішення.
bosco

1
Мені байдуже, чи людина набирає іспанську мову. Це не потрібно бути суто англійською мовою. Я сказав, що символи, які використовуються на англійській мові, так що від A до Z (з великої літери та без літер) + цифри. Якщо інші мови використовують ті самі символи, то мені добре. Що я не хочу дозволити - це кирилиця, кандзі, арабські букви (не знаю назви), і все, що не Aa-Zz + 0-9. Мова не має значення.
Майкл Роджерс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.