Limita la ricerca ai caratteri latini


9

Vorrei limitare la ricerca ai caratteri utilizzati nella lingua inglese + numeri. Il motivo è che guardando le query più lente sul registro mysql ho riscontrato che la maggior parte proviene da ricerche in caratteri arabi, russi e cinesi, quindi vorrei saltarle e visualizzare invece un messaggio di errore.


Se descrivi in ​​dettaglio come desideri visualizzare il tuo errore, modificherò la mia risposta per includerla
bosco

Vorrei che l'errore venisse visualizzato nella pagina di ricerca, sotto o sopra il modulo di ricerca.
Michael Rogers,

Risposte:


10

Questa soluzione filtra le stringhe di ricerca applicando un'espressione regolare che corrisponde solo ai caratteri degli script Unicode comuni e latini.


Corrispondenza di caratteri latini con espressioni regolari

Ho appena fatto esplodere la mia mente a Stack Overflow . A quanto pare, le espressioni regolari hanno un meccanismo per abbinare intere categorie Unicode, inclusi i valori per specificare interi "script" Unicode , ciascuno corrispondente a gruppi di caratteri utilizzati in diversi sistemi di scrittura.

Questo viene fatto utilizzando il \pmeta-carattere seguito da un identificatore di categoria Unicode tra parentesi graffe - quindi [\p{Common}\p{Latin}]corrisponde a un singolo carattere negli script latino o comune - questo include punteggiatura, numeri e simboli vari.

Come sottolinea Biron 'Sparrow Hawk' di @Paul , il u flag modificatore di pattern dovrebbe essere impostato alla fine dell'espressione regolare affinché le funzioni PCRE di PHP trattino la stringa di soggetti come UTF-8codificata Unicode.

Tutti insieme, quindi, il modello

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

corrisponderà a un'intera stringa composta da uno o più caratteri negli script Latin e Common Unicode.


Filtro della stringa di ricerca

Un buon posto per intercettare una stringa di ricerca è l' pre_get_postsazione che si attiva immediatamente prima che WordPress esegua la query. Con più attenzione , questo potrebbe anche essere realizzato usando un requestfiltro .

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

Risposta a ricerche non consentite

Una volta stabilito che una stringa di ricerca contiene caratteri non latini, è possibile utilizzare WP_Query::set()per modificare la query modificandone il nome chiamato query - influenzando così la query SQL che WordPress successivamente compone ed esegue.

Le variabili di query più rilevanti sono probabilmente le seguenti:

  • sè la variabile di query corrispondente a una stringa di ricerca. L'impostazione su nullo una stringa vuota ( '') comporterà che WordPress non considera più la query come una ricerca - spesso questo porta a un modello di archivio che mostra tutti i post o la prima pagina del sito, a seconda dei valori dell'altro query var. Impostandolo su un singolo spazio ( ' '), tuttavia, WordPress lo riconoscerà come una ricerca e quindi tenterà di visualizzare il search.phpmodello.
  • page_id potrebbe essere utilizzato per indirizzare l'utente a una pagina specifica di tua scelta.
  • post__inpuò limitare la query a una selezione specifica di post. Impostandolo su un array con un ID post impossibile, può servire come misura per garantire che la query non restituisca assolutamente nulla .

Quanto sopra in mente, potresti fare quanto segue per rispondere a una cattiva ricerca caricando il search.phpmodello senza risultati:

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

Visualizzazione di un errore

Il modo in cui visualizzi effettivamente il messaggio di errore dipende fortemente dalla tua applicazione e dalle capacità del tuo tema: ci sono molti modi per farlo. Se il tuo tema chiama get_search_form()nel suo modello di ricerca, la soluzione più semplice è probabilmente quella di utilizzare un hook di pre_get_search_formazione per generare l'errore immediatamente sopra il modulo di ricerca:

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

Alcune altre possibilità per visualizzare un messaggio di errore includono:

  • Se il tuo sito utilizza JavaScript che può visualizzare messaggi "flash" o "modali" (o aggiungi tali abilità da solo), aggiungi ad esso la logica per visualizzare i messaggi al caricamento della pagina quando viene impostata una variabile specifica, quindi aggiungi un wp_enqueue_scripthook con un valore $prioritymaggiore di quello che accoda quel JavaScript e usa wp_localize_script()per impostare quella variabile in modo da includere il tuo messaggio di errore.
  • Utilizzare wp_redirect()per inviare l'utente all'URL desiderato (questo metodo richiede un caricamento di pagina aggiuntivo).
  • Imposta una variabile PHP o invoca un metodo che informerà il tuo tema / plugin sull'errore in modo che possa visualizzarlo dove appropriato.
  • Impostare la svariabile di query su ''invece di ' 'e utilizzare page_idal posto di post__inper restituire una pagina di propria scelta.
  • Usa un loop_starthook per iniettare un WP_Postoggetto falso contenente il tuo errore nei risultati della query: questo è sicuramente un brutto hack e potrebbe non sembrare giusto con il tuo tema particolare, ma ha l'effetto collaterale potenzialmente desiderabile di sopprimere il messaggio "Nessun risultato".
  • Utilizzare un template_includehook di filtro per scambiare il modello di ricerca con uno personalizzato nel tema o nel plug-in che visualizza l'errore.

Senza esaminare il tema in questione, è difficile determinare quale percorso si dovrebbe prendere.


2

Lo faresti inserendo una funzione di validazione in PHP per testare l'input con un'espressione regolare simile ^[a-zA-Z0-9,.!?' ]*

Quindi sarebbe simile a questo:

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

Il RexEx ho usato per tutti i personaggi A-Z, a-z, 0-9, così come ,, ., !, ?, ', ", e (spazio).


2

EDIT: questa soluzione non è raccomandata

La mia soluzione di seguito è un hack che abusa delle funzioni mbstring di PHP nel tentativo di magicamente divini alfabeti osservando la disposizione dei byte che compongono la stringa. Questa è una pessima idea ed è molto soggetta a errori .

Vedi la mia altra risposta per una soluzione molto più semplice e molto più affidabile.


Un mezzo per impedire le ricerche utilizzando alfabeti non latini è utilizzare la mb_detect_encoding()funzione di PHP per vedere se la stringa di ricerca è conforme a una di una selezione personalizzata di codifiche di caratteri. Un buon posto per farlo è l' pre_get_postsazione , poiché si attiva subito prima dell'esecuzione della query.

Quello che fai effettivamente dopo aver determinato che una ricerca utilizza una codifica non valida è davvero specifico dell'applicazione. Qui ho impostato la query di ricerca su un singolo spazio per assicurarmi che WordPress interpreti ancora la query come una ricerca e quindi carichi ancora il search.phpmodello (e non indirizzi l'utente alla prima pagina, come accade quando la stringa di ricerca è una stringa vuota). Prendo anche un'ulteriore precauzione per l' impostazione 'post__in'su un array con un ID post impossibile al fine di assicurarmi che non venga restituito assolutamente nulla .

In alternativa, si potrebbe considerare l'impostazione della stringa di ricerca per nulle l'impostazione page_idal fine di indirizzare l'utente a una pagina con il messaggio di errore personalizzato.

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

La scelta di codifiche

Ho scritto un test di copertura confrontando alcune stringhe fittizie in diversi alfabeti con tutte le codifiche predefinite supportate da PHP . Non è perfetto da nessuna parte (non ho idea di quanto siano realistiche le mie stringhe fittizie, e sembra soffocare sulla rilevazione giapponese), ma è in qualche modo utile per determinare i candidati. Puoi vederlo in azione qui .

Dopo aver ricercato potenziali codifiche di caratteri contrassegnate da quel test, sembra che Windows-1252sia la scelta perfetta per le tue esigenze, coprendo l'alfabeto latino e gli accenti per le lingue latine comuni.

Una selezione dei ISO-8859set di caratteri dovrebbe essere un'altra scelta praticabile, tuttavia per motivi che non riesco a avvolgere la mia testa, le mb_funzioni non sembrano differenziare ISO-8859i diversi set di caratteri, nonostante li elencino come codifiche separate.

Per consentire alcuni altri personaggi comuni, potresti anche prendere in considerazione l'aggiunta HTML-ENTITIES.


Sembra che il meccanismo con cui funzionano le funzioni mbstring non sia in grado di differenziare tra ISO-8859codifiche .
bosco

Ho imparato che il mio test collegato è inaccurato e fuorviante: le funzioni mbstring funzionano in base alle sequenze di byte, quindi mentre una codifica può utilizzare sequenze di byte che potrebbero supportare gli alfabeti elencati, non significa che la codifica supporti effettivamente quelle personaggi. Pertanto, filtrare gli alfabeti delle stringhe testando le codifiche non è un meccanismo affidabile . Per favore, considera invece la mia altra risposta.
bosco

1

Come ho cercato di spiegare a @MichaelRogers quando ha pubblicato una domanda simile diversi giorni fa, sapendo che il set di caratteri (o lo script) usato in una stringa è NON sufficiente per rilevare la lingua di quella stringa.

Quindi, mentre il metodo dettagliato da @bosco sarà rimuovere russo, ecc archi (con le 2 correzioni sotto), sarà NON limitare le ricerche a Inglese.

Per vedere questo, prova:

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

[ nota: le 2 correzioni sopra menzionate a ciò che @bosco ha fornito sono:

  1. il modello è racchiuso in una stringa (necessario per essere PHP sintatticamente corretto)
  2. aggiunto il /umodificatore (necessario per trattare pattern e soggetti come codificati UTF-8, vedere PHP: Regex Pattern Modifiers ]

che produrrà:

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

[ nota: parlo inglese, francese e un po 'di tedesco (e un po' di Lorem ipsum :-), ma ho fatto affidamento su Google Translate per l'arabo, il russo e il cinese]

Come puoi vedere, fare affidamento sul controllo dello script latino lo farà NON ti assicurerà di avere l'inglese.

Esistono numerosi thread su StackOverflow (ad esempio, Rileva lingua dalla stringa in PHP ) che forniscono ulteriori informazioni sull'argomento.


Lasciatemi lasciare una nota amichevole e pedante: Lorem ipsum non è una lingua, dire che qualcuno parla "lorem ipsum" è come dire che qualcuno parla "ciao mondo" :) Il linguaggio di Lorem ipsum è latino antico e no "lorem ipsum " non significa " ciao mondo " :) In realtà è un refuso per " dolorem ipsum " che significa " dolore stesso " o qualcosa del genere.
gmazzap

@gmazzap Lo so, era uno scherzo (da qui il ":-)"). Ho incluso Lorem ipsum a rafforzare il punto che controllando la sceneggiatura non non testare la lingua.
Paul 'Sparrow Hawk' Biron,

e per essere ancora più pedanti, come dice su lipsum.com , "Lorem Ipsum proviene dalle sezioni 1.10.32 e 1.10.33 di" de Finibus Bonorum et Malorum "(Gli estremi del bene e del male) di Cicerone, scritto in 45 AVANTI CRISTO." Ma ha anche varie "randomizzazioni" per renderlo insensato a un madrelingua latino, quindi non è in realtà "vecchio latino", ma un "linguaggio" completamente inventato.
Paul 'Sparrow Hawk' Biron

Ah, belle catture @ Paul'SparrowHawk'Biron! Aggiornerò la mia risposta per correggere l'espressione regolare e chiarire cosa fa esattamente la mia soluzione.
bosco

1
Non mi interessa se la persona digita in spagnolo. Non ha bisogno di essere rigorosamente in lingua inglese. Ho detto che i caratteri usati sulla lingua inglese sono dalla A alla Z (maiuscoli e senza maiuscoli) + numeri. Se altre lingue usano gli stessi caratteri, va bene anche per me. Quello che non voglio permettere è cirillico, kanji, lettere arabe (non conosco il nome) e tutto ciò che non è Aa-Zz + 0-9. La lingua non ha importanza.
Michael Rogers,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.