Make WordPress Core


Ignore:
Timestamp:
10/30/2025 12:46:29 AM (7 months ago)
Author:
westonruter
Message:

General: Add wp_send_late_headers action which fires right before the template enhancement output buffer is flushed.

This adds a (missing) wp_send_late_headers action which fires right after the wp_template_enhancement_output_buffer filters have applied and right before the output buffer is flushed. The filtered output buffer is passed as an argument to the action so that plugins may do things like send an ETag header which is calculated from the content. This action eliminates the need for plugins to hack the wp_template_enhancement_output_buffer filter with a high priority to send a late response header. This action compliments the send_headers action which is commonly used to send HTTP headers before the template is rendered. Furthermore:

  • The template enhancement output buffer is now enabled by default if there is a callback added to either the wp_template_enhancement_output_buffer filter or the wp_send_late_headers action.
  • The wp_start_template_enhancement_output_buffer() callback for the wp_before_include_template action is increased from the default of 10 to 1000. This goes with the previous point, so that plugins can add those filters and actions during the wp_before_include_template action without having to worry about adding them too late, that is, after wp_start_template_enhancement_output_buffer() has run.
  • The wp_send_late_headers action fires regardless of whether the buffered response is HTML.

Developed in https://github.com/WordPress/wordpress-develop/pull/10381

Follow-up to [60936].

Props westonruter, peterwilsoncc, johnbillion.
See #43258.
Fixes #64126.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/template.php

    r61076 r61088  
    604604     *
    605605     * @ticket 43258
     606     *
    606607     * @covers ::wp_should_output_buffer_template_for_enhancement
    607608     * @covers ::wp_start_template_enhancement_output_buffer
     
    627628     *
    628629     * @ticket 43258
     630     * @ticket 64126
     631     *
    629632     * @covers ::wp_start_template_enhancement_output_buffer
    630633     * @covers ::wp_finalize_template_enhancement_output_buffer
     
    634637        ob_start();
    635638
    636         $filter_args = null;
     639        $mock_filter_callback = new MockAction();
    637640        add_filter(
    638641            'wp_template_enhancement_output_buffer',
    639             static function ( string $buffer ) use ( &$filter_args ): string {
    640                 $filter_args = func_get_args();
    641 
     642            array( $mock_filter_callback, 'filter' ),
     643            10,
     644            PHP_INT_MAX
     645        );
     646
     647        $mock_action_callback = new MockAction();
     648        add_filter(
     649            'wp_send_late_headers',
     650            array( $mock_action_callback, 'action' ),
     651            10,
     652            PHP_INT_MAX
     653        );
     654
     655        add_filter(
     656            'wp_template_enhancement_output_buffer',
     657            static function ( string $buffer ): string {
    642658                $p = WP_HTML_Processor::create_full_parser( $buffer );
    643659                while ( $p->next_tag() ) {
     
    657673                }
    658674                return $p->get_updated_html();
    659             },
    660             10,
    661             PHP_INT_MAX
     675            }
    662676        );
    663677
     
    696710        $this->assertSame( $initial_ob_level, ob_get_level(), 'Expected the output buffer to be back at the initial level.' );
    697711
     712        $this->assertSame( 1, $mock_filter_callback->get_call_count(), 'Expected the wp_template_enhancement_output_buffer filter to have applied.' );
     713        $filter_args = $mock_filter_callback->get_args()[0];
    698714        $this->assertIsArray( $filter_args, 'Expected the wp_template_enhancement_output_buffer filter to have applied.' );
    699715        $this->assertCount( 2, $filter_args, 'Expected two args to be supplied to the wp_template_enhancement_output_buffer filter.' );
     
    717733        $this->assertStringContainsString( '<h1>¡Hola, mundo!</h1>', $processed_output, 'Expected processed output to contain string.' );
    718734        $this->assertStringContainsString( '</html>', $processed_output, 'Expected processed output to contain string.' );
     735
     736        $this->assertSame( 1, did_action( 'wp_send_late_headers' ), 'Expected the wp_send_late_headers action to have fired.' );
     737        $this->assertSame( 1, $mock_action_callback->get_call_count(), 'Expected wp_send_late_headers action callback to have been called once.' );
     738        $action_args = $mock_action_callback->get_args()[0];
     739        $this->assertCount( 1, $action_args, 'Expected the wp_send_late_headers action to have been passed only one argument.' );
     740        $this->assertSame( $processed_output, $action_args[0], 'Expected the arg passed to wp_send_late_headers to be the same as the processed output buffer.' );
    719741    }
    720742
     
    723745     *
    724746     * @ticket 43258
     747     * @ticket 64126
     748     *
    725749     * @covers ::wp_start_template_enhancement_output_buffer
    726750     * @covers ::wp_finalize_template_enhancement_output_buffer
     
    730754        ob_start();
    731755
    732         $applied_filter = false;
     756        $mock_filter_callback = new MockAction();
    733757        add_filter(
    734758            'wp_template_enhancement_output_buffer',
    735             static function ( string $buffer ) use ( &$applied_filter ): string {
    736                 $applied_filter = true;
    737 
     759            array( $mock_filter_callback, 'filter' )
     760        );
     761
     762        add_filter(
     763            'wp_template_enhancement_output_buffer',
     764            static function ( string $buffer ): string {
    738765                $p = WP_HTML_Processor::create_full_parser( $buffer );
    739766                if ( $p->next_tag( array( 'tag_name' => 'TITLE' ) ) ) {
     
    742769                return $p->get_updated_html();
    743770            }
     771        );
     772
     773        $mock_action_callback = new MockAction();
     774        add_filter(
     775            'wp_send_late_headers',
     776            array( $mock_action_callback, 'action' ),
     777            10,
     778            PHP_INT_MAX
    744779        );
    745780
     
    775810        $this->assertSame( $initial_ob_level, ob_get_level(), 'Expected the output buffer to be back at the initial level.' );
    776811
    777         $this->assertFalse( $applied_filter, 'Expected the wp_template_enhancement_output_buffer filter to not have applied.' );
    778         $this->assertSame( 0, did_action( 'wp_final_template_output_buffer' ), 'Expected the wp_final_template_output_buffer action to not have fired.' );
     812        $this->assertSame( 0, $mock_filter_callback->get_call_count(), 'Expected the wp_template_enhancement_output_buffer filter to not have applied.' );
    779813
    780814        // Obtain the output via the wrapper output buffer.
     
    784818        $this->assertStringNotContainsString( '<title>Processed</title>', $output, 'Expected output buffer to not have string since the filter did not apply.' );
    785819        $this->assertStringContainsString( '<title>Output Buffer Not Processed</title>', $output, 'Expected output buffer to have string since the output buffer was ended with cleaning.' );
     820
     821        $this->assertSame( 0, did_action( 'wp_send_late_headers' ), 'Expected the wp_send_late_headers action to not have fired.' );
     822        $this->assertSame( 0, $mock_action_callback->get_call_count(), 'Expected wp_send_late_headers action callback to have been called once.' );
    786823    }
    787824
     
    790827     *
    791828     * @ticket 43258
     829     * @ticket 64126
     830     *
    792831     * @covers ::wp_start_template_enhancement_output_buffer
    793832     * @covers ::wp_finalize_template_enhancement_output_buffer
     
    797836        ob_start();
    798837
    799         $called_filter = false;
     838        $mock_filter_callback = new MockAction();
    800839        add_filter(
    801840            'wp_template_enhancement_output_buffer',
    802             static function ( string $buffer ) use ( &$called_filter ): string {
    803                 $called_filter = true;
    804 
     841            array( $mock_filter_callback, 'filter' )
     842        );
     843
     844        add_filter(
     845            'wp_template_enhancement_output_buffer',
     846            static function ( string $buffer ): string {
    805847                $p = WP_HTML_Processor::create_full_parser( $buffer );
    806848                if ( $p->next_tag( array( 'tag_name' => 'TITLE' ) ) ) {
     
    809851                return $p->get_updated_html();
    810852            }
     853        );
     854
     855        $mock_action_callback = new MockAction();
     856        add_filter(
     857            'wp_send_late_headers',
     858            array( $mock_action_callback, 'action' ),
     859            10,
     860            PHP_INT_MAX
    811861        );
    812862
     
    847897        $this->assertSame( $initial_ob_level, ob_get_level(), 'Expected the output buffer to be back at the initial level.' );
    848898
    849         $this->assertTrue( $called_filter, 'Expected the wp_template_enhancement_output_buffer filter to have applied.' );
     899        $this->assertSame( 1, $mock_filter_callback->get_call_count(), 'Expected the wp_template_enhancement_output_buffer filter to have applied.' );
    850900
    851901        // Obtain the output via the wrapper output buffer.
     
    855905        $this->assertStringContainsString( '<title>Processed</title>', $output, 'Expected output buffer to have string due to filtering.' );
    856906        $this->assertStringContainsString( '<h1>Template Replaced</h1>', $output, 'Expected output buffer to have string due to replaced template.' );
     907
     908        $this->assertSame( 1, did_action( 'wp_send_late_headers' ), 'Expected the wp_send_late_headers action to have fired.' );
     909        $this->assertSame( 1, $mock_action_callback->get_call_count(), 'Expected wp_send_late_headers action callback to have been called once.' );
     910        $action_args = $mock_action_callback->get_args()[0];
     911        $this->assertCount( 1, $action_args, 'Expected the wp_send_late_headers action to have been passed only one argument.' );
     912        $this->assertSame( $output, $action_args[0], 'Expected the arg passed to wp_send_late_headers to be the same as the processed output buffer.' );
    857913    }
    858914
     
    861917     *
    862918     * @ticket 43258
     919     * @ticket 64126
     920     *
    863921     * @covers ::wp_start_template_enhancement_output_buffer
    864922     * @covers ::wp_finalize_template_enhancement_output_buffer
     
    870928        $mock_filter_callback = new MockAction();
    871929        add_filter( 'wp_template_enhancement_output_buffer', array( $mock_filter_callback, 'filter' ) );
     930
     931        $mock_action_callback = new MockAction();
     932        add_filter(
     933            'wp_send_late_headers',
     934            array( $mock_action_callback, 'action' ),
     935            10,
     936            PHP_INT_MAX
     937        );
    872938
    873939        $initial_ob_level = ob_get_level();
     
    904970        $this->assertIsString( $output, 'Expected ob_get_clean() to return a string.' );
    905971        $this->assertSame( $json, $output, 'Expected output to not be processed.' );
     972
     973        $this->assertSame( 1, did_action( 'wp_send_late_headers' ), 'Expected the wp_send_late_headers action to have fired even though the wp_template_enhancement_output_buffer filter did not apply.' );
     974        $this->assertSame( 1, $mock_action_callback->get_call_count(), 'Expected wp_send_late_headers action callback to have been called once.' );
     975        $action_args = $mock_action_callback->get_args()[0];
     976        $this->assertCount( 1, $action_args, 'Expected the wp_send_late_headers action to have been passed only one argument.' );
     977        $this->assertSame( $output, $action_args[0], 'Expected the arg passed to wp_send_late_headers to be the same as the processed output buffer.' );
    906978    }
    907979
Note: See TracChangeset for help on using the changeset viewer.