Conditional Excerpt Function
I use this function on practically every site that has any posts/archives section. This enables us to display a standardized excerpt inside or outside the Loop.
Arguments (all optional):
$ex_length the word count
$postref the post or text to excerpt. Can be an ID, a post object, a block of text, or if called within the Loop, null or not given
$addmore_keyThe text/html to display when there is additional content. I like to set this to something unique (like the default ‘<!–wpcx_more–>’) and str_replace within the caller instance.
$include_p Whether to include paragraphs within the excerpt
$user_break If false, return no more than the maximum word count; if true, use the entire user-defined excerpt or user-defined ‘<!–more–>’ break
$apply_shortcodes True to apply shortcode formatting within the excerpt
Note: this function always retains b, strong, i and em tags.
View comments within code for detailed explanations.
function conditional_excerpt( $ex_length=80, $postref=null, $addmore_key='<!--wpcx_more-->', $include_p=false, $user_break=false, $apply_shortcodes=false ) {
// retrieve or build post object from $postref input (can be
// post object, ID, text, or current loop post if not defined/null)
// check if whole $post object was passed
if ( is_object( $postref ) ) :
$postob = $postref;
// check if it's a post ID ( integer or string-format number ) :
elseif ( is_numeric( $postref ) ) :
$postob = get_post( $postref );
// otherwise assume it's text, define a minimal object with just the properties we need:
elseif ( is_string( $postref ) && !empty( $postref ) ) :
$postob = ( object )array(
'ID' => 0,
'post_content' => $postref
);
// finally, if empty / null / not defined, assume we're in The Loop:
elseif ( empty( $postref ) ) :
$postob = get_post( get_the_ID() );
endif;
// bail if none of the above tests passed:
if ( !is_object( $postob ) ) return;
// now, check if there is a user-defined excerpt, and act upon it
if ( $postob->ID && ! empty( $postob->post_excerpt ) ) :
$featex = $postob->post_excerpt;
$addmore = $addmore_key;
if ( $user_break ) :
return $featex.$addmore;
endif;
// no user-defined excerpt, so construct it
else :
$addmore = '';
// strip out any javascript
$stripped = preg_replace( '@<script[^>]*?>.*?</script>@si', '', $postob->post_content );
// strip out shortcodes if dictated by $apply_shortcodes:
if ( !$apply_shortcodes ) {
$stripped = strip_shortcodes( $stripped );
}
// if '<!--more-->' is not there ( user did not specify a break ),
// $content_split array will only contain one item.
$content_split = explode( '<!--more-->', $stripped );
$featex = $content_split[0];
if ( count( $content_split ) > 1 ) :
$addmore = $addmore_key;
endif;
// this is the recommended way to convert raw post_content;
// shortcodes will be applied herein if not stripped out earlier:
$featex = apply_filters( 'the_content', $featex );
$featex = str_replace( ']]>', ']]>', $featex );
// prepare argument for strip_tags() (standard PHP function)
$exclude_open = '<b><i><em><strong><a>';
if ( $include_p ) :
$exclude_open .= '<p><br><h1><h2><h3><h4>';
endif;
$featex = strip_tags( $featex, $exclude_open );
// will append closing tags to excerpt so any that have been
// truncated will be properly closed
$exclude_close = '</b></i></em></strong></a>';
if ( $include_p ) :
$exclude_close .= '</p></h1></h2></h3></h4>';
endif;
endif;
if ( !$user_break || count( $content_split ) < 2 ) :
$words = explode( ' ', $featex, $ex_length + 1 );
if ( count( $words ) > $ex_length ) :
array_pop( $words );
$featex = implode( ' ', $words );
$addmore = $addmore_key;
endif;
endif;
return $featex . $exclude_close . $addmore;
}
Coders note:
Lines 29 & 30: Why did I test ! empty( $postob->post_excerpt ), and then set $featex = $postob->post_excerpt;? Wouldn’t it be better to use the WordPress functions has_excerpt() and get_the_excerpt()?
No, for two reasons:
- This function can become part of a filter so all excerpts are structured this way (see below), hooked into get_the_excerpt — which is applied to the output of the get_the_excerpt() function. If I used get_the_excerpt() herein, I’d create an infinite recursion.
- Looking at the core code, has_excerpt() goes to the database for the post object, then simply returns the ->post_excerpt property if it exists. Similarly, get_the_excerpt() simply returns the ->post_excerpt property. Since I already have a post object, why increase overhead with another database call?
How to use it:
Make sure this function is in your functions.php file. Then, you can edit / create your theme files to call this function wherever you see the_excerpt() or get_the_excerpt() (likely archive.php, category.php, search.php and other ‘list’ templates).
Or, hook into the get_the_excerpt filter, which is encountered by both the_excerpt() and get_the_excerpt(), always within The Loop. Add this to your functions.php file.
add_filter( 'get_the_excerpt', 'wpcx_excerpt_filter' );
function wpcx_excerpt_filter( $excerpt ) {
return conditional_excerpt( 50 ); // ... or your preferred word count
}