我想将搜索限制为英语+数字上使用的字符。原因是,在mysql日志上查看最慢的查询时,我发现大多数查询来自阿拉伯,俄文和中文字符的搜索,因此我想跳过它们,而是显示一条错误消息。
我想将搜索限制为英语+数字上使用的字符。原因是,在mysql日志上查看最慢的查询时,我发现大多数查询来自阿拉伯,俄文和中文字符的搜索,因此我想跳过它们,而是显示一条错误消息。
Answers:
此解决方案通过应用仅与Common和Latin Unicode脚本中的字符匹配的正则表达式来过滤搜索字符串。
我只是对Stack Overflow感到震惊。事实证明,正则表达式具有一种机制来匹配整个Unicode类别,包括用于指定整个Unicode“脚本”的值,每个值都对应于不同书写系统中使用的字符组。
这是通过\p
在大括号中使用元字符和Unicode类别标识符来完成的,因此可以[\p{Common}\p{Latin}]
匹配拉丁或通用脚本中的单个字符,其中包括标点符号,数字和其他符号。
正如@Paul'Sparrow Hawk'Biron指出的那样,应该在正则表达式的末尾设置u
模式修饰符标志,以便PHP的PCRE函数将主题字符串视为UTF-8
Unicode编码。
然后一起,模式
/^[\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>';
}
显示错误消息的其他一些可能性包括:
wp_enqueue_script
挂钩的$priority
大小大于使JavaScript入队的大小,并用于wp_localize_script()
设置该变量以包含您的错误消息。wp_redirect()
用户发送到您选择的URL(此方法需要额外的页面加载)。s
查询变量''
,而不是' '
和使用page_id
到位post__in
,以回报您所选择的页面。loop_start
钩子将WP_Post
包含您的错误的假对象注入查询结果-这绝对是丑陋的破解,可能与您的特定主题看起来不一样,但是它可能具有抑制“无结果”消息的潜在副作用。template_include
过滤器挂钩将搜索模板替换为主题或插件中显示错误的自定义模板。如果不检查相关主题,就很难确定应采取的路线。
我下面的解决方案是一种黑客,它通过查看组成字符串的字节排列来滥用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
。
ISO-8859
编码。
当我试图解释@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个更正是:
/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中的字符串检测语言)可提供有关该主题的更多信息。