I’ve used code like this, based (extremely) loosely on the plugin relevanssi: (in “functions.php”)
// Roughly based on the relevanssi_do_excerpt() (https://wordpress.org/plugins/relevanssi/, GPLv2 or later), seriously simplified.
function mytheme_search_context( $haystack, $needles, $len = 30 ) {
$ret="";
$haystack = strip_tags(mytheme_strip_invisibles($haystack));
$haystack = trim( preg_replace( array( "/\n\r|\r\n|\n|\r/", '/\s\s+/', '/&.{1,30}?;/' ), ' ', $haystack ) );
$words = explode( ' ', $haystack );
for ( $i = 0, $cnt = count( $words ); $i < $cnt; $i += $len ) {
$slice = array_slice( $words, $i, $len );
$slice=" " . implode( ' ', $slice );
$search = $replace = array();
foreach ( $needles as $needle ) {
$pos = mb_stripos( $slice, $needle );
if ( $pos !== false ) {
$target = mb_substr( $slice, $pos, mb_strlen( $needle ) );
$search[] = $target;
$replace[] = '<strong>' . $target . '</strong>';
}
}
if ( $search ) {
if ( $i ) {
$ret .= '… ';
}
if ( $i + $len > $cnt ) {
$slice = array_slice( $words, $cnt - $len, $len );
$slice=" " . implode( ' ', $slice );
$ret .= str_replace( $search, $replace, $slice );
} else {
$ret .= str_replace( $search, $replace, $slice ) . ' …';
}
}
}
return $ret;
}
// Straight copy of relevanssi_strip_invisibles().
// found here: http://forums.digitalpoint.com/showthread.php?t=1106745
function mytheme_strip_invisibles( $text ) {
$text = preg_replace(
array(
'@<style[^>]*?>.*?</style>@siu',
'@<script[^>]*?.*?</script>@siu',
'@<object[^>]*?.*?</object>@siu',
'@<embed[^>]*?.*?</embed>@siu',
'@<applet[^>]*?.*?</applet>@siu',
'@<noscript[^>]*?.*?</noscript>@siu',
'@<noembed[^>]*?.*?</noembed>@siu',
'@<iframe[^>]*?.*?</iframe>@siu',
'@<del[^>]*?.*?</del>@siu',
),
' ',
$text );
return $text;
}
Then depending on your theme (you’ve probably already done this), copy “search.php” to your child theme, and change the line (if it’s there) <?php get_template_part( 'content', get_post_format() ); ?>
to <?php get_template_part( 'content', 'search' ); ?>
, and then copy the theme’s “content.php” to “content-search.php” in the child theme, and replace the line (or equivalent) the_extract();
with something like
if ( $mytheme_search_context = mytheme_search_context( do_shortcode( get_the_content() ), get_query_var( 'search_terms' ) ) ) {
echo '<p>', $mytheme_search_context, '</p>';
} elseif ( $get_the_excerpt = get_the_excerpt() ) {
echo '<p>', strip_shortcodes( $get_the_excerpt ), '</p>';
}