Working with excerpts in WordPress, or why isn’t this filter working?

When is an excerpt not an excerpt?

If you call get_the_excerpt() WordPress returns a string that looks like an excerpt – but it might not be. If your post doesn’t have a handcrafted excerpt, WordPress returns an automatically generated word-counted trimmed-down version of the full post content (see codex).

TL/DR: WordPress returns a string for get_the_excerpt() even when has_excerpt() is FALSE.

I’d used this bit of code to automatically add a ‘read more’ link at the end of every excerpt on a search page.

function ds_excerpt_read_more( $output ) {
 
	$read_more_link = ' (<a class="more-link" href="' . get_permalink() . '">Read more</a>)';
 
	if ( has_excerpt() && ! is_attachment() & is_search() ) {
 
		$output .= " " . $read_more_link;
 
	}
	return $output;
}
add_filter( 'get_the_excerpt', 'ds_excerpt_read_more'  );

Problem was, it wasn’t working everywhere – some posts didn’t have the modified excerpt.

has_excerpt() was the problem – it only returns TRUE if there is a manual excerpt.

So this works better:

function ds_excerpt_read_more( $output ) {
 
	$read_more_link = ' (<a class="more-link" href="' . get_permalink() . '">Read more</a>)';
 
	if ( has_excerpt() && ! is_attachment() & is_search() ) {
 
		$output .= " " . $read_more_link;
 
	} elseif (is_search() &&  ! has_excerpt() )  {
 
		$excerpt_length = apply_filters('excerpt_length', 55);
		$output = wp_trim_words( $output, $excerpt_length, '' );
		$output .= " " . $read_more_link;
 
	}
	return $output;
}
add_filter( 'get_the_excerpt', 'ds_excerpt_read_more'  );

but this is best:

function ds_excerpt_read_more( $output ) {
 
	if (  is_search() && ! is_attachment() ) {
 
		$read_more_link = ' (<a class="more-link" href="' . get_permalink() . '">Read more</a>)';
 
		if ( ! has_excerpt() ) {
 
			$excerpt_length = apply_filters('excerpt_length', 55);
			$output = wp_trim_words( $output, $excerpt_length, '' );
 
			/* Finds the last full stop in the excerpt, removes everything after it. */
			if ( ( $pos = mb_strrpos( $output, '.' ) ) !== false ) {
				$output = substr( $output, 0, $pos + 2 );
			}
		}
 
		/* Appends a 'read more' link at the end of the excerpt - if it's a search */
		$output .= " " . $read_more_link;
 
	}
	return $output;
}
add_filter( 'get_the_excerpt', 'ds_excerpt_read_more'  );