From d707208b6f8abc6317f55920c3d4e95e4f5bad45 Mon Sep 17 00:00:00 2001 From: RafaelKr <14234815+rafaelkr@users.noreply.github.com> Date: Mon, 16 Dec 2024 10:41:04 +0100 Subject: [PATCH] fix: request handler output buffer handling Previously, Acorn with the experimental request handler enabled broke WordPress static caching plugins like WP Rocket. This happened because the caching plugins work at a higher level than Acorn, but Acorn cleared all output buffers before the caching plugin could handle the contents. Fixes #406 --- .../Acorn/Application/Concerns/Bootable.php | 56 ++++++++++++------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/src/Roots/Acorn/Application/Concerns/Bootable.php b/src/Roots/Acorn/Application/Concerns/Bootable.php index 4aa01d7f..3d1706aa 100644 --- a/src/Roots/Acorn/Application/Concerns/Bootable.php +++ b/src/Roots/Acorn/Application/Concerns/Bootable.php @@ -143,33 +143,37 @@ protected function enableHttpsInConsole(): void */ protected function registerWordPressRoute(): void { - Route::any('{any?}', fn () => tap(response(''), function (Response $response) { - foreach (headers_list() as $header) { - [$header, $value] = preg_split("/:\s{0,1}/", $header, 2); + Route::any('{any?}', fn (Request $request) => tap( + response(''), + function (Response $response) use ($request) { + foreach (headers_list() as $header) { + [$header, $value] = preg_split("/:\s{0,1}/", $header, 2); - if (! headers_sent()) { - header_remove($header); - } + if (! headers_sent()) { + header_remove($header); + } - $response->header($header, $value, $header !== 'Set-Cookie'); - } + $response->header($header, $value, $header !== 'Set-Cookie'); + } - if ($this->hasDebugModeEnabled()) { - $response->header('X-Powered-By', $this->version()); - } + if ($this->hasDebugModeEnabled()) { + $response->header('X-Powered-By', $this->version()); + } - $response->setStatusCode(http_response_code()); + $response->setStatusCode(http_response_code()); - $content = ''; + // Get the output buffer contents from our shutdown handler + $content = $request->request->get('wp_ob_content'); - $levels = ob_get_level(); + $levels = ob_get_level(); - for ($i = 0; $i < $levels; $i++) { - $content .= ob_get_clean(); - } + for ($i = 0; $i < $levels; $i++) { + $content .= ob_get_contents(); + } - $response->setContent($content); - })) + $response->setContent($content); + }) + ) ->where('any', '.*') ->name('wordpress'); } @@ -220,9 +224,23 @@ protected function registerRequestHandler( $route->middleware('wordpress'); + // We need to save our start level as there might be higher level output buffer handlers due to caching plugins. + $startLevel = ob_get_level(); ob_start(); remove_action('shutdown', 'wp_ob_end_flush_all', 1); + add_action('shutdown', function () use ($request, $startLevel) { + // Clean buffer only until our start level, keep possible higher level buffers to their handlers + $levels = ob_get_level() - $startLevel; + $content = ''; + + for ($i = 0; $i < $levels; $i++) { + $content .= ob_get_clean(); + } + + // Save content onto request to have it available in the Response handler + $request->request->set('wp_ob_content', $content); + }, 1); add_action('shutdown', fn () => $this->handleRequest($request), 100); }