将搜索限制为拉丁字符


9

我想将搜索限制为英语+数字上使用的字符。原因是,在mysql日志上查看最慢的查询时,我发现大多数查询来自阿拉伯,俄文和中文字符的搜索,因此我想跳过它们,而是显示一条错误消息。


如果您详细说明了如何显示错误,我将修改我的答案以包括该错误
bosco

我希望该错误显示在搜索页面下方或上方的搜索页面中。
Michael Rogers

Answers:


10

此解决方案通过应用仅与Common和Latin Unicode脚本中的字符匹配的正则表达式来过滤搜索字符串。


将拉丁字符与正则表达式匹配

我只是对Stack Overflow感到震惊。事实证明,正则表达式具有一种机制来匹配整个Unicode类别,包括用于指定整个Unicode“脚本”的值,每个值都对应于不同书写系统中使用的字符组。

这是通过\p在大括号中使用元字符和Unicode类别标识符来完成的,因此可以[\p{Common}\p{Latin}]匹配拉丁或通用脚本中的单个字符,其中包括标点符号,数字和其他符号。

正如@Paul'Sparrow Hawk'Biron指出的那样,应该在正则表达式的末尾设置u 模式修饰符标志,以便PHP的PCRE函数将主题字符串视为UTF-8Unicode编码。

然后一起,模式

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

将匹配由拉丁和通用Unicode脚本中的一个或多个字符组成的整个字符串。


过滤搜索字符串

一个好地方,拦截搜索字符串是pre_get_posts行动,因为它立即触发WordPress的执行查询之前。随着更多的照顾,这也可以通过完成一个request过滤器

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

响应不允许的搜索

一旦确定搜索字符串包含非拉丁字符,就可以WP_Query::set()通过更改其命名查询vars来使用该命令来修改查询 -从而影响WordPress随后编写和执行的SQL查询。

最相关的查询变量可能如下:

  • s是与搜索字符串相对应的查询变量。将其设置为null或将其设置为空字符串('')将导致WordPress不再将查询视为搜索-通常,这会导致在归档模板中显示所有帖子或网站首页,具体取决于其他网站的值查询变量 ' '但是,将其设置为单个空格()将导致WordPress将其识别为搜索,从而尝试显示search.php模板。
  • page_id 可用于将用户定向到您选择的特定页面。
  • post__in可以将查询限制为特定的帖子选择。通过将其设置为具有无法发布的ID的数组,它可以用作确保查询绝对不返回任何值的一种措施

考虑到以上几点,您可以执行以下操作以通过加载search.php没有结果的模板来响应错误的搜索:

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

显示错误

实际显示错误消息的方式在很大程度上取决于您的应用程序和主题的功能-可以通过多种方式来完成此操作。如果您的主题调用get_search_form()了它的搜索模板,则最简单的解决方案可能是使用pre_get_search_form操作挂钩在搜索表单上方立即输出错误:

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

显示错误消息的其他一些可能性包括:

  • 如果您的网站使用的JavaScript可以显示“ flash”或“ modal”消息(或您自己添加此类功能),请在设置了特定变量时向其添加在页面加载时显示消息的逻辑,然后添加一个wp_enqueue_script挂钩的$priority大小大于使JavaScript入队的大小,并用于wp_localize_script()设置该变量以包含您的错误消息。
  • 使用wp_redirect()用户发送到您选择的URL(此方法需要额外的页面加载)。
  • 设置一个PHP变量或调用一个方法,该方法将通知您的主题/插件有关该错误的信息,以便可以在适当的位置显示该错误。
  • 在设置s查询变量'',而不是' '和使用page_id到位post__in,以回报您所选择的页面。
  • 使用loop_start钩子WP_Post包含您的错误的假对象注入查询结果-这绝对是丑陋的破解,可能与您的特定主题看起来不一样,但是它可能具有抑制“无结果”消息的潜在副作用。
  • 使用template_include过滤器挂钩将搜索模板替换为主题或插件中显示错误的自定义模板。

如果不检查相关主题,就很难确定应采取的路线。


2

您可以通过在PHP中放入验证函数来针对正则表达式(例如 ^[a-zA-Z0-9,.!?' ]*

所以它看起来像这样:

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

该RexEx我用所有的字符A-Za-z0-9,以及,.!?'",和(空间)。


2

编辑:不建议使用此解决方案

我下面的解决方案是一种黑客,它通过查看组成字符串的字节排列来滥用PHP的mbstring函数,以试图神奇地破解神圣的字母。这是一个非常糟糕的主意,极容易出错

请参阅我的其他答案,以获得更简单,更可靠的解决方案。


防止使用非拉丁字母进行搜索的一种方法是使用PHP的mb_detect_encoding()功能来查看搜索字符串是否符合自定义字符编码之一。一个很好的做到这一点pre_get_posts动作,因为它触发执行查询权之前。

确定搜索使用无效编码后的实际操作实际上是特定于应用程序的。在这里,我将搜索查询设置为一个空格,以确保WordPress仍将查询解释为搜索,因此仍加载search.php模板(并且不会将用户定向到首页,就像在搜索字符串为一个空字符串)。为了确保绝对不会返回任何内容,我还采取了额外的预防措施,即将其设置'post__in'为具有不可能的post ID的数组

或者,您可以考虑将搜索字符串null设置为和设置page_id,以将用户定向到包含您的自定义错误消息的页面。

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

选择编码

我编写了一个覆盖率测试,将一些不同字母的虚拟字符串与PHP支持的所有默认编码进行比较。它在任何方面都不是完美的(我不知道我的虚拟字符串有多逼真,而且似乎在日语检测中令人cho目结舌),但是它对于确定候选者还是很有用的。您可以在此处查看它的运行情况。

在研究了该测试标记的潜在字符编码之后,似乎Windows-1252是满足您需求的理想选择,涵盖了拉丁字母以及常见拉丁语言的重音符号。

选择ISO-8859字符集应该是另一个可行的选择,但是出于我无法绕开的原因,尽管将它们列出为单独的编码,但这些mb_函数似乎无法区分ISO-8859不同的字符集。

要允许其他一些常见字符,您也可以考虑添加HTML-ENTITIES


看来mbstring函数的工作机制无法区分ISO-8859编码
bosco

我了解到我的链接测试不准确且具有误导性-mbstring函数以字节序列为前提,因此尽管编码可能使用可以支持所列字母的字节序列,但实际上并不意味着编码实际上支持那些字符。因此,通过测试编码来过滤字符串的字母不是一种可靠的机制。请考虑其他答案。
博斯科

1

当我试图解释@MichaelRogers时,他数天前发布了类似的问题,知道在字符串中使用的字符集(或脚本)是不是足以检测的语言是字符串。

因此,虽然通过@bosco详细描述的方法去除俄罗斯等字符串(与2更正如下图),这将限制你搜索到的英语。

要查看此内容,请尝试:

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

[ 注意:上面提到的对@bosco提供的2个更正是:

  1. 该模式用字符串括起来(在语法上必须是正确的PHP)
  2. 添加了/u修饰符(需要将模式和主题视为UTF-8编码,请参见PHP:Regex模式修饰符

会产生:

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

[ 注意:我会说英语,法语和一些德语(还有一点Lorem ipsum :-),但是阿拉伯语,俄语和中文依赖于Google翻译]

正如你所看到的,依靠检查拉丁文脚本将保证你的英语。

StackOverflow上有许多线程(例如,从PHP中的字符串检测语言)可提供有关该主题的更多信息。


让我留下一个友好的,学究的注释:Lorem ipsum不是一种语言,说某人说“ lorem ipsum”就好比说某人在说“ hello world” :) Lorem ipsum的语言是拉丁语,不是,“ lorem” ipsum”并不表示“ hello world”:)实际上,它是“ dolorem ipsum”的错字,表示“疼痛本身”或类似的东西。
gmazzap

@gmazzap我知道,这是个玩笑(因此为“ :-)”)。我包括Lorem存有加强该检查点脚本没有考语言。
保罗“雀鹰”伯龙

甚至更加书呆子,就像它在lipsum.com所说的那样:“ Lorem Ipsum出自Cicero撰写的《善恶之极》(De Finibus Bonorum et Malorum)的1.10.32和1.10.33节,写于45公元前。” 但是它也有各种“随机化”,使它对讲拉丁语的人毫无意义,因此它实际上不是“旧拉丁语”,而是完全构成的“语言”。
保罗“雀鹰”伯龙

啊,好极了@ Paul'SparrowHawk'Biron!我将更新答案以修正正则表达式,并明确说明我的解决方案的功能。
bosco

1
我不在乎此人是否输入西班牙语。不需要严格使用英语。我说过英文上使用的字符,所以从A到Z(大写且无大写)+数字。如果其他语言碰巧使用相同的字符,那么我可以。我不想允许的是西里尔字母,日文汉字,阿拉伯字母(不知道名字)以及任何非Aa-Zz + 0-9的东西。语言无关紧要。
迈克尔·罗杰斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.