Skip to Content

excerpt()

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

PHP April 25, 2018

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 685.

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');