Changing WordPress Excerpt Length: Learning Through Spelunking

cave | wordpress spelunking

One of the more common tasks for a novice designer or developer working on WordPress to do is to change the length of the_excerpt(). It’s so common, in fact, that if that was all this article was aiming to do I’d feel silly.

So I want to take that concept, and after showing how you specifically do it in WordPress, zoom out into what it means about WordPress and working effectively as a WordPress developer.

We’ll head through three steps:

  1. How to modify the excerpt length,
  2. Why you were able to modify the excerpt length, and
  3. What that has to teach you about WordPress code more generally.

How you Actually Change the Length of WordPress Excerpts

Most of these tutorials will tell you. Even the Codex will tell you, there’s not much to it really:

function custom_excerpt_length( $length ) {
	return 20;
add_filter( 'excerpt_length', 'custom_excerpt_length', 999 );

Basically, you create a function. In this code I brazenly stole from the Codex, it’s called custom_excerpt_length, and it returns the value 20.

Then you just use this magical add_filter call, with the first argument being 'excerpt_length', the second being a string of our function’s name — 'custom_excerpt_length' — and the third being a mostly arbitrary but large value.

Quick Aside on Function Priority

Operation_Upshot-Knothole_-_Badger_001Many tutorials (and the Codex when I accessed it) are making a claim that you need to make that third priority value really large, like 999. You don’t. If you’re the only one hooking on 'excerpt_length' on your WordPress site, a default value of 10 will be fine. If not, 11 will probably be sufficient. Values like 999 or 9999 are people reaching for the nuclear option when they don’t know who or what passed what priority. (Though if somone else went nuclear, you’ll have to go even more nuclear…)

Now here’s where I want to go a bit deeper than most tutorials. We’ve covered how we accomplish our goal, but not said anything about how the whole thing actually works—at this point, it’s magic to us. Let’s dig a bit deeper.

But How Did That Actually Make Something Happen?

So let’s consider again that line, but with a little more abstract perspective. Let’s be aliens!

What is this add_filter business, anyway?

Well there appears to be some add_filter function/system in existence. And it seems to have a known entity by the name of excerpt_length. That entity, in turn, seems to be a value which takes our created function and (because something happened) uses its returned value. And that whole system seems to have some sort of priority system, as we were told we need to have a high value as the third argument.

Indeed if we dive into WordPress’s source, we can find the add_filter function. And it looks like this:

function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
        global $wp_filter, $merged_filters;

       $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
      $wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
      unset( $merged_filters[ $tag ] );
      return true;

For the sake of this not being a 5000+ word article, we’ll mostly leave aside the implementation of that function, but it clearly does seem to take those arguments we thought, along with one more than we noticed. By default WordPress will assume that our add_filtered function will receive one value, but we can make it more.

How the the_excerpt function actually works

So now that we understand what we told WordPress to do, lets go from the other end. When we want to display an excerpted version of a post, we call a function called the_excerpt(), very simply, inside the Loop. What does that function do? Well, it’s pretty simple really:

function the_excerpt() {
   echo apply_filters( 'the_excerpt', get_the_excerpt() );

If you’re used to WordPress, this makes sense. It’s just taking the value of get_the_excerpt() and echoing it. But before that, it’s doing this strange apply_filters thing. What’s that? Well, it’s probably related to that add_filter thing. In fact, it’d make a lot of sense if it just applied the filters we worked with earlier…

And indeed it does. (Again leaving out how for sanity’s sake.) What’s important, relative to the filter “tag” name we’re using here, is that we’ve not yet found where the excerpt_length filter we’re using is.

Your first impulse might be to follow down the chain of get_the_excerpt itself. But what you’ll find there is pretty thin. But it too ends with an apply_filters(). Going further than that, you’ll quickly get lost. So I’m going to make a jump…

WordPress Has Some Default Filters…

coffee-filter-funOne of the important, quite relevant, and non-obvious things about WordPress is that there are some things hooked onto core WordPress events out of the box. And they’re our next leap in understanding the anatomy of what we’re doing when we change the excerpt length.

If you’ve never looked at them, you can find all the default filter uses in WordPress core in /wp-includes/default-filters.php. And right around line 140, you’ll find a block like this:

add_filter( 'the_excerpt',     'wptexturize'      );
add_filter( 'the_excerpt',     'convert_smilies'  );
add_filter( 'the_excerpt',     'convert_chars'    );
add_filter( 'the_excerpt',     'wpautop'          );
add_filter( 'the_excerpt',     'shortcode_unautop');
add_filter( 'get_the_excerpt', 'wp_trim_excerpt'  );

We still haven’t found where this 'excerpt_length' guy we’re looking for. But I promise we’re getting closer…

But first, if you’re not familiar with WordPress, you may be scratching your heads about a few of those filters. A very short summary of the each:

  • wptexturize does a lot of pretty-ing up of text typing. Things like making sure your keystrokes match typographic standards with curly quotes and more.
  • convert_smilies changes your basic keyboarded faces into real images, if you’ve got that option on.
  • convert_chars takes care of some strange characters and other special-cases that wptexturize doesn’t.
  • wpautop converts line breaks — stored in the WordPress database as raw new lines, \n — into HTML markup (mostly <p> tags) for display in your browser.
  • shortcode_unautop makes sure that none of that paragraphing added by wpautop are going to mess up shortcodes (I’m not saying it makes a lot of sense).

What’s Up With wp_trim_excerpt?

Finally, it’s the last one where we’ll find our excerpt_length named. wp_trim_excerpt does just about exactly what you’d expect from the name — it trims your post into a excerpt of the expected length. And it looks a little like this:

// /wp-includes/formatting.php 
function wp_trim_excerpt($text = '') {
	$raw_excerpt = $text;
	if ( '' == $text ) {
		$text = get_the_content('');

		$text = strip_shortcodes( $text );

		$text = apply_filters( 'the_content', $text );
		$text = str_replace(']]>', ']]&gt;', $text);

		$excerpt_length = apply_filters( 'excerpt_length', 55 );
		$excerpt_more = apply_filters( 'excerpt_more', ' ' . '[…]' );
		$text = wp_trim_words( $text, $excerpt_length, $excerpt_more );
	return apply_filters( 'wp_trim_excerpt', $text, $raw_excerpt );

The line we were looking for, where that add_filter('excerpt_length', …) from the very top of this whole story finally kicks into action right here in the middle of that. We see that the apply_filters for the 'excerpt_length' default to a value of 55, and our snippet above changed it to 20. That makes sense.

This code reveals a number of other more interesting details as well. Like:

  • The the_content filtering is done to excerpts. This isn’t too surprising, and if you’ve been fiddling with WordPress text for long you probably already knew it.
  • You can, just like we did with our except length, change the default way WordPress shows that it trimmed an excerpt. The default’s an ellipsis inside of square brackets ([…]). To change it you’ll use the 'excerpt_more' tag to hook.
  • If you wanted to change the way the excerpt works with more control — say with a character rather than word count, or make it break at a paragraph, etc — you’d actually want to hook into the wp_trim_excerpt filter itself. An example’s below.

Changing Excerpt Length to Break After the First Paragraph

At this point, you get super powers.

As the last bullet above mentions. If you’re able to follow all of these things, you get super powers. Like the ability to make the_excerpt() give you the first full paragraph. That’d look something like:

add_filter( 'wp_trim_excerpt', 'wpshout_excerpt');
function wpshout_excerpt($text = '') {
	$raw_excerpt = $text;
	if ( '' == $text ) {
		$text = get_the_content('');

		$text = strip_shortcodes( $text );

		$text = apply_filters( 'the_content', $text );
		$text = str_replace(']]>', ']]&gt;', $text);

		$text = substr( $text, 0, strpos( $text, '</p>' ) + 4 );
	return $text;

What’s it doing? Well essentially the same thing as the native wp_trim_excerpt function, but instead of breaking at a word boundary, it’s breaking at the first closing paragraph tag it finds. The PHP functions are a bit terse, but essentially the function returns the “substring” of the $text variable from the start — 0 — to the position right after the closing paragraph tag.

If you’re getting really fancy, the first paragraph break after, say 400, characters is an interesting little PHP exercise, and can make your site look better too. But we’ll leave that as an exercise to the reader.

What This All Means

You can go spelunking in the WordPress source, and find answers there that you can’t find on the internet.

It’s hard for me to distill this winding journey to a single point. The core thing is that you can go spelunking in the WordPress source whenever you’d like — that’s why we love open source — and you’ll probably find answers there that you can’t find on the internet. Finding out how to make the WordPress excerpt break after the first paragraph with a search engine isn’t impossible, but the journey through the heart of WordPress is way more informative.

Another core lesson is that event-driven programming, which WordPress hooks-system of actions and filters is an example of, is really powerful and makes it easy to reach into things. We just needed a small four-line snippet (of which only two really mattered) to change the number of words in an except.

Event-driven programming systems (like WordPress) can be hard to debug, or even follow.

But we also touched on how event-driven systems can be hard to debug. Or follow all the way through the code. Without knowing that the default filters exist, you might spend hours looking through plugins and themes and never find some effect you’re hoping to change. Because event-driving systems are hard to debug and reason about, you’ll often find people resort to hacks, like the priority of 999 we found in the first example from the Codex. Putting a 999 on your WordPress add_filter call is a bit like !importanting your CSS: possible, good to know, but not likely to be the best answer. It’s much better to strap on your spelunking hat and see what’s actually going on.

Happy hacking!

Image credit: paurlan, Public Domain, mwphillips75