Giới hạn tìm kiếm cho các ký tự Latin


9

Tôi muốn giới hạn tìm kiếm các ký tự được sử dụng trên các ngôn ngữ + số tiếng Anh. Lý do là vì nhìn vào các truy vấn chậm nhất trên nhật ký mysql tôi thấy hầu hết đến từ các tìm kiếm bằng các ký tự Ả Rập, Nga và Trung Quốc, vì vậy tôi muốn bỏ qua chúng và thay vào đó hiển thị thông báo lỗi.


Nếu bạn nêu chi tiết cách bạn muốn hiển thị lỗi của mình, tôi sẽ sửa đổi câu trả lời của mình để đưa nó
bosco

Tôi muốn lỗi xuất hiện trong trang tìm kiếm, bên dưới hoặc bên trên mẫu tìm kiếm.
Michael Rogers

Câu trả lời:


10

Giải pháp này lọc các chuỗi tìm kiếm bằng cách áp dụng một biểu thức chính quy chỉ khớp với các ký tự từ các tập lệnh Unicode thông dụng và Latin.


Kết hợp các ký tự Latin với các biểu thức chính quy

Tôi vừa mới suy nghĩ về Stack Overflow . Hóa ra, các biểu thức chính quy có một cơ chế khớp với toàn bộ các loại Unicode, bao gồm các giá trị để chỉ định toàn bộ "tập lệnh" Unicode , mỗi tập lệnh tương ứng với các nhóm ký tự được sử dụng trong các hệ thống chữ viết khác nhau.

Điều này được thực hiện bằng cách sử dụng \pký tự meta theo sau là mã định danh Unicode trong các dấu ngoặc nhọn - do đó [\p{Common}\p{Latin}]khớp với một ký tự trong các tập lệnh Latin hoặc Common - bao gồm dấu chấm câu, chữ số và ký hiệu linh tinh.

Như @Paul 'Sparrow Hawk' Biron chỉ ra , u cờ công cụ sửa đổi mẫu nên được đặt ở cuối biểu thức chính quy để các hàm PCRE của PHP xử lý chuỗi chủ đề như UTF-8được mã hóa Unicode.

Tất cả cùng nhau sau đó, mô hình

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

sẽ khớp với toàn bộ một chuỗi gồm một hoặc nhiều ký tự trong các tập lệnh Unicode và Latin phổ biến.


Lọc chuỗi tìm kiếm

Một nơi tốt để đánh chặn một chuỗi tìm kiếm là những pre_get_postshành động như nó cháy ngay trước WordPress thực hiện truy vấn. Với dịch vụ chăm sóc nhiều hơn , điều này cũng có thể được thực hiện sử dụng một requestbộ lọc .

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

Đáp ứng các tìm kiếm không được phép

Khi đã xác định rằng chuỗi tìm kiếm có chứa các ký tự không phải là tiếng Latinh, bạn có thể sử dụng WP_Query::set()để sửa đổi truy vấn bằng cách thay đổi các vars truy vấn có tên - do đó ảnh hưởng đến truy vấn SQL sau đó WordPress sẽ thực hiện và thực thi.

Các biến truy vấn phù hợp nhất có lẽ là như sau:

  • slà biến truy vấn tương ứng với chuỗi tìm kiếm. Đặt nó thành nullhoặc một chuỗi trống ( '') sẽ dẫn đến việc WordPress không còn coi truy vấn là tìm kiếm - thường sẽ dẫn đến kết quả này trong một mẫu lưu trữ hiển thị tất cả các bài đăng hoặc trang nhất của trang web, tùy thuộc vào các giá trị của cái khác vars truy vấn. ' 'Tuy nhiên, đặt nó thành một không gian ( ), sẽ dẫn đến việc WordPress nhận ra nó là một tìm kiếm và do đó cố gắng hiển thị search.phpmẫu.
  • page_id có thể được sử dụng để hướng người dùng đến một trang cụ thể mà bạn chọn.
  • post__incó thể hạn chế truy vấn đến một lựa chọn cụ thể của bài viết. Bằng cách đặt nó thành một mảng với ID bài đăng không thể, nó có thể dùng làm thước đo để đảm bảo rằng truy vấn hoàn toàn không có gì .

Ở trên, bạn có thể thực hiện các thao tác sau để phản hồi tìm kiếm xấu bằng cách tải search.phpmẫu không có kết quả:

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

Hiển thị một lỗi

Cách bạn thực sự hiển thị thông báo lỗi phụ thuộc rất nhiều vào ứng dụng của bạn và khả năng của chủ đề của bạn - có nhiều cách có thể được thực hiện. Nếu chủ đề của bạn gọi get_search_form()trong mẫu tìm kiếm, giải pháp đơn giản nhất có lẽ là sử dụng móc pre_get_search_formhành động để đưa ra lỗi của bạn ngay trên biểu mẫu tìm kiếm:

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

Một số khả năng khác để hiển thị thông báo lỗi bao gồm:

  • Nếu trang web của bạn sử dụng JavaScript có thể hiển thị thông báo "flash" hoặc "phương thức" (hoặc bạn tự thêm các khả năng đó), hãy thêm vào logic để hiển thị thông báo khi tải trang khi đặt một biến cụ thể, sau đó thêm wp_enqueue_scriptmóc với giá trị $prioritylớn hơn mã JavaScript đó và sử dụng wp_localize_script()để đặt biến đó để bao gồm thông báo lỗi của bạn.
  • Sử dụng wp_redirect()để gửi người dùng đến URL bạn chọn (phương pháp này yêu cầu tải trang bổ sung).
  • Đặt một biến PHP hoặc gọi một phương thức sẽ thông báo cho chủ đề / plugin của bạn về lỗi để nó có thể hiển thị nó khi thích hợp.
  • Đặt sbiến truy vấn thành ''thay vì ' 'và sử dụng page_idthay post__incho để trả về một trang bạn chọn.
  • Sử dụng một loop_startcái móc để tiêm một WP_Postđối tượng giả mạo có chứa lỗi của bạn vào kết quả truy vấn - đây chắc chắn là một vụ hack xấu xí và có thể không phù hợp với chủ đề cụ thể của bạn, nhưng nó có tác dụng phụ đáng mong muốn là loại bỏ thông báo "Không có kết quả".
  • Sử dụng template_includemóc lọc để trao đổi mẫu tìm kiếm với mẫu tùy chỉnh trong chủ đề hoặc plugin hiển thị lỗi của bạn.

Nếu không kiểm tra chủ đề được đề cập, thật khó để xác định tuyến đường nào bạn nên đi.


2

Bạn sẽ làm điều này bằng cách đặt một hàm xác thực trong PHP để kiểm tra đầu vào theo một biểu thức thông thường như ^[a-zA-Z0-9,.!?' ]*

Vì vậy, nó sẽ trông như thế này:

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

Các RexEx tôi sử dụng cho tất cả các nhân vật A-Z, a-z, 0-9, cũng như ,, ., !, ?, ', ", và (không gian).


2

EDIT: Giải pháp này không được khuyến khích

Giải pháp của tôi dưới đây là một bản hack lạm dụng các hàm mb chuỗi của PHP trong nỗ lực tạo ra các bảng chữ cái thần thánh bằng cách xem xét cách sắp xếp các byte tạo thành chuỗi. Đây là một ý tưởng thực sự tồi tệ và rất dễ bị lỗi .

Xin vui lòng xem câu trả lời khác của tôi cho một giải pháp đơn giản và đáng tin cậy hơn nhiều.


Một phương tiện để ngăn chặn các tìm kiếm sử dụng bảng chữ cái không phải là tiếng Latinh là sử dụng chức năng của PHPmb_detect_encoding() để xem liệu chuỗi tìm kiếm có phù hợp với một trong các lựa chọn mã hóa ký tự tùy chỉnh hay không. Một nơi tốt để làm điều này là những pre_get_postshành động , vì nó bắn ngay trước khi truy vấn được thực thi.

Những gì bạn thực sự làm sau khi bạn xác định một tìm kiếm đang sử dụng mã hóa không hợp lệ thực sự là ứng dụng cụ thể. Ở đây tôi đã đặt truy vấn tìm kiếm thành một không gian duy nhất để đảm bảo rằng WordPress vẫn diễn giải truy vấn là tìm kiếm và do đó vẫn tải search.phpmẫu (và không hướng người dùng đến trang trước, như xảy ra khi chuỗi tìm kiếm là một chuỗi rỗng). Tôi cũng có một biện pháp phòng ngừa bổ sung cho việc thiết lập 'post__in'một mảng với ID bài đăng không thể để đảm bảo rằng hoàn toàn không có gì được trả về .

Thay phiên, bạn có thể xem xét cài đặt chuỗi tìm kiếm nullvà cài page_idđặt để hướng người dùng đến một trang có thông báo lỗi tùy chỉnh của bạn.

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

Chọn bảng mã

Tôi đã viết một bài kiểm tra bảo hiểm so sánh một số chuỗi giả trong các bảng chữ cái khác nhau so với tất cả các bảng mã mặc định được PHP hỗ trợ . Nó không hoàn hảo bởi bất kỳ sự kéo dài nào (tôi không biết chuỗi hình nộm của tôi thực tế đến mức nào và dường như nó bị bóp nghẹt khi phát hiện tiếng Nhật), nhưng nó phần nào hữu ích để xác định các ứng cử viên. Bạn có thể thấy nó trong hành động ở đây .

Sau khi nghiên cứu mã hóa ký tự tiềm năng được gắn cờ bởi bài kiểm tra đó, có vẻ như đó Windows-1252là sự lựa chọn hoàn hảo cho nhu cầu của bạn, bao gồm bảng chữ cái Latinh cũng như các điểm nhấn cho các ngôn ngữ Latin phổ biến.

Một lựa chọn các ISO-8859bộ ký tự sẽ là một lựa chọn khả thi khác, tuy nhiên vì lý do tôi không thể quấn đầu, các mb_chức năng dường như không phân biệt giữa ISO-8859các bộ ký tự khác nhau, mặc dù liệt kê chúng là các bảng mã riêng biệt.

Để cho phép một số ký tự phổ biến khác, bạn cũng có thể xem xét thêm HTML-ENTITIES.


Dường như cơ chế mà các hàm mb chuỗi hoạt động không có khả năng phân biệt giữa các ISO-8859bảng mã .
bosco

Tôi đã học được rằng kiểm tra được liên kết của tôi là không chính xác và sai lệch - các hàm mb chuỗi hoạt động dựa trên tiền đề của các chuỗi byte, do đó, trong khi mã hóa có thể sử dụng các chuỗi byte có thể hỗ trợ các bảng chữ cái được liệt kê, nhưng thực tế không có nghĩa là mã hóa thực sự hỗ trợ các mã đó nhân vật. Do đó, lọc bảng chữ cái của chuỗi bằng cách kiểm tra mã hóa không phải là một cơ chế đáng tin cậy . Hãy xem xét câu trả lời khác của tôi thay vào đó.
bosco

1

Như tôi đã cố gắng giải thích với @MichaelRogers khi anh ấy đăng một câu hỏi tương tự vài ngày trước, biết rằng bộ ký tự (hoặc tập lệnh) được sử dụng trong một chuỗi là KHÔNG đủ để phát hiện ngôn ngữ của chuỗi đó.

Do đó, mặc dù phương pháp được chi tiết bởi @bosco sẽ loại bỏ các chuỗi tiếng Nga, v.v (với 2 chỉnh sửa bên dưới), nhưng nó sẽ KHÔNG giới hạn các tìm kiếm của bạn bằng tiếng Anh.

Để xem điều này, hãy thử:

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

[ lưu ý: 2 điều chỉnh được đề cập ở trên đối với những gì @bosco cung cấp là:

  1. mẫu được đính kèm một chuỗi (bắt buộc phải đúng về mặt cú pháp)
  2. đã thêm công cụ /usửa đổi (bắt buộc để xử lý mẫu và chủ đề dưới dạng mã hóa UTF-8, xem PHP: Công cụ sửa đổi mẫu Regex ]

sẽ tạo ra:

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

[ lưu ý: Tôi nói tiếng Anh, tiếng Pháp và một số tiếng Đức (và một chút tiếng Lorem ipsum :-), nhưng dựa vào Google Dịch cho tiếng Ả Rập, tiếng Nga và tiếng Trung Quốc]

Như bạn có thể thấy, việc dựa vào việc kiểm tra tập lệnh Latin sẽ KHÔNG đảm bảo bạn có tiếng Anh.

Có một số luồng trên StackOverflow (ví dụ: Phát hiện ngôn ngữ từ chuỗi trong PHP ) cung cấp thêm thông tin về chủ đề này.


Hãy để tôi để lại một ghi chú thân thiện, mang tính mô phạm: Lorem ipsum không phải là ngôn ngữ, để nói ai đó nói "lorem ipsum" giống như nói rằng ai đó nói "xin chào thế giới" :) Ngôn ngữ của Lorem ipsumtiếng Latin cũ , và không, "lorem ipsum " không có nghĩa là " xin chào thế giới " :) Thật ra nó là một lỗi đánh máy cho " dolorem ipsum " có nghĩa là " chính nỗi đau " hoặc một cái gì đó tương tự.
gmazzap

@gmazzap Tôi biết, đó là một trò đùa (do đó là ":-)"). Tôi bao gồm lorem ipsum để củng cố quan điểm rằng việc kiểm tra kịch bản nào không kiểm tra ngôn ngữ.
Paul 'Sparrow Hawk' Biron

và thậm chí còn mang tính mô phạm hơn, như đã nói trên lipum.com , "Lorem Ipsum đến từ các phần 1.10.32 và 1.10.33 của" de Finibus Bonorum et Malorum "(The Extreme Extreme of Good and Evil) của Cicero, được viết trong 45 BC. " Nhưng nó cũng có nhiều "ngẫu nhiên" khác nhau để làm cho nó trở nên vô nghĩa với một người nói tiếng Latin bản địa, vì vậy nó không thực sự là "tiếng Latin cũ", mà là một "ngôn ngữ" hoàn toàn được tạo thành.
Paul 'Sparrow Hawk' Biron

À, những sản phẩm tuyệt vời @ Paul'SparrowHawk'Biron! Tôi sẽ cập nhật câu trả lời của mình để sửa biểu thức chính quy và làm rõ chính xác giải pháp của tôi làm gì.
bosco

1
Tôi không quan tâm nếu người đó gõ tiếng Tây Ban Nha. Nó không cần phải là ngôn ngữ tiếng Anh. Tôi đã nói các ký tự được sử dụng trên ngôn ngữ tiếng Anh nên từ A đến Z (trong mũ và không có mũ) + số. Nếu các ngôn ngữ khác xảy ra để sử dụng cùng một ký tự thì tốt thôi. Những gì tôi không muốn cho phép là Cyrillic, kanji, chữ Ả Rập (không biết tên) và bất cứ thứ gì không phải là Aa-Zz + 0-9. Ngôn ngữ không thành vấn đề.
Michael Rogers
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.