Batasi pencarian hingga karakter latin

9

Saya ingin membatasi pencarian untuk karakter yang digunakan pada angka + bahasa Inggris. Alasannya adalah karena melihat kueri paling lambat pada log mysql saya menemukan sebagian besar berasal dari pencarian dalam karakter Arab, Rusia dan Cina, jadi saya ingin melewatkannya dan menampilkan pesan kesalahan sebagai gantinya.

Michael Rogers
sumber
Jika Anda merinci bagaimana Anda ingin menampilkan kesalahan Anda, saya akan mengubah jawaban saya untuk memasukkannya
bosco
Saya ingin kesalahan muncul di halaman pencarian, di bawah atau di atas form pencarian.
Michael Rogers

Jawaban:

10

Solusi ini memfilter string pencarian dengan menerapkan ekspresi reguler yang hanya cocok dengan karakter dari skrip Unicode Umum dan Latin.


Mencocokkan Karakter Latin dengan Ekspresi Reguler

Saya hanya telah pikiranku tumbang di Stack Overflow . Ternyata, ekspresi reguler memiliki mekanisme untuk mencocokkan seluruh kategori Unicode, termasuk nilai untuk menentukan seluruh "skrip" Unicode , masing-masing sesuai dengan kelompok karakter yang digunakan dalam sistem penulisan yang berbeda.

Ini dilakukan dengan menggunakan \pmeta-karakter diikuti oleh pengenal kategori Unicode dalam kurung kurawal - sehingga [\p{Common}\p{Latin}]cocok dengan satu karakter dalam skrip Latin atau Umum - ini termasuk tanda baca, angka, dan simbol lain-lain.

Seperti yang ditunjukkan oleh @Paul 'Sparrow Hawk' Biron , u bendera pengubah pola harus ditetapkan di akhir ekspresi reguler agar fungsi-fungsi PCRE PHP memperlakukan string subjek sebagai UTF-8dikodekan oleh Unicode.

Secara keseluruhan, polanya

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

akan cocok dengan seluruh string yang terdiri dari satu atau lebih karakter dalam skrip Latin dan Common Unicode.


Memfilter String Pencarian

Tempat yang baik untuk mencegat string pencarian yang pre_get_poststindakan seperti kebakaran segera sebelum WordPress mengeksekusi query. Dengan lebih banyak perawatan , ini juga bisa dicapai dengan menggunakan sebuah requestsaringan .

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' );

Menanggapi Penelusuran yang Dilarang

Setelah ditentukan bahwa string pencarian berisi karakter non-Latin, Anda dapat menggunakan WP_Query::set()untuk mengubah kueri dengan mengubah itu bernama vars permintaan - dengan demikian mempengaruhi permintaan SQL WordPress kemudian menyusun dan mengeksekusi.

Variabel kueri yang paling relevan mungkin adalah yang berikut:

  • sadalah variabel kueri yang terkait dengan string pencarian. Menyetelnya ke nullatau string kosong ( '') akan mengakibatkan WordPress tidak lagi memperlakukan kueri sebagai pencarian - sering kali ini menghasilkan template arsip yang menampilkan semua posting atau halaman depan situs, tergantung pada nilai yang lain permintaan vars. Menetapkannya ke satu ruang ( ' '), akan menghasilkan WordPress mengenalinya sebagai pencarian, dan dengan demikian berusaha untuk menampilkan search.phptemplat.
  • page_id dapat digunakan untuk mengarahkan pengguna ke halaman tertentu pilihan Anda.
  • post__indapat membatasi kueri ke pilihan posting tertentu. Dengan mengaturnya ke array dengan ID posting yang tidak mungkin, itu dapat berfungsi sebagai ukuran untuk memastikan bahwa permintaan tidak menghasilkan apa-apa .

Di atas dalam pikiran, Anda dapat melakukan hal berikut untuk menanggapi pencarian yang buruk dengan memuat search.phptemplate tanpa hasil:

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' );

Menampilkan Kesalahan

Cara Anda benar-benar menampilkan pesan kesalahan sangat tergantung pada aplikasi Anda dan kemampuan tema Anda - ada banyak cara yang bisa dilakukan. Jika tema Anda memanggil get_search_form()templat pencariannya, solusi termudah mungkin adalah menggunakan hook pre_get_search_formtindakan untuk menampilkan kesalahan Anda tepat di atas form pencarian:

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>';
}

Beberapa kemungkinan lain untuk menampilkan pesan kesalahan meliputi:

  • Jika situs Anda menggunakan JavaScript yang dapat menampilkan pesan "flash" atau "modal" (atau Anda menambahkan kemampuan seperti itu sendiri), tambahkan padanya logika untuk menampilkan pesan pada pemuatan halaman ketika variabel tertentu diatur, lalu tambahkan wp_enqueue_scriptkait dengan yang $prioritylebih besar daripada yang membuat JavaScript itu, dan gunakan wp_localize_script()untuk mengatur variabel itu untuk memasukkan pesan kesalahan Anda.
  • Gunakan wp_redirect()untuk mengirim pengguna ke URL pilihan Anda (metode ini membutuhkan pemuatan halaman tambahan).
  • Tetapkan variabel PHP atau aktifkan metode yang akan menginformasikan tema / plugin Anda tentang kesalahan sehingga dapat menampilkannya jika perlu.
  • Tetapkan svariabel kueri ''sebagai ganti ' 'dan gunakan page_idsebagai pengganti post__inuntuk mengembalikan halaman yang Anda pilih.
  • Gunakan loop_startpengait untuk menyuntikkan WP_Postobjek palsu yang mengandung kesalahan Anda ke dalam hasil kueri - ini jelas merupakan peretasan yang buruk dan mungkin tidak cocok dengan tema khusus Anda, tetapi memiliki efek samping yang berpotensi diinginkan dengan menekan pesan "Tanpa Hasil".
  • Gunakan template_includekait filter untuk menukar templat pencarian dengan yang kustom di tema atau plugin Anda yang menampilkan kesalahan Anda.

Tanpa memeriksa tema yang dimaksud, sulit untuk menentukan rute mana yang harus Anda ambil.

bosco
sumber
2

Anda akan melakukan ini dengan memasukkan fungsi validasi dalam PHP untuk menguji input terhadap ekspresi reguler seperti ^[a-zA-Z0-9,.!?' ]*

Jadi akan terlihat seperti ini:

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

The RexEx Aku digunakan untuk semua karakter A-Z, a-z, 0-9, serta ,, ., !, ?, ', ", dan (ruang).

Cedon
sumber
2

EDIT: Solusi ini tidak disarankan

Solusi saya di bawah ini adalah hack yang menyalahgunakan fungsi mbstring PHP dalam upaya untuk membuat huruf ilahi secara ajaib dengan melihat pengaturan byte yang menyusun string. Ini adalah ide yang sangat buruk dan sangat rentan terhadap kesalahan .

Silakan lihat jawaban saya yang lain untuk solusi yang jauh lebih sederhana dan jauh lebih dapat diandalkan.


Salah satu cara untuk mencegah pencarian menggunakan huruf non-Latin adalah dengan menggunakan fungsi PHPmb_detect_encoding() untuk melihat apakah string pencarian sesuai dengan salah satu pilihan kustom pengkodean karakter. Tempat yang baik untuk melakukan ini adalah dengan pre_get_poststindakan , seperti kebakaran tepat sebelum query dijalankan.

Apa yang sebenarnya Anda lakukan setelah Anda menentukan pencarian menggunakan pengkodean yang tidak valid adalah benar-benar spesifik aplikasi. Di sini saya telah menetapkan permintaan pencarian ke satu ruang untuk memastikan bahwa WordPress masih menginterpretasikan permintaan sebagai pencarian, dan dengan demikian masih memuat search.phptemplate (dan tidak mengarahkan pengguna ke halaman depan, seperti yang terjadi ketika string pencarian adalah string kosong). Saya juga mengambil tindakan pencegahan tambahan pengaturan 'post__in'ke array dengan ID posting mustahil untuk memastikan bahwa benar-benar tidak ada yang dikembalikan .

Sebagai alternatif, Anda dapat mempertimbangkan mengatur string pencarian nulldan pengaturan page_iduntuk mengarahkan pengguna ke halaman dengan pesan kesalahan khusus Anda.

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' );

Memilih Penyandian

Saya menulis tes cakupan membandingkan beberapa string dummy dalam huruf yang berbeda terhadap semua penyandian default yang didukung oleh PHP . Itu tidak sempurna oleh peregangan apa pun (saya tidak tahu seberapa realistis string dummy saya, dan tampaknya menghambat deteksi Jepang), tetapi agak berguna untuk menentukan kandidat. Anda dapat melihatnya beraksi di sini .

Setelah meneliti potensi penyandian karakter yang ditandai oleh tes itu, sepertinya Windows-1252ini adalah pilihan yang sempurna untuk kebutuhan Anda, meliputi alfabet Latin dan aksen untuk bahasa Latin umum.

Pilihan ISO-8859set karakter harus menjadi pilihan lain yang layak, namun karena alasan saya tidak dapat membungkus kepala saya, mb_fungsi - fungsi tersebut sepertinya tidak membedakan antara ISO-8859set karakter yang berbeda, meskipun mendaftarkannya sebagai pengkodean terpisah.

Untuk mengizinkan beberapa karakter umum lainnya, Anda juga dapat mempertimbangkan untuk menambahkan HTML-ENTITIES.

bosco
sumber
Tampaknya mekanisme yang berfungsi fungsi mbstring tidak mampu membedakan antara ISO-8859pengkodean .
bosco
Saya telah belajar bahwa tes tertaut saya tidak akurat dan menyesatkan - fungsi mbstring bekerja dengan premis urutan byte, jadi sementara pengodean dapat menggunakan urutan byte yang dapat mendukung huruf yang tercantum, itu tidak berarti bahwa pengkodean sebenarnya mendukung karakter. Jadi, memfilter huruf string dengan menguji penyandian bukanlah mekanisme yang andal . Silakan pertimbangkan jawaban saya yang lain sebagai gantinya.
bosco
1

Ketika saya mencoba menjelaskan kepada @MichaelRogers ketika dia memposting pertanyaan serupa beberapa hari yang lalu, mengetahui set karakter (atau skrip) yang digunakan dalam string TIDAK cukup untuk mendeteksi bahasa string itu.

Jadi, sementara metode yang dirinci oleh @bosco akan menghapus string Rusia, dll. (Dengan 2 koreksi di bawah), TIDAK akan membatasi pencarian Anda ke bahasa Inggris.

Untuk melihat ini, coba:

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

[ catatan: 2 koreksi yang disebutkan di atas untuk apa yang disediakan @bosco adalah:

  1. polanya dilampirkan string (diperlukan untuk PHP yang benar secara sintaksis)
  2. menambahkan /upengubah (diperlukan untuk memperlakukan pola dan subjek sebagai pengkodean UTF-8, lihat PHP: Pengubah Pola Regex ]

yang akan menghasilkan:

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

[ catatan: Saya berbicara bahasa Inggris, Prancis & beberapa Jerman (dan sedikit Lorem ipsum :-), tetapi mengandalkan Google Translate untuk bahasa Arab, Rusia, dan Cina]

Seperti yang Anda lihat, mengandalkan memeriksa skrip latin TIDAK akan memastikan Anda memiliki bahasa Inggris.

Ada sejumlah utas di StackOverflow (misalnya, Deteksi bahasa dari string di PHP ) yang memberikan lebih banyak informasi tentang subjek.

Paul 'Sparrow Hawk' Biron
sumber
Biarkan saya meninggalkan catatan ramah, bertele-tele: Lorem ipsum bukan bahasa, untuk mengatakan seseorang berbicara "lorem ipsum" adalah seperti mengatakan bahwa seseorang berbicara "halo dunia" :) Bahasa Lorem ipsum adalah bahasa latin yang lama , dan tidak, "lorem ipsum " tidak berarti " halo dunia " :) Sebenarnya ini adalah kesalahan ketik untuk " dolorem ipsum " yang berarti " sakit itu sendiri " atau sesuatu seperti itu.
gmazzap
@ gmazzap Saya tahu, itu lelucon (karenanya ":-)"). Saya termasuk lorem ipsum untuk memperkuat titik yang memeriksa naskah tidak tidak menguji bahasa.
Paul 'Sparrow Hawk' Biron
dan menjadi lebih bertele-tele, seperti dikatakan di lipsum.com , "Lorem Ipsum berasal dari bagian 1.10.32 dan 1.10.33 dari" de Finibus Bonorum et Malorum "(The Extremes of Good and Evil) oleh Cicero, ditulis dalam 45 SM. " Tetapi juga memiliki berbagai "pengacakan" untuk membuatnya tidak masuk akal bagi penutur latin asli, jadi itu sebenarnya bukan "latin tua", tetapi "bahasa" yang sepenuhnya dibuat-buat.
Paul 'Sparrow Hawk' Biron
Ah, hasil tangkapan yang bagus @ Paul'SparrowHawk'Biron! Saya akan memperbarui jawaban saya untuk memperbaiki ekspresi reguler dan mengklarifikasi apa sebenarnya solusi saya.
bosco
1
Saya tidak peduli jika orang itu mengetik dalam bahasa Spanyol. Tidak harus hanya bahasa Inggris. Saya mengatakan karakter yang digunakan pada bahasa Inggris jadi Dari A ke Z (dalam huruf besar dan tanpa huruf kecil) + angka. Jika bahasa lain kebetulan menggunakan karakter yang sama maka baik-baik saja oleh saya. Yang tidak ingin saya izinkan adalah huruf Sirilik, kanji, Arab (tidak tahu nama), dan apa pun yang bukan Aa-Zz + 0-9. Bahasa tidak masalah.
Michael Rogers