Beschränken Sie die Suche auf lateinische Zeichen

9

Ich möchte die Suche auf Zeichen beschränken, die in der englischen Sprache + Zahlen verwendet werden. Der Grund dafür ist, dass die langsamsten Abfragen im MySQL-Protokoll, die ich am häufigsten gefunden habe, aus Suchanfragen in arabischen, russischen und chinesischen Schriftzeichen stammen. Daher möchte ich sie überspringen und stattdessen eine Fehlermeldung anzeigen.

Michael Rogers
quelle
Wenn Sie genau angeben, wie Sie Ihren Fehler anzeigen möchten, werde ich meine Antwort so ändern, dass sie ihn enthält
Bosco
Ich möchte, dass der Fehler auf der Suchseite unter oder über dem Suchformular angezeigt wird.
Michael Rogers

Antworten:

10

Diese Lösung filtert Suchzeichenfolgen, indem ein regulärer Ausdruck angewendet wird, der nur mit Zeichen aus den allgemeinen und lateinischen Unicode-Skripten übereinstimmt.


Übereinstimmende lateinische Zeichen mit regulären Ausdrücken

Ich hatte gerade meinen Verstand bei Stack Overflow umgehauen . Wie sich herausstellt, verfügen reguläre Ausdrücke über einen Mechanismus zum Abgleichen ganzer Unicode-Kategorien, einschließlich Werten zum Angeben ganzer Unicode- "Skripte" , die jeweils Gruppen von Zeichen entsprechen, die in verschiedenen Schriftsystemen verwendet werden.

Dies erfolgt mithilfe des \pMetazeichens, gefolgt von einer Unicode-Kategorie-ID in geschweiften Klammern - entspricht also [\p{Common}\p{Latin}]einem einzelnen Zeichen in der lateinischen oder der allgemeinen Schrift - dies umfasst Interpunktion, Ziffern und verschiedene Symbole.

Wie @ Paul ‚Sperber‘ Biron weist darauf hin , das u Muster Modifikator - Flag sollte am Ende des regulären Ausdrucks , um die PHP-PCRE Funktionen eingestellt werden , um die Zeichenkette zu behandeln , als UTF-8Unicode codiert.

Alles in allem also das Muster

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

stimmt mit einer gesamten Zeichenfolge überein, die aus einem oder mehreren Zeichen in den Skripten Latin und Common Unicode besteht.


Filtern der Suchzeichenfolge

Ein guter Ort, um eine Suchzeichenfolge abzufangen, ist die pre_get_postsAktion , die unmittelbar vor der Ausführung der Abfrage durch WordPress ausgelöst wird. Mit größerer Sorgfalt könnte dies auch unter Verwendung eines requestFilters erreicht werden .

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

Antworten auf nicht zugelassene Suchvorgänge

Sobald festgestellt wurde, dass eine Suchzeichenfolge nicht-lateinische Zeichen enthält, können Sie WP_Query::set()die Abfrage ändern, indem Sie die benannten Abfragevariablen ändern. Dies wirkt sich auf die SQL-Abfrage aus, die WordPress anschließend erstellt und ausführt.

Die relevantesten Abfragevariablen sind wahrscheinlich die folgenden:

  • sist die Abfragevariable, die einer Suchzeichenfolge entspricht. Wenn Sie es auf nulloder eine leere Zeichenfolge ( '') setzen, behandelt WordPress die Abfrage nicht mehr als Suche. Oft führt dies zu einer Archivvorlage, in der alle Beiträge oder die Startseite der Site angezeigt werden, abhängig von den Werten der anderen Abfrage vars. Wenn Sie es jedoch auf ein einzelnes Leerzeichen ( ' ') setzen, erkennt WordPress es als Suche und versucht daher, die search.phpVorlage anzuzeigen .
  • page_id kann verwendet werden, um den Benutzer auf eine bestimmte Seite Ihrer Wahl zu leiten.
  • post__inkann die Abfrage auf eine bestimmte Auswahl von Beiträgen beschränken. Durch Festlegen eines Arrays mit einer unmöglichen Post-ID kann es als Maß dafür dienen, dass die Abfrage absolut nichts zurückgibt .

Vor diesem Hintergrund können Sie Folgendes tun, um auf eine fehlerhafte Suche zu reagieren, indem Sie die search.phpVorlage ohne Ergebnisse laden :

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

Fehler anzeigen

Die Art und Weise, wie Sie die Fehlermeldung tatsächlich anzeigen, hängt stark von Ihrer Anwendung und den Fähigkeiten Ihres Themas ab. Es gibt viele Möglichkeiten, wie dies getan werden kann. Wenn Ihr Thema die Suchvorlage aufruft get_search_form(), besteht die einfachste Lösung wahrscheinlich darin, einen pre_get_search_formAktions- Hook zu verwenden, um Ihren Fehler direkt über dem Suchformular auszugeben:

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

Einige andere Möglichkeiten zum Anzeigen einer Fehlermeldung sind:

  • Wenn Ihre Site JavaScript verwendet, das "Flash" - oder "Modal" -Nachrichten anzeigen kann (oder Sie solche Fähigkeiten selbst hinzufügen), fügen Sie die Logik hinzu, um Nachrichten beim Laden der Seite anzuzeigen, wenn eine bestimmte Variable festgelegt ist, und fügen Sie dann einen wp_enqueue_scriptHook hinzu mit einem $prioritygrößeren Wert als dem, der das JavaScript in die Warteschlange stellt, und verwenden Sie wp_localize_script()diese Variable, um Ihre Fehlermeldung einzuschließen .
  • Verwenden Sie wp_redirect()diese Option , um den Benutzer an die URL Ihrer Wahl zu senden (diese Methode erfordert ein zusätzliches Laden der Seite).
  • Legen Sie eine PHP-Variable fest oder rufen Sie eine Methode auf, die Ihr Thema / Plugin über den Fehler informiert, sodass es gegebenenfalls angezeigt wird.
  • Setzen Sie die sAbfragevariable auf ''anstelle von ' 'und verwenden Sie sie page_idanstelle von post__in, um eine Seite Ihrer Wahl zurückzugeben.
  • Verwenden Sie einen loop_startHook , um ein gefälschtes WP_PostObjekt, das Ihren Fehler enthält, in die Abfrageergebnisse einzufügen. Dies ist definitiv ein hässlicher Hack und sieht mit Ihrem speziellen Thema möglicherweise nicht richtig aus, hat jedoch den potenziell wünschenswerten Nebeneffekt, dass die Meldung "Keine Ergebnisse" unterdrückt wird.
  • Verwenden Sie einen template_includeFilter-Hook, um die Suchvorlage gegen eine benutzerdefinierte in Ihrem Design oder Plugin auszutauschen, die Ihren Fehler anzeigt.

Ohne das betreffende Thema zu untersuchen, ist es schwierig zu bestimmen, welchen Weg Sie einschlagen sollten.

Bosco
quelle
2

Sie würden dies tun, indem Sie eine Validierungsfunktion in PHP einfügen, um die Eingabe gegen einen regulären Ausdruck wie zu testen ^[a-zA-Z0-9,.!?' ]*

So würde es aussehen:

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

Die RexEx ich für alle Zeichen verwendet A-Z, a-z, 0-9sowie ,, ., !, ?, ', ", und (Raum).

Cedon
quelle
2

BEARBEITEN: Diese Lösung wird nicht empfohlen

Meine Lösung unten ist ein Hack, der die mbstring- Funktionen von PHP missbraucht, um Alphabete auf magische Weise zu erraten, indem er die Anordnung der Bytes betrachtet, aus denen die Zeichenfolge besteht. Dies ist eine wirklich schlechte Idee und sehr fehleranfällig .

In meiner anderen Antwort finden Sie eine weitaus einfachere und zuverlässigere Lösung.


Ein Mittel, um Suchvorgänge mit nicht-lateinischen Alphabeten zu verhindern, besteht darin, mithilfe der PHP- mb_detect_encoding()Funktion festzustellen, ob die Suchzeichenfolge einer benutzerdefinierten Auswahl von Zeichencodierungen entspricht. Ein guter Ort, um dies zu tun, ist die pre_get_postsAktion , da sie unmittelbar vor der Ausführung der Abfrage ausgelöst wird.

Was Sie tatsächlich tun, nachdem Sie festgestellt haben, dass eine Suche eine ungültige Codierung verwendet, ist wirklich anwendungsspezifisch. Hier habe ich die Suchabfrage auf ein einzelnes Leerzeichen gesetzt, um sicherzustellen, dass WordPress die Abfrage weiterhin als Suche interpretiert und somit die search.phpVorlage weiterhin lädt (und den Benutzer nicht zur Startseite weiterleitet, wie dies bei der Suchzeichenfolge der Fall ist eine leere Zeichenfolge). Ich habe auch eine zusätzliche Vorsichtsmaßnahme getroffen , 'post__in'um ein Array mit einer unmöglichen Post-ID festzulegen, um sicherzustellen, dass absolut nichts zurückgegeben wird .

Alternativ können Sie die Suchzeichenfolge auf nullund einstellen page_id, um den Benutzer auf eine Seite mit Ihrer benutzerdefinierten Fehlermeldung zu leiten.

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

Codierungen auswählen

Ich habe einen Abdeckungstest geschrieben, in dem einige Dummy-Zeichenfolgen in verschiedenen Alphabeten mit allen von PHP unterstützten Standardcodierungen verglichen wurden . Es ist keineswegs perfekt (ich habe keine Ahnung, wie realistisch meine Dummy-Saiten sind, und es scheint an der japanischen Erkennung zu ersticken), aber es ist etwas nützlich, um Kandidaten zu bestimmen. Sie können es hier in Aktion sehen .

Nachdem Sie nach potenziellen Zeichenkodierungen gesucht haben, die durch diesen Test gekennzeichnet wurden, scheint dies Windows-1252die perfekte Wahl für Ihre Anforderungen zu sein, da sie sowohl das lateinische Alphabet als auch die Akzente für gängige lateinische Sprachen abdecken.

Eine Auswahl der ISO-8859Zeichensätze sollte eine weitere praktikable Wahl sein. Aus Gründen, die ich nicht mb_verstehen kann, scheinen die Funktionen jedoch nicht zwischen ISO-8859den verschiedenen Zeichensätzen zu unterscheiden, obwohl sie als separate Codierungen aufgeführt sind.

Um einige andere gebräuchliche Zeichen zuzulassen, können Sie auch das Hinzufügen in Betracht ziehen HTML-ENTITIES.

Bosco
quelle
Es scheint, dass der Mechanismus, nach dem die mbstring-Funktionen funktionieren, nicht in der LageISO-8859 ist , zwischen Codierungen zu unterscheiden .
Bosco
Ich habe erfahren, dass mein verknüpfter Test ungenau und irreführend ist - die mbstring-Funktionen funktionieren unter der Voraussetzung von Byte-Sequenzen. Während eine Codierung möglicherweise Byte-Sequenzen verwendet, die die aufgelisteten Alphabete unterstützen könnten, bedeutet dies nicht, dass die Codierung diese tatsächlich unterstützt Figuren. Das Filtern der Alphabete von Zeichenfolgen durch Testen von Codierungen ist daher kein zuverlässiger Mechanismus . Bitte beachten Sie stattdessen meine andere Antwort.
Bosco
1

Wie ich @MichaelRogers zu erklären versuchte, als er vor einigen Tagen eine ähnliche Frage stellte, reicht es NICHT aus, den in einer Zeichenfolge verwendeten Zeichensatz (oder das Skript) zu kennen, um die Sprache dieser Zeichenfolge zu erkennen .

Während also die Methode , mit @bosco detailliert wird Russisch entfernen, etc Strings (mit den 2 Korrekturen unten), wird es NICHT Ihre Suche auf Englisch begrenzen.

Um dies zu sehen, versuchen Sie:

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

[ Anmerkung: Die 2 oben erwähnten Korrekturen an @bosco sind:

  1. Das Muster ist von einer Zeichenfolge umgeben (erforderlich, um syntaktisch korrektes PHP zu sein).
  2. Der /uModifikator wurde hinzugefügt (erforderlich, um Muster und Betreff als UTF-8-codiert zu behandeln, siehe PHP: Regex-Mustermodifikatoren ).

was produzieren wird:

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

[ Anmerkung: Ich spreche Englisch, Französisch und etwas Deutsch (und ein bisschen Lorem ipsum :-), habe mich aber für Arabisch, Russisch und Chinesisch auf Google Translate verlassen]

Wie Sie sehen können, wird die Überprüfung des lateinischen Skripts NICHT sicherstellen, dass Sie Englisch sprechen .

Es gibt eine Reihe von Threads in StackOverflow (z. B. Sprache anhand von Zeichenfolgen in PHP erkennen ), die weitere Informationen zu diesem Thema bereitstellen.

Paul 'Sparrow Hawk' Biron
quelle
Lassen Sie mich eine freundliche, pedantische Bemerkung hinterlassen: Lorem ipsum ist keine Sprache. Zu sagen, dass jemand "lorem ipsum" spricht, ist wie zu sagen, dass jemand "hallo Welt" spricht :) Die Sprache von Lorem ipsum ist altes Latein und nein, "lorem" ipsum " bedeutet nicht " hallo Welt " :) Eigentlich ist es ein Tippfehler für " dolorem ipsum ", was " Schmerz selbst " oder so etwas bedeutet .
gmazzap
@gmazzap Ich weiß, das war ein Witz (daher das ":-)"). Ich enthalten Lorem ipsum den Punkt zu verstärken , dass die Überprüfung Skript nicht nicht die Sprache testen.
Paul 'Sparrow Hawk' Biron
und um noch pedantischer zu sein, wie es auf lipsum.com heißt: "Lorem Ipsum stammt aus den Abschnitten 1.10.32 und 1.10.33 von" de Finibus Bonorum et Malorum "(Die Extreme von Gut und Böse) von Cicero, geschrieben in 45 BC. " Es gibt aber auch verschiedene "Randomisierungen", die es für einen lateinischen Muttersprachler unsinnig machen. Es handelt sich also nicht um "altes Latein", sondern um eine vollständig erfundene "Sprache".
Paul 'Sparrow Hawk' Biron
Ah, schöne Fänge @ Paul'SparrowHawk'Biron! Ich werde meine Antwort aktualisieren, um den regulären Ausdruck zu korrigieren und zu klären, was genau meine Lösung bewirkt.
bosco
1
Es ist mir egal, ob die Person Spanisch schreibt. Es muss nicht ausschließlich englischsprachig sein. Ich sagte die in der englischen Sprache verwendeten Zeichen also von A bis Z (in Großbuchstaben und ohne Großbuchstaben) + Zahlen. Wenn andere Sprachen dieselben Zeichen verwenden, ist das für mich in Ordnung. Was ich nicht zulassen möchte, sind Kyrillisch, Kanji, arabische Buchstaben (ich kenne den Namen nicht) und alles, was nicht Aa-Zz + 0-9 ist. Sprache spielt keine Rolle.
Michael Rogers