Skip to content

Fix FacetWP refresh and other shutdown buffer readers: process in the output handler by default again - #1158

Merged
selul merged 3 commits into
developmentfrom
fix/shutdown-capture-foreign-buffers
Sep 21, 2026
Merged

selul merged 3 commits into
developmentfrom
fix/shutdown-capture-foreign-buffers

Conversation

@selul

@selul selul commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Since 4.2.12, plugins that open an output buffer early and read it back with ob_get_clean() on shutdown priority 0 receive an empty string, so FacetWP refreshes return HTML followed by JSON. The page is processed inside the output handler by default again, as up to 4.2.11, and the shutdown capture becomes an opt-in.

Note

The bug was added in d6cc7b5 on version 4.2.12.

Reported in #1156. Same root cause as #1149.

What changed

  • optml_capture_at_shutdown filter — before: default true, the page was captured and processed at shutdown PHP_INT_MIN. After: default false, the output handler processes the page when the buffer is flushed. Return true from the filter to opt in to the shutdown capture.

  • Output handler — processes the page by default. If the replacement throws, the handler returns the content untouched, so the page never breaks.

  • close_final_buffer() — does nothing unless the shutdown capture is on.

  • Groovy Menu compatibility — unchanged in code. It acts only with the shutdown capture, so it is inert by default, and Groovy Menu keeps its own shutdown step.

Note

Why switch back and not patch the capture: the capture must flush third-party buffers above ours and re-arm a buffer after it, and each step breaks plugins that read their buffer at shutdown. The reporters of #1149 and #1156 confirmed that in-handler processing fixes their production sites.

The crash that motivated the capture, #1126, logged Optml_Manager::replace_content(): Cannot use output buffering in output buffering display handlers. A nested ob_start() logs that message with an ob_start(): prefix. A memory-limit fatal inside a handler logs it with the prefix of the running method, which matches the telemetry. The capture does not prevent that crash, it only restores the real message.

The first commit on this branch and its revert are an earlier approach that was dropped. Squash on merge.

QA

  1. Install FacetWP, the FacetWP Elementor add-on, Elementor and Elementor Pro. Connect Optimole and keep Image replacement on at WP Admin → Media → Optimole → Settings → General.

  2. Go to WP Admin → Pages → Add New and edit the page with Elementor. Add a Loop Grid widget, open its Content tab, and turn on Enable FacetWP. Add any facet to the page with the FacetWP facet widget, then publish.

  3. Open the page on the front end, open the browser DevTools Network tab, and change the facet value.

    Expect: the POST request to the page URL returns only JSON, the template key is not empty, the listing updates, and the loading spinner clears. On 4.2.13 the response is the HTML page followed by JSON.

  4. With Divi and Groovy Menu auto-integration set up as in Fix Groovy Menu markup missing since the shutdown capture (4.2.12) #1150, load any front-end page and view the source.

    Expect: the .gm-navbar element is present one time.

  5. Load any front-end page with images and view the source.

    Expect: image URLs use the Optimole CDN domain.

Related: #1156, #1149

🤖 Generated with Claude Code

Since 4.2.12 close_buffer() re-armed an empty capture buffer and
force-flushed buffers stacked above ours at shutdown PHP_INT_MIN. Code
that opens a buffer early and reads it back with ob_get_clean() on
shutdown priority 0 (FacetWP refresh, Groovy Menu) received an empty
string.

- Stop re-arming a buffer after the capture, so a buffer opened before
  ours is on top again and holds the processed page.
- When third-party buffers sit above ours, defer the capture until the
  priority 0 shutdown callbacks ran instead of force-flushing them.
- Guard the Groovy Menu compatibility against a double menu insertion
  when its own shutdown step runs before the deferred capture.

Fixes #1156

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@pirate-bot

pirate-bot commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

Plugin build for d844ce2 is ready 🛎️!

selul and others added 2 commits September 21, 2026 14:02
The shutdown capture added in 4.2.12 flushes the buffers stacked above
ours and opens a new buffer afterwards. Code that opens a buffer early
and reads it back with ob_get_clean() on shutdown priority 0 (FacetWP
refresh, Groovy Menu) received an empty string.

Restore the 4.2.11 in-handler processing as the default and keep the
shutdown capture as an opt-in through optml_capture_at_shutdown. An
exception thrown during the replacement still never breaks the page.

Fixes #1156

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@selul selul changed the title Fix third-party output buffers left empty at shutdown since 4.2.12 (FacetWP refresh) Fix FacetWP refresh and other shutdown buffer readers: process in the output handler by default again Sep 21, 2026
@selul
selul merged commit 8080cbc into development Sep 21, 2026
12 checks passed
@selul
selul deleted the fix/shutdown-capture-foreign-buffers branch September 21, 2026 11:47
@selul selul mentioned this pull request Sep 21, 2026
@pirate-bot

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 4.2.14 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@pirate-bot pirate-bot added the released Indicate that an issue has been resolved and released in a particular version of the product. label Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

released Indicate that an issue has been resolved and released in a particular version of the product.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants