Skip to content

4.2.12 regression: re-armed capture buffer at shutdown breaks FacetWP AJAX refresh (empty template, HTML + JSON response) #1156

Description

@KingGrizzly

Description

Summary

Since 4.2.12, Optml_Manager::close_buffer() re-arms a fresh capture buffer after processing the page. Any plugin that captures its own output buffer later in shutdown receives that empty buffer instead of its own. FacetWP does exactly this, so every facet refresh on a FacetWP "wp" template page now returns the full HTML page followed by a JSON payload with "template":"". FacetWP's client throws a JSON error and the loading spinner never clears.

This is a second symptom of the change discussed in #1149 (Groovy Menu) and introduced by #1132. The Groovy Menu fix in #1150 does not cover this case, since the problem here is the re-arm, not the flush of foreign buffers.

Environment

  • Optimole 4.2.13 (reproduced on 4.2.12 and 4.2.13; fine before 4.2.12)
  • FacetWP 4.5 with the FacetWP Elementor add-on 1.9.4
  • Elementor 4.2.4 / Elementor Pro 4.2.3, Loop Grid with "Enable FacetWP" (FacetWP's "wp" auto-detect template mode)
  • WordPress on WP Engine, PHP output_buffering on
  • Site URL available privately on request
  1. Page with an Elementor Loop Grid using "Enable FacetWP" and any facet, so FacetWP uses its "wp" template mode and POSTs facetwp_refresh to the page URL.
  2. Optimole active with image replacement enabled.
  3. Change any facet value.

Observed

The refresh response has Content-Type: application/json but the body is the complete HTML document followed by FacetWP's JSON, with "template":"". Browser console:

SyntaxError: Unexpected non-whitespace character after JSON at position 1099

Root cause

FacetWP opens ob_start() at init and, in FacetWP_Request::inject_template() hooked to shutdown at priority 0, calls ob_get_clean() expecting its own buffer, extracts <body>…</body> into template, then wp_send_json().

Optimole's close_buffer() runs first at shutdown priority PHP_INT_MIN. It captures and processes the page, echoes the result (which lands in FacetWP's buffer), then calls start_capture_buffer() again. FacetWP's ob_get_clean() therefore returns the new empty buffer.

ob_get_status(true) captured at shutdown priority -1 during a FacetWP refresh:

level handler buffer_used
0 default output handler 0
1 WpeCommon::filter_html_output 0
2 default output handler (FacetWP, opened at init) 208422
3 Optml_Manager::handle_buffer_fallback (re-armed) 0

Workaround that resolves it

add_filter( 'optml_capture_at_shutdown', function ( $capture ) {
    if ( function_exists( 'FWP' ) && ! empty( FWP()->request->is_refresh ) ) {
        return false;
    }
    return $capture;
} );

### Step-by-step reproduction instructions

1. Page with an Elementor Loop Grid using "Enable FacetWP" and any facet, so FacetWP uses its "wp" template mode and POSTs `facetwp_refresh` to the page URL.
2. Optimole active with image replacement enabled.
3. Change any facet value.


### Screenshots, screen recording, code snippet or Help Scout ticket

_No response_

### Environment info

_No response_

### Is the issue you are reporting a regression

Yes, this is a regression.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugThis label could be used to identify issues that are caused by a defect in the product.regressionIssue represents a change in behavior from a previous version that is not intended or desired..

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions