Using wp_trim_excerpt to get the_excerpt() outside the loop

I’m building a theme that is going to show excerpts on the homepage for potentially dozens of posts. I don’t have manual excerpts on all of my posts, so $post->post_excerpt is empty for many posts. In the event that there isn’t a manual excerpt, I would like to use the built-in get_the_excerpt() function, but it isn’t available outside the loop.

Tracking down the function, it looks like it uses wp_trim_excerpt from wp-includes/formatting.php to create excerpts on the fly. I am calling it in my code like wp_trim_excerpt( $item->post_content ), but it is simply returning the full content. Am I doing something wrong?

Read More

I know that I can create my own function to create an excerpt, but I like to use built-in functions where possible, keeping my code compatible with other potential plugins / filters.

http://adambrown.info/p/wp_hooks/hook/wp_trim_excerpt?version=3.0&file=wp-includes/formatting.php

Related posts

Leave a Reply

7 comments

  1. wp_trim_excerpt() has a little curious mechanics – if anything is passed to it then it does nothing.

    Here is basic logic behind it:

    • get_the_excerpt() checks for manual excerpt;
    • wp_trim_excerpt() chimes in if there is no manual excerpt and makes one from content or teaser.

    Both are tightly tied to global variables and so Loop.

    Outside the Loop you are better of taking code out of wp_trim_excerpt() and writing your own trim function.

  2. Update:

    Here is a derivative of wp_trim_excerpt() which I used. Works perfectly. Derived from WordPress version 3.0.4

    function my_excerpt($text, $excerpt)
    {
        if ($excerpt) return $excerpt;
    
        $text = strip_shortcodes( $text );
    
        $text = apply_filters('the_content', $text);
        $text = str_replace(']]>', ']]>', $text);
        $text = strip_tags($text);
        $excerpt_length = apply_filters('excerpt_length', 55);
        $excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
        $words = preg_split("/[nrt ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
        if ( count($words) > $excerpt_length ) {
                array_pop($words);
                $text = implode(' ', $words);
                $text = $text . $excerpt_more;
        } else {
                $text = implode(' ', $words);
        }
    
        return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
    }
    
  3. Here’s my take on a “trim_excerpt” that takes the post object or a post ID as a parameter.

    Obviously based on what’s in core. Don’t know why this (and get_the_author()) don’t have non-loop equivalents.

    /**
         * Generates an excerpt from the content, if needed.
         *
         * @param int|object $post_or_id can be the post ID, or the actual $post object itself
         * @param string $excerpt_more the text that is applied to the end of the excerpt if we algorithically snip it
         * @return string the snipped excerpt or the manual excerpt if it exists         
         */
        function zg_trim_excerpt($post_or_id, $excerpt_more = ' [...]') {
            if ( is_object( $post_or_id ) ) $postObj = $post_or_id;
            else $postObj = get_post($post_or_id);
    
            $raw_excerpt = $text = $postObj->post_excerpt;
            if ( '' == $text ) {
                $text = $postObj->post_content;
    
                $text = strip_shortcodes( $text );
    
                $text = apply_filters('the_content', $text);
                $text = str_replace(']]>', ']]>', $text);
                $text = strip_tags($text);
                $excerpt_length = apply_filters('excerpt_length', 55);
    
                // don't automatically assume we will be using the global "read more" link provided by the theme
                // $excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
                $words = preg_split("/[nrt ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
                if ( count($words) > $excerpt_length ) {
                    array_pop($words);
                    $text = implode(' ', $words);
                    $text = $text . $excerpt_more;
                } else {
                    $text = implode(' ', $words);
                }
            }
            return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
        }
    
  4. +1 to Rast. It is very weird that there is no such thing as get_the_excerpt($post->ID), when it should be quite obvious that it should. Anyway, here is wp_trim_excerpt() in wordpress version 3.0.4:

    http://core.trac.wordpress.org/browser/tags/3.0.4/wp-includes/formatting.php

    function wp_trim_excerpt($text) {
    1824            $raw_excerpt = $text;
    1825            if ( '' == $text ) {
    1826                    $text = get_the_content('');
    1827    
    1828                    $text = strip_shortcodes( $text );
    1829    
    1830                    $text = apply_filters('the_content', $text);
    1831                    $text = str_replace(']]>', ']]>', $text);
    1832                    $text = strip_tags($text);
    1833                    $excerpt_length = apply_filters('excerpt_length', 55);
    1834                    $excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
    1835                    $words = preg_split("/[nrt ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
    1836                    if ( count($words) > $excerpt_length ) {
    1837                            array_pop($words);
    1838                            $text = implode(' ', $words);
    1839                            $text = $text . $excerpt_more;
    1840                    } else {
    1841                            $text = implode(' ', $words);
    1842                    }
    1843            }
    1844            return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
    1845    }
    

    You can see on line 1826 that it is linked to the $post global variable via get_the_contents. And yes, I have no idea what were they thinking. But from here, replace the get_the_content with $text in your own my_excerpt, and it should behave in a similar fashion.

  5. The get_the_content() function would return full content if $more != 0.
    You have to set global variable $more to 0 to make sure get_the_content() function return excerpt.

    Modified wp_trim_excerpt() function:

    function wp_trim_excerpt($text) {
        $raw_excerpt = $text;
        if ( '' == $text ) {
            global $more;
            $tmp = $more;
            $more = 0;
            $text = get_the_content('');
            $more = $tmp;
    
            $text = strip_shortcodes( $text );
    
            $text = apply_filters('the_content', $text);
            $text = str_replace(']]>', ']]>', $text);
            $text = strip_tags($text);
            $excerpt_length = apply_filters('excerpt_length', 55);
            $excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
            $words = preg_split("/[nrt ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
            if ( count($words) > $excerpt_length ) {
                array_pop($words);
                $text = implode(' ', $words);
                $text = $text . $excerpt_more;
            } else {
                $text = implode(' ', $words);
            }
        }
        return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
    }
    
  6. Using others’ answers above, here’s a simpler answer that seems to work well:

    global $post;
    
    $excerpt = apply_filters('get_the_excerpt', get_post_field('post_excerpt', $post->ID));
    
    if ( $excerpt == '' ) {
        $excerpt = wp_trim_words( $post->post_content, 55 );
    }
    

    I’m using it in the <meta> tags in a function to define OpenGraph descriptions. So then I just add:

    <meta property="og:description" content="<?php echo esc_html( $excerpt ); ?>" />