라틴 문자로 검색 제한


9

영어 + 숫자에 사용되는 문자로 검색을 제한하고 싶습니다. 그 이유는 mysql 로그에서 가장 느린 쿼리를 보면서 아랍, 러시아어 및 중국어 문자로 검색 한 결과 가장 빠르기 때문에 건너 뛰고 대신 오류 메시지를 표시하고 싶습니다.


오류를 표시하는 방법을 자세히 설명하면 오류를 포함하도록 답변을 수정하겠습니다.
bosco

검색 페이지의 검색 양식 아래 또는 위에 오류가 표시되기를 원합니다.
Michael Rogers

답변:


10

이 솔루션은 공통 및 라틴 유니 코드 스크립트의 문자 만 일치하는 정규식을 적용하여 검색 문자열을 필터링합니다.


정규식과 라틴 문자 일치

방금 스택 오버플로에서 마음이 터졌습니다 . 알 수 있듯이 정규 표현식에는 유니 코드 "스크립트" 전체를 지정하는 값을 포함하여 전체 유니 코드 범주와 일치 하는 메커니즘 이 있습니다 .유니 코드 "스크립트" 는 서로 다른 쓰기 시스템에서 사용되는 문자 그룹에 해당합니다.

\p메타 문자 다음에 중괄호로 묶은 유니 코드 범주 식별자를 사용하여 수행됩니다. 따라서 라틴어 또는 공통 스크립트[\p{Common}\p{Latin}] 의 단일 문자와 일치합니다. 여기에는 구두점, 숫자 및 기타 기호가 포함됩니다.

따라 @ 폴 '참새 호크'곤 지적u 패턴 수정 플래그 로 주제 문자열을 처리하는 PHP의 PCRE 기능을 위해서는 정규 표현식의 끝에서 설정해야합니다 UTF-8유니 코드 인코딩.

모두 함께, 패턴

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

라틴 및 일반 유니 코드 스크립트에서 하나 이상의 문자로 구성된 전체 문자열과 일치합니다.


검색 문자열 필터링

검색 문자열을 차단하는 좋은 장소입니다 행동 은 워드 프레스 쿼리를 실행하기 직전에 발사한다. 으로 더주의 , 이것은 또한 사용하여 수행 할 수 필터를 .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()이름을 query vars 로 변경하여 쿼리를 수정하는 데 사용할 수 있습니다 . 따라서 SQL 쿼리에 영향을 미칩니다. WordPress는 그 후 작성하고 실행합니다.

가장 관련성이 높은 쿼리 변수는 다음과 같습니다.

  • s검색 문자열에 해당하는 쿼리 변수입니다. 이 값을 설정 null하거나 빈 문자열 ( '')로 설정 하면 WordPress에서 더 이상 쿼리를 검색으로 처리하지 않습니다. 종종이 값은 다른 게시물의 값에 따라 모든 게시물 또는 사이트의 첫 페이지를 표시하는 아카이브 템플릿이됩니다. 검색어 변수 ' '그러나 단일 공백 ​​( )으로 설정하면 WordPress에서 검색으로 인식하여 search.php템플릿 을 표시하려고합니다 .
  • page_id 사용자를 선택한 특정 페이지로 안내하는 데 사용될 수 있습니다.
  • post__in검색어를 특정 게시물로 제한 할 수 있습니다. 불가능한 게시물 ID를 가진 배열로 설정 하면 쿼리가 절대적으로 아무것도 반환하지 않도록 측정하는 역할을 할 수 있습니다 .

위의 사항을 염두에두고 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큰 것보다하는 대기열에 자바 스크립트 및 사용은 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

편집 :이 솔루션은 권장하지 않습니다

아래의 내 솔루션 은 문자열을 구성하는 바이트 배열을보고 마법의 신성한 알파벳을 찾으려고 PHP의 mbstring 함수 를 악용하는 해킹입니다 . 이것은 정말 나쁜 생각이며 오류가 발생하기 쉽습니다. .

훨씬 간단하고 훨씬 안정적인 솔루션에 대한 다른 답변 을 참조하십시오 .


비 라틴 문자를 사용하는 검색을 방지하는 한 가지 방법은 PHP의 mb_detect_encoding()기능 을 사용 하여 검색 문자열이 사용자 정의 된 문자 인코딩 중 하나를 따르는 지 확인하는 것입니다. 이것을하기에 좋은 곳 pre_get_posts 작업 action 입니다. 쿼리가 실행되기 직전에 실행됩니다.

검색에서 유효하지 않은 인코딩을 사용하고 있다고 판단한 후 실제로 수행하는 작업은 실제로 응용 프로그램마다 다릅니다. 여기에서는 WordPress가 여전히 쿼리를 검색으로 해석하여 search.php템플릿을 로드하도록 검색 쿼리를 단일 공간으로 설정했습니다 (검색 문자열이 빈 문자열). 또한 설정에 대한 추가 예방 조치를 취 합니다.'post__in' 아무 것도 반환되지 않도록하기 위해 포스트 ID가 불가능한 배열로 .

또는, 당신은에 검색 문자열을 설정하는 것이 좋습니다 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가 며칠 전에 비슷한 질문을 게시했을 때 @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가 제공 한 내용에 대해 위에서 언급 한 2 가지 수정 사항은 다음과 같습니다.

  1. 패턴은 문자열로 묶여 있습니다 (구문 적으로 올바른 PHP 여야 함)
  2. /u수정 자 추가 (패턴 및 주제를 UTF-8 인코딩으로 처리해야 함, PHP : 정규식 패턴 수정 자 참조 )

그것은 생산할 것이다 :

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 ""hello world "를 의미하지 않습니다. :) 실제로 "dolorem ipsum " 의 오타 는 "고통 자체 " 또는 이와 유사한 것을 의미합니다.
gmazzap

@gmazzap 내가 아는 것은 농담이었다 (따라서 ":-)"). 스크립트 를 검사 해도 언어가 테스트 되지 않는다는 점을 강조하기 위해 lorem ipsum 을 포함 시켰습니다 .
폴 '스패로우 호크'비론

이에 말한대로하고, 더 현학적으로 lipsum.com , "로렘 입숨 섹션 1.10.32 및 1.10.33에서 온다"드 Finibus Bonorum 등 Malorum "(45)에 기록 된 시세로 (선악의 극단) 기원전." 그러나 그것은 또한 라틴어 원어민에게 무의미하게 만들기 위해 다양한 "무작위"를 가지고 있기 때문에 실제로는 "오래된 라틴어"가 아니라 완전히 구성된 "언어"입니다.
폴 '스패로우 호크'비론

아, @ Paul'SparrowHawk'Biron을 잡습니다! 정규식을 수정하고 솔루션이 정확히 무엇을하는지 명확하게하기 위해 답변을 업데이트합니다.
bosco

1
그 사람이 스페인어로 입력해도 상관 없습니다. 반드시 영어 일 필요는 없습니다. 나는 영어에서 사용되는 문자를 말 했으므로 A에서 Z까지 (대문자와 대문자 없음) + 숫자를 사용했습니다. 다른 언어가 동일한 문자를 사용하는 경우 나에게 좋습니다. 내가 허용하고 싶지 않은 것은 키릴 자모, 한자, 아랍어 문자 (이름을 모름) 및 Aa-Zz + 0-9가 아닌 것입니다. 언어는 중요하지 않습니다.
Michael Rogers
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.