Skip to Content

excerpt()

A smart excerpt function that allows customization per instance (rather than globally across the whole website).

PHP April 7, 2021

Usage

PHP
nebula()->excerpt($options)

Parameters

$options
(Optional) (Array) An array of options.
Default: See below

Parameter Notes

id
An ID to pull the text from. This can be passed as a string or int. A WP_Post object could also be passed here instead.
Default: Current post ID

text
The text to shorten. If an ID is passed this uses that post’s excerpt or content.
Default: Post excerpt/content

characters
The maximum number of characters.
Default: false

words
The maximum length of the text in words.
Default: 55

length
Used for dynamic length by passing “dynamic”, otherwise an alias of “words”.
Default: false

min
The minimum length of a dynamic sentence.
Default: 0

ellipsis
Whether or not to add an ellipsis to shorten text.
Default: false

url
The URL to link to. If the “more” option is set (including default value), the link uses the permalink of the post.
Default: Post permalink

wp_more
Listen for the <!--more--> tag within the post. This tag can override the more parameter by using: <!--more Keep Reading-->
Default: true

more
The text that is linked to the URL after the excerpt.
Default: “Read More &raquo;”

button
Turn the “more” text into a button (rather than just a link).
Default: false

strip_shortcodes
Whether to strip WordPress shortcodes from the excerpt.
Default: true

strip_tags
Whether to strip HTML tags from the excerpt.
Default: true

wrap_links
Wrap URLs in <a> tags.
Default: false

Request or provide clarification »

Examples

Inside the loop (or outside the loop for current post/page)

PHP
<?php echo nebula()->excerpt(array('length' => 20, 'ellipsis' => true)); ?>

Outside the loop

PHP
<?php echo nebula()->excerpt(array('id' => 572, 'length' => 20, 'ellipsis' => true)); ?>

Custom excerpt

PHP
<?php echo nebula()->excerpt(array('text' => 'Lorem ipsum <strong>dolor</strong> sit amet.', 'more' => 'Continue &raquo;', 'length' => 3, 'ellipsis' => true, 'strip_tags' => true)); ?>

Using an ACF field

PHP
<?php echo nebula()->excerpt(array('text' => get_field('some_text_field', 33), 'length' => 50, 'ellipsis' => false, 'more' => false)); ?>

Use as a meta description ending with a sentence

PHP
<?php echo nebula()->excerpt(array('length' => 'dynamic', 'characters' => 160, 'more' => '', 'ellipsis' => false, 'strip_tags' => true)); ?>

Source File

Located in /libs/Functions.php on line 730.

2 Hooks

Find these filters and actions in the source code below to hook into them. Use do_action() and add_filter() in your functions file or plugin.

Filters
"pre_nebula_excerpt"
"nebula_excerpt_defaults"
Need a new filter hook? Request one here.

Actions
This function has no action hooks available. Request one?

Note: This function contains 1 to-do comment.

PHP
        public function excerpt($options=array()){
            $override = apply_filters('pre_nebula_excerpt', null, $options);
            if ( isset($override) ){return $override;}

            $defaults = apply_filters('nebula_excerpt_defaults', array(
                'id' => false,
                'text' => false,
                'paragraphs' => false, //Allow paragraph tags in the excerpt //@todo "Nebula" 0: currently not working
                'characters' => false,
                'words' => get_theme_mod('nebula_excerpt_length', 55),
                'length' => false, //Used for dynamic length, otherwise an alias of "words"
                'min' => 0, //Minimum length of dynamic sentence
                'ellipsis' => false,
                'url' => false,
                'more' => get_theme_mod('nebula_excerpt_more_text', __('Read More', 'nebula') . ' &raquo;'),
                'wp_more' => true, //Listen for the WP more tag
                'btn' => false, //Alias of "button"
                'button' => false,
                'strip_shortcodes' => true,
                'strip_tags' => true,
                'wrap_links' => false,
                'shorten_urls' => false, //Currently only works with wrap_links
            ));

            $data = array_merge($defaults, $options);

            //Establish text
            if ( empty($data['text']) ){
                if ( !empty($data['id']) ){
                    if ( is_object($data['id']) && get_class($data['id']) == 'WP_Post' ){ //If we already have a WP_Post class object
                        $the_post = $data['id'];
                    } elseif ( intval($data['id']) ){ //If an ID is passed
                        $the_post = get_post(intval($data['id']));
                    }
                } else {
                    $the_post = get_post(get_the_ID());
                }

                if ( empty($the_post) ){
                    return false;
                }

                if ( !empty($the_post->post_excerpt) ){
                    $data['text'] = $the_post->post_excerpt;
                } else {
                    $data['text'] = $the_post->post_content;
                    if ( $data['wp_more'] ){
                        $wp_more_split = get_extended($the_post->post_content); //Split the content on the WordPress <!--more--> tag
                        $data['text'] = $wp_more_split['main'];

                        if ( preg_match('/<!--more(.*?)?-->/', $the_post->post_content, $matches) ){ //Get the custom <!--more Keep Reading--> text. RegEx from: https://core.trac.wordpress.org/browser/tags/4.8/src/wp-includes/post-template.php#L288
                            if ( !empty($matches[1]) ){
                                $data['more'] = strip_tags(wp_kses_no_null(trim($matches[1])));
                            }
                        }
                    }
                }
            }

            //Strip Newlines
            $data['text'] = str_replace(array("\r\n", "\r", "\n"), " ", $data['text']); //Replace newline characters (keep double quotes)
            $data['text'] = preg_replace('/\s+/', ' ', $data['text']); //Replace multiple spaces with single space

            //Strip Shortcodes
            if ( $data['strip_shortcodes'] ){
                $data['text'] = strip_shortcodes($data['text']);
            } else {
                $data['text'] = preg_replace('~(?:\[/?)[^/\]]+/?\]~s', ' ', $data['text']);
            }

            //Strip Tags
            if ( $data['strip_tags'] ){
                $allowable_tags = ( !empty($data['paragraphs']) )? 'p' : '';
                $data['text'] = strip_tags($data['text'], $allowable_tags);
            }

            //Apply string limiters (words or characters)
            if ( !empty($data['characters']) && intval($data['characters']) ){ //Characters
                $limited = $this->string_limit_chars($data['text'], intval($data['characters'])); //Returns array: $limited['text'] is the string, $limited['is_limited'] is boolean if it was limited or not.
                $data['text'] = trim($limited['text']);
            } elseif ( (!empty($data['words']) && intval($data['words'])) || (!empty($data['length']) && intval($data['length'])) ){ //Words (or Length)
                $word_limit = ( !empty($data['length']) && intval($data['length']) )? intval($data['length']) : intval($data['words']);
                $limited = $this->string_limit_words($data['text'], $word_limit); //Returns array: $limited['text'] is the string, $limited['is_limited'] is boolean if it was limited or not.
                $data['text'] = trim($limited['text']);
            }

            //Apply dynamic sentence length limiter
            if ( $data['length'] === 'dynamic' ){
                $last_punctuation = -1;
                foreach ( array('.', '?', '!') as $punctuation ){
                    if ( strrpos($data['text'] . ' ', $punctuation . ' ') ){
                        $this_punctuation = strrpos($data['text'] . ' ', $punctuation . ' ')+1; //Find the last punctuation (add a space to the end of the string in case it already ends at the punctuation). Add 1 to capture the punctuation, too.

                        if ( $this_punctuation > $last_punctuation ){
                            $last_punctuation = $this_punctuation;
                        }
                    }
                }

                if ( $last_punctuation >= $data['min'] ){
                    $data['text'] = substr($data['text'], 0, $last_punctuation); //Remove everything after the last punctuation in the string.
                }
            }

            //Check here for links to wrap
            if ( $data['wrap_links'] ){
                $data['text'] = preg_replace('/(\(?(?:(http|https|ftp):\/\/)?(?:((?:[^\W\s]|\.|-|[:]{1})+)@{1})?((?:www.)?(?:[^\W\s]|\.|-)+[\.][^\W\s]{2,4}|localhost(?=\/)|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?::(\d*))?([\/]?[^\s\?]*[\/]{1})*(?:\/?([^\s\n\?\[\]\{\}\#]*(?:(?=\.)){1}|[^\s\n\?\[\]\{\}\.\#]*)?([\.]{1}[^\s\?\#]*)?)?(?:\?{1}([^\s\n\#\[\]]*))?([\#][^\s\n]*)?\)?)(?![^<]*<\/)/i', '<a class="nebula-excerpt-url" href="$1">$1</a>', $data['text']); //Capture any URL not within < and </ using a negative lookahead (so it plays nice in case strip_tags is false)
            }

            //Shorten visible URL text
            if ( $data['shorten_urls'] ){
                $data['text'] = preg_replace_callback('/(<a.+>)(.+)(<\/a>)/', function($matches){
                    $output = $matches[1];
                    if ( strlen($matches[2]) > 20 ){
                        $short_url = str_replace(array('http://', 'https://'), '', $matches[2]);
                        $url_directories = explode('/', $short_url);
                        $short_url = $url_directories[0];
                        if ( count($url_directories) > 1 ){
                            $short_url .= '/...';
                        }
                        $output .= $short_url;
                    } else {
                        $output .= $matches[2];
                    }
                    $output .= $matches[3];
                    return $output;
                }, $data['text']);
            }

            //Ellipsis
            if ( $data['ellipsis'] && !empty($limited['is_limited']) ){
                $data['text'] .= '&hellip;';
            }

            //Link
            if ( !empty($data['more']) ){
                if ( empty($data['url']) ){ //If has "more" text, but no link URL
                    $data['url'] = ( !empty($data['id']) )? get_permalink($data['id']) : get_permalink(get_the_id()); //Use the ID if available, or use the current ID.
                }

                //Button
                $btn_class = '';
                if ( $data['button'] || $data['btn'] ){
                    $button = ( $data['button'] )? $data['button'] : $data['btn'];
                    $btn_class = ( is_bool($button) )? 'btn btn-brand' : 'btn ' . $data['button'];

                    $data['text'] .= '<br /><br />';
                }

                $data['text'] .= ' <a class="nebula_excerpt ' . $btn_class . '" href="' . $data['url'] . '">' . $data['more'] . '</a>';
            }

            return $data['text'];
        }

Override

To override this PHP function, use this hook in your child theme or plugin ("my_custom" can be changed):

PHP
add_filter('pre_nebula_excerpt', 'my_custom_excerpt', 10, 2); //The last integer must be 1 more than the actual parameters
function my_custom_excerpt($null, $options){ //$null is required, but can be ignored
    //Write your own code here

    return true; //Return true to prevent the original function from running afterwords
}

You can completely disable this PHP function with a single line actions:

PHP
 add_filter('pre_nebula_excerpt', '__return_false');