YAOHAIXIAO.COM

HTML(5),CSS(3),JavaScript,DOM,Ajax,JSON,Front-end technologies & Yaohaixiao

热门标签:JavaScript Performance 前端开发 前端性能优化 原创

Rss

Home » WordPress » 使用 preg_replace() 函数为 WordPress 添加搜索关键字高亮

使用 preg_replace() 函数为 WordPress 添加搜索关键字高亮

这两天在完善 BlueNight 主题的搜索功能,给 WordPress 自带的搜索结果添加了单个关键字高亮(以下简称关键词高亮,多关键字的分词高亮也不是一会半会可以搞好的,不过在我的算法更新里简单的实现了多关键字高亮)。之前在网上参考过一些资料,有的是通过获取 Google 或百度的搜索结果来显示,效果不错,不过会搜到很多不相关的内容。有的就是直接简单的在 WordPress 里提供的 get_the_excerpt() 的内容中添加高亮,这样很多时候在摘要中和标题中都看不到搜索的关键字。在参考了各种资料后,自己还是采取直接在 WordPress 的搜索结果上来添加关键字高亮。最后弄出来的搜索结果标题和文章摘要都有高亮,并且保证了摘要中至少出现了一个关键字的高亮。这里把我的检验介绍一下,先看看我的搜索关键字高亮的效果图吧:

wordpress-search-keywords-highlighted

单个关键字高亮

效果演示:多关键字高亮

preg_replace() 函数给 WordPress 文章标题添加关键字高亮

给标题添加高亮相对容易,我们只需要使用 PHP 中的 preg_replace() 函数替换就行关键字。

preg_replace() 函数简介:

preg_replace($find,$replace,$string,$count)
参数 描述
$find 必需。规定要查找的值的正则表达式。
$replace 必需。规定替换 find 中的值的值。
$string 必需。规定被搜索的字符串。
$count 可选。一个变量,对替换数进行计数。

所以我们给标题高亮的代码就应该是下面这样的:

// is_search() 只在搜索结果页 search.php 页面中才显示高亮的效果
if(is_search()){
    // 获得搜索关键字
    $key = get_search_query();
    // 使用正则替换关键字,本人正则是软肋,写得不好还要多包含			
    echo preg_replace('/('.$key.')/is','<span class="highlight">'.$key.'</span>',get_the_title());
}
else{
    the_title();
}

高亮效果的产生,主要就是给原本的字符添加了 .highlight 的高亮的样式,.highlightCSS 代码如下:

.highlight {
    background-color: #FF0;
    padding: 3px;
}

WordPress 文章摘要添加关键字高亮

摘要的关键字高亮跟标题是一样的实现原理,这里就不再赘述。给摘要实现关键字高亮的难点是需要在 WordPressget_the_content() 函数获得的内容中找到关键字第一次出现的位置,然后再在第一次出现的位置开始截取指定长度的摘要。我们结合完整的实现代码来详细讲讲具体的实现步骤:

// 完整的实现代码
<?php
if (is_search()) {
    $content = strip_tags(get_the_content());
    $content_length = mb_strlen($content, 'utf8');
    $key = get_search_query();
    $pos = mb_stripos($content, $key, 1, 'utf8');
    $excerpt_length = get_option('bluenight_excerpt_length');
    
    if ($excerpt_length == '' || intval($excerpt_length) <= 0) {
        $excerpt_length = 70;
    } else {
        $excerpt_length = intval(get_option('bluenight_excerpt_length'));
    }
    
    if ($pos) {
        if ($pos > 1) {
            if ($content_length < $excerpt_length) {
                $content = mb_substr($content, 0, $content_length - 1, 'utf8');
            } else {
                if ($content_length >= $excerpt_length) {
                    $content = mb_substr($content, $pos - 1, $excerpt_length, 'utf8');
                }
            }
        } else {
            if ($content_length < $excerpt_length) {
                $content = mb_substr($content, 0, $content_length - 1, 'utf-8');
            } else {
                if ($content_length >= $excerpt_length) {
                    $content = mb_substr($content, 0, $content_length - $pos, 'utf-8');
                }
            }
        }
        $content = '<p>'.preg_replace('/('.$key.')/is', '<span class="highlight">'.$key.'</span>', $content).new_excerpt_more().'</p>';
    } else {
        $content = '<p>'.get_the_excerpt().'</p>';
    }

    echo $content;
}
?>

第一步:strip_tags() 函数去掉内容中的 HTML 标签

是的,没有错。第一步不是搜索关键字,而是需要先使用 strip_tags() 函数去掉内容中的 HTML 标签。

strip_tags() 函数简介

strip_tags() 函数可以剥去字符串中的 HTML、XML 以及 PHP 的标签。

strip_tags($string,$allow)
参数 描述
$string 必需。规定要检查的字符串。
$allow 可选。规定允许的标签。这些标签不会被删除。

利用 strip_tags() 函数去掉 WordPress 文章内容中的 HTML 标签很容易:

$content = strip_tags(get_the_content());

第二步:mb_strlen() 获得文章内容长度

选择 mb_strlen() 函数而不是使用 strlen() 函数是因为我们的博客都是中英文混排的,strlen() 函数会把 utf-8 编码的中文字符算成3个字节长度,而 mb_strlen() 函数则会把一个中文字符做一个自己计算,这个在我们稍后的定位和获取再要长度的时候是十分有用的。

mb_strlen() 函数简介

mb_strlen($string,$encoding);
参数 描述
$string 必需。规定要检查长度的字符串。
$encoding 可选。规定计算长度的编码。如果没有填,则使用 internal character 编码。

所以文章内容的长度是怎么获得的:

$content_length = mb_strlen($content, 'utf8');

第三步:mb_stripos() 获得关键字在文章内容首次出现的位置

mb_stripos() 函数要说明一下,是在 PHP 5.2 以上版本中才出现的,如果你的服务商的 PHP 版本低于5.2这个函数是使用不了的。

使用 mb_stripos() 函数可以帮助我们解决两个问题:第一,计算关键字出现位置的时候,会对文章内容中大写英文字母只做一个计算;第二,还是针对中文的编码问题,对 utf-8 编码汉字也做一个字节计算。

mb_stripos() 函数简介

mb_stripos ($haystack , $needle, $offset,$encoding);
参数 描述
$haystack 必需。规定要搜索的目标文本。
$needle 必需。在 haystack 中要查找的字符串。
$offset 可选。$haystack 里开始搜索的位置 。
$encoding 可选。使用的字符编码名称。 如果省略了它,将使用内部字符编码。

或以想获得关键字首次出现的位置,要这么处理:

$pos = mb_stripos($content,$key,1,'utf8');

第四步:计算截取摘要的起始位置

在开始获取摘要前,我们要先计算好截取摘要的起始位置。这里就直接用代码来解释更清楚:

// 如果文章内容中包含关键字
if ($pos) {
    // 关键字的起始为大于1
    if ($pos > 1) {
	    // 正文内容很短,就从头开始显示全部正文
        if ($content_length < $excerpt_length) {
            $content = mb_substr($content, 0, $content_length - 1, 'utf8');
        } else {
		    // 正文内容够长,大于 $excerpt_length 预设的摘要长度
            if ($content_length >= $excerpt_length) {
			    // 关键字第一次显示的位置开始,到文章结束的长度超过 $excerpt_length
				// 有足够的字显示,就显示 $excerpt_length
                $content = mb_substr($content, $pos - 1, $excerpt_length, 'utf8');
            }
        }
    } else {// 内容一开始就出现了关键字了
	    // 文章正文内容小于我们预设的 $excerpt_length(BlueNight 默认的长度是 70 个字符长度),
		// 那么起始就显示全部的文章正文,所以 mb_substr() 函数截取的长度就是 $content_length - 1
        if ($content_length < $excerpt_length) {
            $content = mb_substr($content, 0, $content_length - 1, 'utf-8');
        } else {
		    // 正文内容的长度超过预设的摘要的长度,那么就从第一个字符开始,取 $excerpt_length 个长度的字符
            if ($content_length >= $excerpt_length) {
                $content = mb_substr($content, 0, $excerpt_length, 'utf-8');
            }
        }
    }
    $content = '<p>'.preg_replace('/('.$key.')/is', '<span class="highlight">'.$key.'</span>', $content).new_excerpt_more().'</p>';
} else {
    // 如果内容中没有关键字,一般是标题里有关键字,内容中没有包含
	// 这个时候就直接输出 WordPress 的摘要
    $content = '<p>'.get_the_excerpt().'</p>';
}

通过上面的算法得出的摘要文本会确保出现关键字,然后再使用 preg_replace() 替换高亮效果的,就可以显示带高亮的摘要了。

第五步:mb_substr() 函数截取摘要

计算好截取字符串的位置和字符长度后,就是使用 mb_substr() 函数在正文中截取摘要。 选择 mb_substr() 函数就是因为这个函数可以处理好 utf-8 编码汉字的字符长度,不会像 substr() 那样出现乱码问题。

mb_substr() 函数简介

mb_substr($string,$start,$length,$encoding);
参数 描述
$string 必需。规定要返回其中一部分的字符串。
start

必需。$规定在字符串的何处开始。

  • 正数 – 在字符串的指定位置开始
  • 负数 – 在从字符串结尾的指定位置开始
  • 0 – 在字符串中的第一个字符处开始
length

可选。$规定要返回的字符串长度。默认是直到字符串的结尾。

  • 正数 – 从 start 参数所在的位置返回
  • 负数 – 从字符串末端返回
$encoding 可选。使用的字符编码名称。 如果省略了它,将使用内部字符编码。

截取完摘要文本后,就要拼接成完整的带高亮的摘要内容:

$content = '<p>'.preg_replace('/('.$key.')/is', '<span class="highlight">'.$key.'</span>', $content).new_excerpt_more().'</p>';

最后要强调的就是,一定要在截取好纯文本的摘要文字后,再使用 preg_replace() 函数替换关键字,然后拼接 Read more。关于给 WordPress 加搜索关键字高亮就说到这里了,希望对大家有帮助。

算法更新 实现多关键词高亮

wordpress-search-keywords-highlighted-1

多个关键字高亮

之前单个的代码高亮使用是单个的正则表达式,仔细的看了 php.com 的 preg_replace() 函数的 API,发现只要前后对应正则数组和替换数组就可以比较完美的实现多关键词高亮。具体的细节不想再多介绍了,只是用了 preg_split() 简单的分开 WordPress 的 get_search_query() 函数获得的关键字:

preg_split('/[\s,+]+/', $keyword)

然后使用这些关键字组个匹配第一次出现的位置,把这些位置的值保存到数组,然后使用 stor() 函数排序这个纯数字的数组,之后取出最小的匹配位置。

$posArray = array();
    $replaceKeys = array();
    foreach ($keys as $keyword) {
        // 逐一匹配位置,保存到一个数组,形成一个纯数字的数组
        $posArray[] = mb_stripos($content, $keyword, 1, 'utf8');
        // 得到 replacement 的数组
        $replaceKeys[] = '<span class="highlight">'.$keyword.'</span>';
    }
    // 默认数值由小到大的排序
    sort($posArray);
    
    // 取到最小的一个匹配位置$pos,只有截取字符需要的起始位置
    for ($i = 0; $i < count($posArray); $i++) {
        if ($posArray[$i]) {
            $pos = $posArray[$i];
            break;
        }
    }

上面的代码在获得匹配位置的数组时,同时保存了替换字符的数组,接下来需要的是对应的正则表达式的数组:

$keys = preg_split('/[\s,+]+/', $key);
    
$reg_keys = array();
// 逐一遍历关键字数组
foreach($keys as $reg_key){
    // 根据关键字拼接处正则表达式的数组
    $reg_keys[] = '/('.$reg_key.')/i';
}

最后用 preg_replace() 函数匹配替换就OK了,最后给出完整的代码:

<?php
if (!function_exists('get_search_excerpt')):
function get_search_excerpt($key, $content) {
    $keys = preg_split('/[\s,+]+/', $key);
    
	$reg_keys = array();
	foreach($keys as $reg_key){
		$reg_keys[] = '/('.$reg_key.')/i';
	}
	
    $content_length = mb_strlen($content, 'utf8');
    
    $posArray = array();
    $replaceKeys = array();
    foreach ($keys as $keyword) {
        $posArray[] = mb_stripos($content, $keyword, 1, 'utf8');
        $replaceKeys[] = '<span class="highlight">'.$keyword.'</span>';
    }
    sort($posArray);
    
    for ($i = 0; $i < count($posArray); $i++) {
        if ($posArray[$i]) {
            $pos = $posArray[$i];
            break;
        }
    }
    
    $excerpt_length = get_option('bluenight_excerpt_length');
    
    if ($excerpt_length == '' || intval($excerpt_length) <= 0) {
        $excerpt_length = 70;
    } else {
        $excerpt_length = intval(get_option('bluenight_excerpt_length'));
    }
    
    if ($pos) {
        if ($pos > 1) {
            if ($content_length < $excerpt_length) {
                $content = mb_substr($content, 0, $content_length - 1, 'utf8');
            } else {
                if ($content_length >= $excerpt_length) {
                    $content = mb_substr($content, $pos - 1, $excerpt_length, 'utf8');
                }
            }
        } else {
            if ($content_length < $excerpt_length) {
                $content = mb_substr($content, 0, $content_length - 1, 'utf-8');
            } else {
                if ($content_length >= $excerpt_length) {
                    $content = mb_substr($content, 0, $excerpt_length, 'utf-8');
                }
            }
        }
        
        $content = '<p>'.preg_replace($reg_keys, $replaceKeys, $content).new_excerpt_more().'</p>';
    } else {
        $content = '<p>'.get_the_excerpt().'</p>';
    }
    
    return $content;
}
endif;
?>

不过还有需要解决的是 WordPress 的搜索的缺陷,”JavaScript WordPress” 这样的关键词的搜索结果居然没有“JavaScript”的结果多,看来 WordPress 只是简单的 like 搜索的。

声明:本文采用BY-NC-SA协议进行授权。转载请注明转自:使用 preg_replace() 函数为 WordPress 添加搜索关键字高亮

« »

4 条评论

  • 有个叫WP MarkKeyword 的插件 可以对搜索引擎来源的关键词高亮显示 wordpress的搜索真心不大好用。。

    • 看了他的代码,基本的替换跟我的实现方式一样,也比较简单。google什么的高亮是代理请求的数据。我改后的代码,这个插件基本上起不了作用。

    • 嗯,我看也只好借助这样的插件来帮忙了,不过我刚刚试了一下,不知道是我改了搜索显示的原因,还是WP升级到3.5,这个插件没有看到效果。

  • 下午折腾了一下,可以实现了多关键字同时高亮了,当然还是比较初级。不过发现 WordPress 的搜索居然搜索“JavaScript”得到的文章数比搜索“JavaScript WordPress”的这样双关键字的要少,无耐,发现我白折腾了!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(Spamcheck Enabled)