Limitar a pesquisa a caracteres latinos

9

Gostaria de limitar a pesquisa a caracteres usados ​​no idioma inglês + números. O motivo é que, olhando para as consultas mais lentas do log do mysql, as mais encontradas vêm de pesquisas em caracteres árabes, russos e chineses, então eu gostaria de ignorá-las e exibir uma mensagem de erro.

Michael Rogers
fonte
Se você detalhar como você gostaria de exibir o seu erro Vou modificar a minha resposta para incluí-lo
bosco
Gostaria que o erro aparecesse na página de pesquisa, abaixo ou acima do formulário de pesquisa.
Michael Rogers

Respostas:

10

Essa solução filtra as cadeias de pesquisa aplicando uma expressão regular que corresponde apenas aos caracteres dos scripts Common e Latin Unicode.


Correspondência de caracteres latinos com expressões regulares

Eu acabei de pensar no Stack Overflow . Como se vê, expressões regulares possuem um mecanismo para corresponder a categorias Unicode inteiras, incluindo valores para especificar "scripts" Unicode inteiros , cada um correspondendo a grupos de caracteres usados ​​em diferentes sistemas de escrita.

Isso é feito usando o \pmeta-caractere seguido por um identificador de categoria Unicode entre chaves - para [\p{Common}\p{Latin}]corresponder a um único caractere nos scripts em latim ou em comum - isso inclui pontuação, numerais e símbolos diversos.

Como o @Paul 'Sparrow Hawk' Biron aponta , o u sinalizador modificador de padrão deve ser definido no final da expressão regular para que as funções PCRE do PHP tratem a sequência de assunto como UTF-8codificada em Unicode.

Todos juntos então, o padrão

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

corresponderá a uma sequência inteira composta por um ou mais caracteres nos scripts Latin e Common Unicode.


Filtrando a cadeia de pesquisa

Um bom lugar para interceptar uma sequência de pesquisa é a pre_get_postsação que é acionada imediatamente antes do WordPress executar a consulta. Com mais cuidado , isso também pode ser realizado usando um 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' );

Respondendo a pesquisas não permitidas

Depois de determinar que uma string de pesquisa contém caracteres não latinos, você pode usá-la WP_Query::set()para modificar a consulta, alterando o nome de vars de consulta - afetando a consulta SQL que o WordPress posteriormente compõe e executa.

As variáveis ​​de consulta mais relevantes são provavelmente as seguintes:

  • sé a variável de consulta correspondente a uma cadeia de pesquisa. Configurá-lo como nulluma string vazia ( '') fará com que o WordPress não trate mais a consulta como uma pesquisa - muitas vezes isso resulta em um modelo de arquivo que exibe todas as postagens ou a página inicial do site, dependendo dos valores dos outros vars de consulta. A configuração para um único espaço ( ' '), no entanto, fará com que o WordPress o reconheça como uma pesquisa e, assim, tente exibir o search.phpmodelo.
  • page_id pode ser usado para direcionar o usuário para uma página específica de sua escolha.
  • post__inpode restringir a consulta a uma seleção específica de postagens. Ao configurá-lo para uma matriz com um ID de postagem impossível, ele pode servir como uma medida para garantir que a consulta não retorne absolutamente nada .

Com o exposto acima, você pode fazer o seguinte para responder a uma pesquisa incorreta carregando o search.phpmodelo sem resultados:

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

Exibindo um erro

A maneira como você realmente exibe a mensagem de erro depende muito do seu aplicativo e das habilidades do seu tema - há várias maneiras de fazer isso. Se o seu tema chamar get_search_form()o modelo de pesquisa, a solução mais fácil é provavelmente usar um gancho de pre_get_search_formação para gerar seu erro imediatamente acima do formulário de pesquisa:

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

Algumas outras possibilidades para exibir uma mensagem de erro incluem:

  • Se seu site usa JavaScript, que pode exibir mensagens "flash" ou "modal" (ou você pode adicionar essas habilidades por conta própria), adicione a lógica para exibir mensagens no carregamento da página quando uma variável específica for definida e adicione um wp_enqueue_scriptgancho com um valor $prioritymaior que o que enfileira esse JavaScript e use wp_localize_script()para definir essa variável para incluir sua mensagem de erro.
  • Use wp_redirect()para enviar o usuário ao URL de sua escolha (esse método requer um carregamento adicional da página).
  • Defina uma variável PHP ou chame um método que informe seu tema / plugin sobre o erro, de forma que ele possa ser exibido quando apropriado.
  • Defina a svariável de consulta como em ''vez de ' 'e use page_idno lugar de post__inpara retornar uma página de sua escolha.
  • Use um loop_startgancho para injetar um WP_Postobjeto falso que contenha seu erro nos resultados da consulta - esse é definitivamente um truque feio e pode não parecer adequado ao seu tema específico, mas tem o efeito colateral potencialmente desejável de suprimir a mensagem "Sem resultados".
  • Use um template_includegancho de filtro para trocar o modelo de pesquisa por um personalizado no seu tema ou plug-in, que exibe seu erro.

Sem examinar o tema em questão, é difícil determinar qual caminho você deve seguir.

bosco
fonte
2

Você faria isso colocando uma função de validação no PHP para testar a entrada com uma expressão regular como ^[a-zA-Z0-9,.!?' ]*

Então ficaria assim:

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

O RexEx I utilizado para todos os caracteres A-Z, a-z, 0-9, bem como ,, ., !, ?, ', ", e (espaço).

Cedon
fonte
2

EDIT: Esta solução não é recomendada

Minha solução abaixo é um hack que abusa das funções mbstring do PHP em uma tentativa de adivinhar magicamente os alfabetos, observando o arranjo de bytes que compõem a string. Essa é uma péssima idéia e é altamente propensa a erros .

Por favor, veja minha outra resposta para uma solução muito mais simples e muito mais confiável.


Um meio de impedir pesquisas usando alfabetos não latinos é usar a mb_detect_encoding()função do PHP para verificar se a string de pesquisa está de acordo com uma de uma seleção personalizada de codificações de caracteres. Um bom lugar para fazer isso é a pre_get_postsação , pois é acionada imediatamente antes da consulta ser executada.

O que você realmente faz depois de determinar que uma pesquisa está usando uma codificação inválida é realmente específico do aplicativo. Aqui, configurei a consulta de pesquisa como um espaço único para garantir que o WordPress ainda interprete a consulta como uma pesquisa e, assim, ainda carrega o search.phpmodelo (e não direciona o usuário para a página inicial, como acontece quando a string de pesquisa é uma string vazia). Também tomo uma precaução adicional de definir 'post__in'uma matriz com um ID de postagem impossível para garantir que absolutamente nada seja retornado .

Como alternativa, você pode considerar definir a sequência de pesquisa para nulle page_iddirecionar o usuário a uma página com sua mensagem de erro personalizada.

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

Escolhendo codificações

Eu escrevi um teste de cobertura comparando algumas seqüências fictícias em diferentes alfabetos em relação a todas as codificações padrão suportadas pelo PHP . Não é perfeito de forma alguma (não tenho idéia de quão realistas são minhas cordas fictícias e parece sufocar na detecção japonesa), mas é um pouco útil para determinar os candidatos. Você pode vê-lo em ação aqui .

Depois de pesquisar as possíveis codificações de caracteres sinalizadas por esse teste, parece que Windows-1252é a escolha perfeita para suas necessidades, cobrindo o alfabeto latino e os detalhes para idiomas latinos comuns.

Uma seleção dos ISO-8859conjuntos de caracteres deve ser outra opção viável, no entanto, por razões que não consigo entender, as mb_funções parecem não diferenciar ISO-8859os diferentes conjuntos de caracteres, apesar de serem listados como codificações separadas.

Para permitir outros caracteres comuns, considere também adicionar HTML-ENTITIES.

bosco
fonte
Parece que o mecanismo pelo qual as funções mbstring funcionam é incapaz de diferenciar entre ISO-8859codificações .
bosco 25/03
Aprendi que meu teste vinculado é impreciso e enganoso - as funções mbstring funcionam com a premissa de sequências de bytes, portanto, embora uma codificação possa usar sequências de bytes que possam suportar os alfabetos listados, na verdade não significa que a codificação realmente suporte aqueles personagens. Portanto, filtrar os alfabetos das strings testando codificações não é um mecanismo confiável . Por favor, considere minha outra resposta.
bosco
1

Como tentei explicar a @MichaelRogers quando ele postou uma pergunta semelhante há alguns dias, conhecer o conjunto de caracteres (ou script) usado em uma string NÃO é suficiente para detectar o idioma dessa string.

Assim, enquanto o método detalhado por @bosco irá remover russo, etc cordas (com os 2 correções abaixo), ele irá não limitar as suas pesquisas para Inglês.

Para ver isso, tente:

$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: as 2 correções mencionadas acima no que o @bosco forneceu são:

  1. o padrão inclui uma cadeia de caracteres (necessário para estar PHP sintaticamente correto)
  2. adicionado o /umodificador (necessário para tratar o padrão e o assunto como codificado em UTF-8, consulte PHP: Modificadores de Padrão Regex ]

que produzirá:

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

[ observação: falo inglês, francês e um pouco de alemão (e um pouco de Lorem ipsum :-), mas confiei no Google Translate para árabe, russo e chinês]

Como você pode ver, depender da verificação do script latino NÃO garantirá que você tenha inglês.

Há vários threads no StackOverflow (por exemplo, Detectar linguagem da string no PHP ) que fornecem mais informações sobre o assunto.

Paul 'Sparrow Hawk' Biron
fonte
Deixe-me deixar uma nota amigável e pedante: Lorem ipsum não é um idioma, dizer que alguém fala "lorem ipsum" é como dizer que alguém fala "olá mundo" :) O idioma de Lorem ipsum é latim antigo e não " , lorem ipsum " não significa " olá mundo " :) Na verdade, é um erro de digitação para " dolorem ipsum ", que significa " dor em si " ou algo assim.
gmazzap
@gmazzap eu sei, isso foi uma piada (daí o ":-)"). Eu incluí lorem ipsum para reforçar o ponto que a verificação do roteiro que não testar o idioma.
Paul 'Sparrow Hawk' Biron /
e para ser ainda mais pedante, como diz no lipsum.com , "Lorem Ipsum vem das seções 1.10.32 e 1.10.33 de" de Finibus Bonorum et Malorum "(Os extremos do bem e do mal), de Cícero, escrito em 45 BC ". Mas também possui várias "randomizações" para torná-lo sem sentido para um falante nativo de latim, portanto, na verdade, não é "latim antigo", mas uma "linguagem" completamente inventada.
Paul 'Sparrow Hawk' Biron /
Ah, boas capturas @ Paul'SparrowHawk'Biron! Atualizarei minha resposta para corrigir a expressão regular e esclarecer o que exatamente minha solução faz.
bosco
11
Eu não ligo se a pessoa digita em espanhol. Não precisa ser estritamente inglês. Eu disse que os caracteres usados ​​no idioma inglês eram de A a Z (em maiúsculas e sem maiúsculas) + números. Se outros idiomas usarem os mesmos caracteres, tudo bem por mim. O que eu não quero permitir é cirílico, kanji, letras árabes (não sei o nome) e qualquer coisa que não seja Aa-Zz + 0-9. Linguagem não importa.
Michael Rogers