라틴 문자로 검색 제한


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.