จำกัด การค้นหาเฉพาะอักขระละติน


9

ฉันต้องการ จำกัด การค้นหาเฉพาะอักขระที่ใช้กับภาษาอังกฤษ + ตัวเลข เหตุผลก็คือการดูข้อความค้นหาที่ช้าที่สุดในบันทึก mysql ที่ฉันพบว่าส่วนใหญ่มาจากการค้นหาในตัวอักษรอาหรับรัสเซียและจีนดังนั้นฉันจึงต้องการข้ามมันและแสดงข้อความแสดงข้อผิดพลาดแทน


หากคุณให้รายละเอียดว่าคุณต้องการแสดงข้อผิดพลาดของคุณอย่างไรฉันจะแก้ไขคำตอบของฉันให้รวมไว้ด้วย
bosco

ฉันต้องการให้ข้อผิดพลาดปรากฏขึ้นในหน้าค้นหาด้านล่างหรือด้านบนของแบบฟอร์มการค้นหา
Michael Rogers

คำตอบ:


10

โซลูชันนี้กรองสตริงการค้นหาโดยใช้นิพจน์ทั่วไปซึ่งจับคู่อักขระจากสคริปต์ Common และ Latin Unicode เท่านั้น


การจับคู่อักขระละตินที่มีการแสดงออกปกติ

ฉันเพิ่งได้ใจของฉันปลิวไปที่กองมากเกิน ตามที่ปรากฎนิพจน์ทั่วไปจะมีกลไกเพื่อจับคู่หมวดหมู่ Unicode ทั้งหมดรวมถึงค่าเพื่อระบุ"สคริปต์" Unicodeทั้งหมดซึ่งแต่ละตัวสอดคล้องกับกลุ่มของอักขระที่ใช้ในระบบการเขียนที่แตกต่างกัน

สิ่งนี้ทำได้โดยใช้\pเมตาอักขระตามด้วยตัวระบุหมวดหมู่ Unicode ในเครื่องหมายปีกกา - [\p{Common}\p{Latin}]จับคู่อักขระเดี่ยวในสคริปต์ละตินหรือสคริปต์ทั่วไป - ซึ่งรวมถึงเครื่องหมายวรรคตอนตัวเลขและสัญลักษณ์อื่น ๆ

ในฐานะที่เป็นชี้ @ Paul 'นกกระจอกเหยี่ยว' Biron ออกที่u ธงปรับปรุงรูปแบบควรจะตั้งในตอนท้ายของการแสดงออกปกติในการสั่งซื้อสำหรับ PHP ของฟังก์ชั่น PCRE ในการรักษาสตริงเรื่องเป็นUTF-8Unicode เข้ารหัส

มารวมกันแล้วลวดลาย

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

จะจับคู่สตริงทั้งหมดที่ประกอบด้วยอักขระหนึ่งตัวหรือมากกว่าในสคริปต์ Latin และ Common Unicode


การกรองสตริงการค้นหา

สถานที่ที่ดีในการดักจับสตริงการค้นหาคือการpre_get_postsกระทำที่มันจะดำเนินการทันทีก่อนที่ WordPress จะดำเนินการค้นหา ด้วยการดูแลมากขึ้นนี้ยังสามารถทำได้โดยใช้กรอง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()เพื่อปรับเปลี่ยนแบบสอบถามโดยการเปลี่ยนชื่อแบบสอบถามเป็น vars - ดังนั้นจึงมีผลต่อแบบสอบถาม SQL WordPress ประกอบและประมวลผล

ตัวแปรข้อความค้นหาที่เกี่ยวข้องมากที่สุดน่าจะเป็นดังนี้:

  • sเป็นตัวแปรแบบสอบถามที่สอดคล้องกับสตริงการค้นหา การตั้งค่าให้nullเป็นหรือสตริงว่างเปล่า ( '') จะส่งผลให้ WordPress ไม่ต้องรักษาข้อความค้นหาอีกต่อไป - บ่อยครั้งที่ผลลัพธ์นี้ในเทมเพลตเก็บถาวรที่แสดงโพสต์ทั้งหมดหรือหน้าแรกของเว็บไซต์ขึ้นอยู่กับค่าของอีก vars แบบสอบถาม อย่างไรก็ตามการตั้งค่าเป็นช่องว่างเดียว ( ' ') จะส่งผลให้ 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_formaction hook เพื่อแสดงข้อผิดพลาดของคุณเหนือแบบฟอร์มการค้นหา:

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 ซึ่งสามารถแสดงข้อความ "flash" หรือ "modal" (หรือคุณเพิ่มความสามารถดังกล่าวด้วยตัวคุณเอง) ให้เพิ่มตรรกะในการแสดงข้อความบนหน้าโหลดเมื่อตั้งค่าตัวแปรเฉพาะจากนั้นเพิ่มwp_enqueue_scripthook ที่มี$priorityขนาดใหญ่กว่าที่ใช้จาวาสคริปต์นั้นและใช้wp_localize_script()เพื่อตั้งค่าตัวแปรให้รวมข้อความแสดงข้อผิดพลาดของคุณ
  • ใช้wp_redirect()เพื่อส่งผู้ใช้ไปยัง URL ที่คุณเลือก (วิธีนี้ต้องการการโหลดหน้าเว็บเพิ่มเติม)
  • ตั้งค่าตัวแปร PHP หรือเรียกใช้เมธอดซึ่งจะแจ้งธีม / ปลั๊กอินของคุณเกี่ยวกับข้อผิดพลาดเพื่อให้มันแสดงผลตามความเหมาะสม
  • ตั้งค่าsตัวแปรคิวรีเป็น''แทนที่' 'และใช้page_idแทนpost__inเพื่อส่งคืนเพจที่คุณเลือก
  • ใช้loop_startเบ็ดเพื่อฉีดWP_Postวัตถุปลอมที่มีข้อผิดพลาดของคุณลงในผลลัพธ์การค้นหา - นี่เป็นแฮ็คที่น่าเกลียดที่สุดและอาจดูไม่เหมาะสมกับธีมของคุณ แต่มีผลข้างเคียงที่อาจเกิดขึ้นจากการปราบปรามข้อความ
  • ใช้template_includehook ตัวกรองเพื่อแลกเปลี่ยนเทมเพลตการค้นหากับแบบกำหนดเองในชุดรูปแบบหรือปลั๊กอินที่แสดงข้อผิดพลาดของคุณ

หากไม่มีการตรวจสอบชุดรูปแบบที่เป็นปัญหามันเป็นการยากที่จะกำหนดเส้นทางที่คุณควรใช้


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

แก้ไข: ไม่แนะนำวิธีนี้

วิธีการแก้ปัญหาด้านล่างของฉันคือแฮ็คที่ใช้ฟังก์ชันmbstringของ PHP ในทางที่ผิดเพื่อพยายามทำนายตัวอักษรอย่างน่าอัศจรรย์โดยดูจากการจัดเรียงของไบต์ที่เขียนสตริง นี่เป็นความคิดที่ไม่ดีจริงๆและมีแนวโน้มที่จะเกิดข้อผิดพลาดสูง

โปรดดูคำตอบอื่น ๆของฉันสำหรับวิธีแก้ปัญหาที่ง่ายกว่าและเชื่อถือได้มากกว่า


วิธีหนึ่งในการป้องกันการค้นหาโดยใช้ตัวอักษรที่ไม่ใช่ละตินคือการใช้ฟังก์ชั่นของ PHPmb_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" ;
        }
    }

[ หมายเหตุ:การแก้ไข 2 รายการที่กล่าวถึงข้างต้นเกี่ยวกับสิ่งที่ @bosco มีให้:

  1. รูปแบบถูกล้อมรอบสตริง (จำเป็นต้องมีการแก้ไข PHP ให้ถูกต้องตามหลักไวยากรณ์)
  2. เพิ่มโมดิ/uฟายเออร์ (จำเป็นสำหรับการรักษารูปแบบและหัวเรื่องตามที่เข้ารหัส UTF-8, ดูPHP: Regex Pattern Modifiers ]

ซึ่งจะผลิต:

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 ipsumเพื่อเสริมจุดที่ตรวจสอบสคริปต์ไม่ได้ทดสอบภาษา
พอล 'สแปร์โรว์ฮอว์ก' Biron

และจะยิ่งอวดรู้มากขึ้นตามที่ระบุไว้ในlipsum.com "Lorem Ipsum มาจากส่วน 1.10.32 และ 1.10.33 ของ" de Finibus Bonorum et Malorum "(The Extremes of Good and Evil) โดย Cicero เขียนใน 45 BC." แต่มันก็มี "การสุ่ม" ต่าง ๆ เพื่อทำให้มันไร้สาระสำหรับเจ้าของภาษาละตินดังนั้นมันจึงไม่ใช่ "ละตินเก่า" แต่เป็นภาษาที่สร้างขึ้นอย่างสมบูรณ์
พอล 'สแปร์โรว์ฮอว์ก' บิโร

Ah, จับที่ดี @ Paul'SparrowHawk'Biron! ฉันจะอัปเดตคำตอบของฉันเพื่อแก้ไขนิพจน์ทั่วไปและชี้แจงว่าโซลูชันของฉันทำอะไร
bosco

1
ฉันไม่สนใจว่าบุคคลนั้นพิมพ์เป็นภาษาสเปนหรือไม่ ไม่จำเป็นต้องเป็นภาษาอังกฤษอย่างเคร่งครัด ฉันบอกว่าตัวละครที่ใช้กับภาษาอังกฤษดังนั้นตั้งแต่ A ถึง Z (เป็นตัวพิมพ์ใหญ่และไม่มีตัวพิมพ์ใหญ่) + ตัวเลข หากภาษาอื่นมีการใช้ตัวอักษรเดียวกันฉันก็สบายดี สิ่งที่ฉันไม่ต้องการคือ Cyrillic, kanji, ตัวอักษรอาหรับ (ไม่รู้ชื่อ) และทุกอย่างที่ไม่ใช่ Aa-Zz + 0-9 ภาษาไม่สำคัญ
Michael Rogers
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.