検索をラテン文字に限定する


9

検索を英語で使用されている文字+数字に制限したい。その理由は、mysqlログで最も遅いクエリを見ると、ほとんどがアラブ、ロシア、および中国語の文字の検索によるものであるため、それらをスキップして、代わりにエラーメッセージを表示したいためです。


エラーをどのように表示したいか詳細を教えていただければ、回答を修正して含めます
bosco

エラーが検索フォームの下または上にある検索ページに表示されるようにしたい。
マイケルロジャース

回答:


10

このソリューションは、CommonおよびLatin Unicodeスクリプトからの文字のみに一致する正規表現を適用することにより、検索文字列をフィルタリングします。


ラテン文字と正規表現の一致

Stack Overflowで気が狂ったばかりです。結局のところ、正規表現にはUnicodeの「スクリプト」全体を指定する値を含むUnicodeカテゴリ全体を照合するメカニズムがあり、それぞれが異なる書記体系で使用される文字のグループに対応しています。

これは\p、中かっこ内のメタ文字とそれに続くUnicodeカテゴリ識別子を使用して行われます。これにより[\p{Common}\p{Latin}]ラテン文字または共通文字のいずれかの単一の文字に一致します。これには、句読点、数字、その他の記号が含まれます。

@Paul「スパローホーク」ビロンが指摘u パターン修飾子フラグとして対象文字列を治療するためにPHPのPCRE機能のために、正規表現の末尾に設定されるべきであるUTF-8Unicodeでエンコード。

まとめると、パターン

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

LatinおよびCommon Unicodeスクリプトの1つ以上の文字で構成される文字列全体と一致します。


検索文字列のフィルタリング

WordPressがクエリを実行する直前に実行されるアクションpre_get_posts検索文字列をインターセプトするのに適した場所です。でより多くのケア、これも使用して達成することができるフィルタをrequest

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はクエリを検索として処理しなくなります-多くの場合、他の値に応じて、すべての投稿またはサイトのフロントページを表示するアーカイブテンプレートが作成されますクエリ変数。' 'ただし、1つのスペース()に設定すると、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その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-Za-z0-9、など,.!?'"、および(スペース)。


2

編集:このソリューションは推奨されません

以下の私の解決策は、文字列を構成するバイトの配置を調べることにより、魔法のようにアルファベットを神聖なものにするために、PHPのmbstring関数を悪用するハックです。これは本当に悪い考えであり、間違いを起こしがちです。

はるかにシンプルで信頼性の高いソリューションについては、他の回答を参照してください。


非ラテン系のアルファベットを使用した検索を防ぐ1つの方法は、PHPのmb_detect_encoding()関数を使用して、検索文字列が文字エンコーディングのカスタム選択の1つに準拠しているかどうかを確認することです。これを行うのに適した場所pre_get_postsactionです。これは、クエリが実行される直前に実行されるためです。

検索が無効なエンコーディングを使用していると判断した後に実際に行うことは、実際にはアプリケーション固有です。ここでは、検索クエリを1つのスペースに設定して、WordPressがクエリを検索として解釈し、search.phpテンプレートを引き続きロードするようにします(検索文字列が空の文字列)。また、絶対に何も返されないことを確認するために、不可能な投稿IDを持つ配列設定'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が提供したものに対する上記の2つの修正は次のとおりです。

  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翻訳に依存しています]

ご覧のように、ラテン文字のチェックに依存しても、英語を持っているとは限りません

StackOverflowにはいくつかのスレッドがあり(たとえば、PHPの文字列から言語を検出)、主題に関する詳細情報を提供しています。


友好的で教育的なメモを残しておきましょう:Lorem ipsumは言語ではありません。誰かが「lorem ipsum」を話すと言うのは、誰かが「hello world」を話すと言うのと同じです:) Lorem ipsumの言語は古いラテン語で、いいえ、「lorem ipsum」「hello world」を意味するものではありません:)実際には、それは「痛み自体」またはそのようなものを意味するdolorem ipsum」のタイプミスです。
gmazzap

@gmazzap知っています、それは冗談でした( ":-)"です)。私は含まれLoremのイプサムをチェックする点強化するスクリプトがないではない言語をテストします。
Paul 'Sparrow Hawk' Biron

そして、さらに知識を深めるためにlipsum.comで述べているように、「Lorem Ipsumは、45で書かれたCiceroの "de Finibus Bonorum et Malorum"(The Extremes of Good and Evil)」のセクション1.10.32と1.10.33から来ています。紀元前。" しかし、ネイティブラテンスピーカーにとって無意味になるようにさまざまな「ランダム化」も行われているため、実際には「古いラテン語」ではなく、完全に作り上げられた「言語」です。
Paul 'Sparrow Hawk' Biron

ああ、素敵なキャッチ@ Paul'SparrowHawk'Biron!正規表現を修正するために私の答えを更新し、私のソリューションが正確に何をするかを明確にします。
bosco 2017年

1
その人がスペイン語でタイプするかどうかは気にしない。厳密に英語である必要はありません。私は英語で使用されている文字を言ったので、AからZまで(大文字と小文字なし)+数字。他の言語で偶然同じ文字が使用されている場合は、私が問題ありません。私が許可したくないのは、キリル文字、漢字、アラビア文字(名前がわからない)、およびAa-Zz + 0-9以外のものです。言語は関係ありません。
Michael Rogers
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.