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


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.