Defer innerHTML serialization in the "Grabbed" debug log#46
Merged
fivefilters merged 1 commit intoJul 15, 2026
Conversation
The log() call in grabArticle() concatenated $articleContent->innerHTML into the message unconditionally. Because PHP evaluates arguments eagerly, the full article subtree was serialized on every parse even when no logger was configured and debug was off, then discarded. Pass a closure instead and resolve it inside log()'s formatter, which runs only after the enabled check. When logging is off the innerHTML is never built; when it is on the output is unchanged. The formatter now resolves any Closure argument first, so other call sites can defer expensive values the same way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BXHSUavFZ8SkC5kX6j59BV
0d6ee28
into
claude/readability-php-modernize-ii46mk
2 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
grabArticle()logged the grabbed article with:PHP evaluates function arguments eagerly, so
innerHTMLserialized the entire article subtree into a string on every parse — even when no logger was configured anddebugwas off.log()'s early return then discarded it. The serialization work (and a large transient allocation) happened beforelog()was ever entered.This changes the call site to pass a closure, and teaches
log()'s formatter to resolve any\Closureargument — but only after the enabled check that guards the method:When logging is off, the closure is never invoked and
innerHTMLis never built. When logging is on, the emitted message is byte-for-byte identical to before.Why a closure, not a change to the element formatter
The formatter's
\Dom\Elementbranch is shared by ~12 other call sites (Candidate:,Looking at sibling node:,setNodeTag,Cleaning Conditionally, …), many inside hot per-node loops. They rely on the compact<div class="...">representation. EmittinginnerHTMLfor all elements would flood those logs with unreadable full-subtree dumps. Deferring via a closure scopes the change to this one line and leaves every other element log untouched. The formatter now also resolves closures generically, so other sites can defer expensive values the same way.Testing
Verified with a standalone script (composer/phpunit unavailable in this environment):
parse()with a PSR-3 logger emits exactly oneGrabbed:message containing the serializedinnerHTML.log()'s structure with a counting closure, the closure is invoked 0 times when logging is off and once when a logger is present.php -lpasses.🤖 Generated with Claude Code
https://claude.ai/code/session_01BXHSUavFZ8SkC5kX6j59BV
Generated by Claude Code