Skip to content

docs(readme): record what a rendered post body costs to derive from - #160

Merged
parisek merged 1 commit into
mainfrom
docs/rendered-content-cost
Aug 27, 2026
Merged

docs(readme): record what a rendered post body costs to derive from#160
parisek merged 1 commit into
mainfrom
docs/rendered-content-cost

Conversation

@parisek

@parisek parisek commented Aug 27, 2026

Copy link
Copy Markdown
Owner

What

A new README section, Deriving a value from a post body, plus its changelog entry. Documentation only — no code changes.

Why

$post->content() reads like an accessor and is a render. It ends in apply_filters( 'the_content' ), and do_blocks() sits on that filter at priority 9. Deriving a cheap value from it — a word count, a reading time, a length, a "has an image" flag — therefore renders the whole article, once per row.

Found on sloneek, whose blog listing called ReadingTime::minutes( $item->content() ) for each of nine teasers:

the_content chain, nine articles 469–644 ms of an 897 ms page
HTML built and discarded 306 kB
the same nine counted from post_content 0.8 ms
page after the fix 0.868 s → 0.354 s

Rendered HTML byte-identical once per-request uniqueId() values are normalised.

Why documentation and not code

The kit does not own the call site. Teaser mappers are written by hand in every consuming theme, so there is no function here to fix — only a trap to name.

What the section adds beyond "don't do that"

Two mechanisms hide the cost, and both are named:

  • Timber memoizes into $this->___content, so the repeated call a reader goes looking for is genuinely free. The cost is the first call, once per post object, and a listing has one object per row.
  • A profile bills the time to WP_Hook->apply_filters, not to the code that asked for it.

Rendering is not automatically the more correct input for a text measure. the_content rewrites e-mail to use a non-breaking hyphen (U+2011). A word-boundary pattern admitting an ASCII hyphen inside a word but not that one then counts every hyphenated word twice. Across 1583 published articles, 1579 counted the same either way; of the four that differed, two held 52 hyphenated words and two sat exactly on a 200-word rounding boundary.

Related

The profiler ranked PHP-Typography first on this page at 20 % of self time. Disabling it in a real request saved ~20 ms — it makes 483 000 tiny calls and Xdebug bills per call. That is the same inclusive-vs-real trap as #147, and it is why the numbers above are all bracketed microtime() in real requests rather than profiler output.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JMYy6JHLf4mU4H4Hd47spb

`$post->content()` reads like an accessor and is a render. It ends in
`apply_filters( 'the_content' )`, and `do_blocks()` sits on that filter at
priority 9, so deriving a word count, a reading time or a length from it
renders the whole article once per row.

Measured on a nine-teaser blog listing: 469-644 ms of an 897 ms page, 306 kB
of HTML built and discarded to keep nine integers. Counting the same nine
from the raw `post_content` cost 0.8 ms, and the page went 0.868 s to
0.354 s with byte-identical output.

Documented rather than fixed in code, because the kit does not own the call
site -- it is a pattern every consuming theme writes by hand in its teaser
mappers.

Two mechanisms hide the cost, and the section names both. Timber memoizes
into `___content`, so the repeated call a reader goes looking for is free
and the real cost is the first call; and a profile bills the time to
`WP_Hook->apply_filters` rather than to the code that asked.

It also records that rendering is not automatically the more correct input
for a text measure. `the_content` rewrites `e-mail` to use a non-breaking
hyphen (U+2011), so a word pattern admitting only an ASCII hyphen counts
every hyphenated word twice. Across 1583 published articles 1579 agreed;
two of the four that differed did so for that reason, two on a rounding
boundary.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JMYy6JHLf4mU4H4Hd47spb (petr@pari.cz)
@parisek parisek self-assigned this Aug 27, 2026
@parisek
parisek merged commit 835623e into main Aug 27, 2026
6 checks passed
@parisek
parisek deleted the docs/rendered-content-cost branch August 27, 2026 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant