Technote

Development workflow Practical

Series Using hooks and filters properly Part 5 of 8

The the_content chain: before and after wpautop are different strings

One filter, two different strings depending on whether you sit at priority 1 or 12. Pick the wrong side and your pattern simply never matches.

the_content is the filter for changing post body HTML. The catch is that core already has several callbacks attached to it, and those callbacks genuinely transform the markup. Which number you stand on decides what string arrives in your hands.

What core already has attached

Core callbacks on the_content — your number decides which string you receive

In short: attach at priority 1 and you see something close to the raw post_content — blocks are still HTML comments, shortcodes are still literal text, and there is not a single <p> anywhere. Attach at priority 12 or later and you get finished HTML with paragraphs wrapped, shortcodes executed and image attributes filled in.

Where “my regex never matches” comes from

This is the most common failure by far. Trying to insert something after the first paragraph by matching </p> at priority 1 can never match, because no paragraph tags exist yet. Conversely, trying to replace a shortcode string at priority 12 finds nothing, because it has already been executed.

The rule is simple: go early for raw markup, late for finished HTML. And remember that HTML you insert early is itself fed to wpautop, so a block-level element added there can pick up a stray closing paragraph tag.

// Sees the original — block comments and shortcodes still intact
add_filter( 'the_content', 'wper_early', 1 );

// Sees finished HTML — paragraphs wrapped, image attributes filled
add_filter( 'the_content', 'wper_late', 12 );

The article pages in this repository use the late side. We parse the body’s h2 elements to add anchors and build a table of contents from the same parse, and that has to run against finished HTML so that block-authored headings and classic markup come out the same shape. Hence priority 12.

This filter does not only run on the post page

Another easy miss: the_content also runs while excerpts are generated. When a post has no hand-written excerpt, WordPress passes the body through this filter to build one.

So an unguarded filter that appends a call to action ends up printing that call to action inside every card excerpt on your listing pages, and in feeds too. Guard it.

function wper_late( $html ) {
	if ( ! is_singular() || ! in_the_loop() || ! is_main_query() ) {
		return $html;   // leave excerpts, feeds and secondary loops alone
	}

	return $html . '<aside class="wper-cta">…</aside>';
}

in_the_loop() earns its place because even on a single post page a secondary loop — related posts, say — can push a body through the filter again. All three checks together are what let you claim “this is the body of the post being viewed”.

More on content rendering and its performance cost sits in the development workflow archive, and sorting out content processing alongside the caching layers is part of our optimization program.

Next part

Sometimes removing a callback is the right answer rather than painting over it. Part six covers taking somebody else’s hook down, and the three ways that quietly fails.

More on this topic

All technotes

Development workflow Advanced

The design QA checklist to run before release

Most design problems found after release are catchable before it, in order. Here is the pass, in three stages: rules, states and real devices.

Designers 6 min read

Development workflow Advanced

Never deploy without a way back

A rollback is not one button. It is two tracks — code and database — with an order to reverse them in, written down before you deploy.

Designers 9 min read

Development workflow Advanced

Adding your own hooks: extension points instead of edits

Code with no extension points gets forked or edited in place. Where you put do_action and apply_filters — and how many — decides how long that code survives.

Developers 7 min read

₩270,000 · Join the program