Limitar búsqueda a caracteres latinos


9

Me gustaría limitar la búsqueda a los caracteres utilizados en el idioma inglés + números. La razón es que, al observar las consultas más lentas en el registro de mysql, la mayoría provino de búsquedas en caracteres árabes, rusos y chinos, por lo que me gustaría omitirlas y mostrar un mensaje de error.


Si detalla cómo le gustaría mostrar su error, modificaré mi respuesta para incluirlo
bosco

Me gustaría que el error aparezca en la página de búsqueda, debajo o encima del formulario de búsqueda.
Michael Rogers

Respuestas:


10

Esta solución filtra las cadenas de búsqueda aplicando una expresión regular que solo coincide con los caracteres de los scripts Common y Latin Unicode.


Relacionar caracteres latinos con expresiones regulares

Simplemente me voló la cabeza en Stack Overflow . Como resultado, las expresiones regulares tienen un mecanismo para unir categorías Unicode completas, incluidos valores para especificar "scripts" Unicode completos , cada uno correspondiente a grupos de caracteres utilizados en diferentes sistemas de escritura.

Esto se realiza mediante el uso de \pmetacaracteres seguido de un identificador de categoría Unicode entre llaves, por lo que [\p{Common}\p{Latin}]coincide con un solo carácter en los guiones latinos o comunes , esto incluye signos de puntuación, números y símbolos misceláneos.

Como señala @Paul 'Sparrow Hawk' Biron , el u indicador del modificador de patrón debe establecerse al final de la expresión regular para que las funciones PCRE de PHP traten la cadena de asunto como UTF-8codificada Unicode.

Todos juntos entonces, el patrón

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

coincidirá con una cadena completa compuesta por uno o más caracteres en los scripts de Unicode latino y común.


Filtrando la cadena de búsqueda

Un buen lugar para interceptar una cadena de búsqueda es la pre_get_postsacción, ya que se dispara inmediatamente antes de que WordPress ejecute la consulta. Con más cuidado , esto también podría lograrse utilizando 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' );

Responder a búsquedas no permitidas

Una vez que se ha determinado que una cadena de búsqueda contiene caracteres no latinos, puede usarla WP_Query::set()para modificar la consulta cambiando sus nombres de consulta , lo que afecta la consulta SQL que WordPress posteriormente compone y ejecuta.

Las variables de consulta más relevantes son probablemente las siguientes:

  • ses la variable de consulta correspondiente a una cadena de búsqueda. Si se configura como nulluna cadena vacía ( ''), WordPress ya no considerará la consulta como una búsqueda, a menudo esto da como resultado una plantilla de archivo que muestra todas las publicaciones o la página principal del sitio, dependiendo de los valores del otro consulta vars. ' 'Sin embargo, si lo configura en un solo espacio ( ), WordPress lo reconocerá como una búsqueda y, por lo tanto, intentará mostrar la search.phpplantilla.
  • page_id podría usarse para dirigir al usuario a una página específica de su elección.
  • post__inpuede restringir la consulta a una selección específica de publicaciones. Al configurarlo en una matriz con una ID de publicación imposible, puede servir como medida para garantizar que la consulta no devuelva absolutamente nada .

Lo anterior en mente, puede hacer lo siguiente para responder a una búsqueda incorrecta cargando la search.phpplantilla sin 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' );

Mostrar un error

La forma en que realmente muestra el mensaje de error depende en gran medida de su aplicación y de las capacidades de su tema; hay muchas maneras de hacerlo. Si su tema get_search_form()aparece en su plantilla de búsqueda, la solución más fácil es probablemente usar un enlace de pre_get_search_formacción para generar su error inmediatamente arriba del formulario de búsqueda:

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

Algunas otras posibilidades para mostrar un mensaje de error incluyen:

  • Si su sitio usa JavaScript que puede mostrar mensajes "flash" o "modales" (o agrega tales habilidades por su cuenta), agregue la lógica para mostrar mensajes en la carga de la página cuando se establece una variable específica, luego agregue un wp_enqueue_scriptgancho con un valor $prioritymayor que el que pone en cola ese JavaScript, y se usa wp_localize_script()para configurar esa variable para incluir su mensaje de error.
  • Use wp_redirect()para enviar al usuario a la URL de su elección (este método requiere una carga de página adicional).
  • Establezca una variable PHP o invoque un método que informará a su tema / complemento sobre el error de modo que pueda mostrarlo cuando sea apropiado.
  • Establezca la svariable de consulta en ''lugar de ' 'y use page_iden lugar de post__inpara devolver una página de su elección.
  • Use un loop_startgancho para inyectar un WP_Postobjeto falso que contenga su error en los resultados de la consulta; este es definitivamente un truco feo y puede no verse bien con su tema en particular, pero tiene el efecto secundario potencialmente deseable de suprimir el mensaje "Sin resultados".
  • Use un template_includegancho de filtro para intercambiar la plantilla de búsqueda con una personalizada en su tema o complemento que muestre su error.

Sin examinar el tema en cuestión, es difícil determinar qué ruta debe tomar.


2

Haría esto poniendo una función de validación en PHP para probar la entrada contra una expresión regular como ^[a-zA-Z0-9,.!?' ]*

Entonces se vería así:

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

El RexEx utilicé para todos los caracteres A-Z, a-z, 0-9, así como ,, ., !, ?, ', ", y (espacio).


2

EDITAR: no se recomienda esta solución

Mi solución a continuación es un truco que abusa de las funciones mbstring de PHP en un intento de adivinar alfabetos mágicamente al observar la disposición de los bytes que componen la cadena. Esta es una muy mala idea y es muy propensa a errores .

Consulte mi otra respuesta para obtener una solución mucho más simple y mucho más confiable.


Una manera de evitar búsquedas usando alfabetos no latinos es usar la mb_detect_encoding()función de PHP para ver si la cadena de búsqueda se ajusta a una de una selección personalizada de codificaciones de caracteres. Un buen lugar para hacerlo es la pre_get_postsacción , ya que se dispara justo antes de que se ejecute la consulta.

Lo que realmente hace después de haber determinado que una búsqueda está utilizando una codificación no válida es realmente específico de la aplicación. Aquí configuré la consulta de búsqueda en un solo espacio para garantizar que WordPress todavía interprete la consulta como una búsqueda y, por lo tanto, todavía cargue la search.phpplantilla (y no dirija al usuario a la página principal, como sucede cuando la cadena de búsqueda es una cadena vacía). También tomo una precaución adicional de configurar 'post__in'una matriz con una ID de publicación imposible para asegurarme de que no se devuelva absolutamente nada .

Alternativamente, puede considerar establecer la cadena de búsqueda nully establecerla page_idpara dirigir al usuario a una página con su mensaje de error personalizado.

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

Elegir codificaciones

Escribí una prueba de cobertura que compara algunas cadenas ficticias en diferentes alfabetos con todas las codificaciones predeterminadas compatibles con PHP . No es perfecto de ninguna manera (no tengo idea de cuán realistas son mis cadenas falsas, y parece ahogarse con la detección japonesa), pero es algo útil para determinar candidatos. Puedes verlo en acción aquí .

Después de investigar posibles codificaciones de caracteres marcadas por esa prueba, parece que Windows-1252es la elección perfecta para sus necesidades, ya que cubre el alfabeto latino y los acentos para los idiomas latinos comunes.

Una selección de los ISO-8859conjuntos de caracteres debería ser otra opción viable, sin embargo, por razones que no puedo entender, las mb_funciones no parecen diferenciar entre ISO-8859los diferentes conjuntos de caracteres, a pesar de enumerarlos como codificaciones separadas.

Para permitir algunos otros caracteres comunes, también puede considerar agregar HTML-ENTITIES.


Parece que el mecanismo por el cual funcionan las funciones de mbstring es incapaz de diferenciar entre ISO-8859codificaciones .
bosco

Aprendí que mi prueba vinculada es inexacta y engañosa: las funciones de mbstring funcionan como premisas de secuencias de bytes, por lo que si bien una codificación puede usar secuencias de bytes que podrían admitir los alfabetos enumerados, en realidad no significa que la codificación realmente los admita caracteres. Por lo tanto, filtrar alfabetos de cadenas mediante la prueba de codificaciones no es un mecanismo confiable . Por favor considere mi otra respuesta en su lugar.
bosco

1

Como traté de explicarle a @MichaelRogers cuando publicó una pregunta similar hace varios días, conocer el conjunto de caracteres (o script) utilizado en una cadena NO es suficiente para detectar el idioma de esa cadena.

Por lo tanto, mientras que el método detallado por @bosco se retire Rusa, etc cuerdas (con las correcciones 2 abajo), será NO limitar las búsquedas a Inglés.

Para ver esto, intente:

$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: las 2 correcciones mencionadas anteriormente a lo que @bosco proporcionó son:

  1. el patrón está encerrado en una cadena (se requiere que sea sintácticamente correcto PHP)
  2. agregó el /umodificador (requerido para tratar el patrón y el sujeto como codificado UTF-8, vea PHP: Modificadores de patrón de expresiones regulares ]

que producirá:

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: hablo inglés, francés y algo de alemán (y un poco de Lorem ipsum :-), pero confié en Google Translate para el árabe, ruso y chino]

Como puede ver, confiar en buscar el script latino NO asegurará que tenga inglés.

Hay varios subprocesos en StackOverflow (por ejemplo, Detect language from string in PHP ) que proporcionan más información sobre el tema.


Permítanme dejar una nota amigable y pedante: Lorem ipsum no es un idioma, decir que alguien habla "lorem ipsum" es como decir que alguien habla "hola mundo" :) El idioma de Lorem ipsum es latín antiguo y no, "lorem ipsum " no significa " hola mundo " :) En realidad es un error tipográfico para " dolorem ipsum " que significa " dolor en sí mismo " o algo así.
gmazzap

@gmazzap Lo sé, fue una broma (de ahí el ":-)"). Incluí lorem ipsum para reforzar el punto de que el control de la secuencia de comandos no no probar el lenguaje.
Paul 'Sparrow Hawk' Biron

y para ser aún más pedante, como dice en lipsum.com , "Lorem Ipsum proviene de las secciones 1.10.32 y 1.10.33 de" de Finibus Bonorum et Malorum "(Los extremos del bien y del mal) de Cicero, escrito en 45 ANTES DE CRISTO." Pero también tiene varias "aleatorizaciones" para que no tenga sentido para un hablante latino nativo, por lo que en realidad no es "latín antiguo", sino un "idioma" completamente inventado.
Paul 'Sparrow Hawk' Biron

Ah, buenas capturas @ Paul'SparrowHawk'Biron! Actualizaré mi respuesta para corregir la expresión regular y aclarar qué hace exactamente mi solución.
bosco

1
No me importa si la persona escribe en español. No necesita ser estrictamente inglés. Dije que los caracteres se usaban en inglés, así que de la A a la Z (en mayúsculas y sin mayúsculas) + números. Si otros idiomas usan los mismos caracteres, entonces bien por mí. Lo que no quiero permitir es cirílico, kanji, letras árabes (no sé el nombre) y cualquier cosa que no sea Aa-Zz + 0-9. El idioma no importa.
Michael Rogers
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.