Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,10 @@ public function has_registered_metric( string $metric_slug ): bool {
/**
* Outputs the Server-Timing header.
*
* This method must be called before rendering the page.
* This must run before the response body is flushed. When output buffering is enabled it is called at the
* {@see 'wp_finalized_template_enhancement_output_buffer'} action, after the template has been captured and
* enhanced; otherwise it is called at the {@see 'wp_before_include_template'} action, right before the template
* is included. Any extra arguments passed by those hooks are ignored.
*
* @since 1.8.0
*/
Expand Down Expand Up @@ -236,56 +239,30 @@ public function use_output_buffer(): bool {
/**
* Adds hooks to send the Server-Timing header.
*
* When output buffering is enabled, buffer as early as possible so that any other plugins that also do output
* buffering will be able to register Server-Timing metrics. The first output buffer callback to be registered
* is the last one to be called, so by starting the Server-Timing output buffer as soon as possible we can be
* assured that other plugins' output buffer callbacks will run before the Server-Timing one that sends the
* Server-Timing header.
* Since WordPress 6.9, this relies on the template enhancement output buffer introduced in Core-43258 (see
* r60936) rather than starting its own output buffer.
*
* When output buffering is enabled, the header is sent at the {@see 'wp_finalized_template_enhancement_output_buffer'}
* action so that metrics measured while the template is rendering are included. Registering a callback on that
* action also opts in to starting the core output buffer (see {@see wp_should_output_buffer_template_for_enhancement()}),
* and core ensures the {@see 'wp_template_enhancement_output_buffer'} filter (used by other consumers such as
* Optimization Detective) runs before this action, so their metrics are captured before the header is sent.
*
* When output buffering is disabled, the header is sent at the {@see 'wp_before_include_template'} action, right
* before the template is included. Note this action only fires when the resolved template is a readable file,
* unlike the previously used `template_include` filter which fired unconditionally.
*
* @since 3.2.0
* @since n.e.x.t Relies on the WordPress 6.9 template enhancement output buffer instead of starting its own.
*/
public function add_hooks(): void {
if ( $this->use_output_buffer() ) {
add_action( 'template_redirect', array( $this, 'start_output_buffer' ), PHP_INT_MIN );
add_action( 'wp_finalized_template_enhancement_output_buffer', array( $this, 'send_header' ) );
} else {
add_filter( 'template_include', array( $this, 'on_template_include' ), PHP_INT_MAX );
add_action( 'wp_before_include_template', array( $this, 'send_header' ) );
}
}

/**
* Hook callback for the 'template_include' filter.
*
* This effectively initializes the class to send the Server-Timing header at the right point.
*
* This method is solely intended for internal use within WordPress.
*
* @since 1.8.0
*
* @param mixed $passthrough Optional. Filter value. Default null.
* @return mixed Unmodified value of $passthrough.
*/
public function on_template_include( $passthrough = null ) {
$this->send_header();
return $passthrough;
}

/**
* Starts output buffering to send the Server-Timing header right before returning the buffer.
*
* @since 3.2.0
*/
public function start_output_buffer(): void {
ob_start(
function ( string $output, ?int $phase ): string {
// Only send the header when the buffer is not being cleaned.
if ( ( $phase & PHP_OUTPUT_HANDLER_CLEAN ) === 0 ) {
$this->send_header();
}
return $output;
}
);
}

/**
* Formats the header segment for a single metric.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public function test_perflab_server_timing(): void {

$server_timing = perflab_server_timing();
$this->assertFalse( $server_timing->use_output_buffer() );
$this->assertSame( PHP_INT_MAX, has_filter( 'template_include', array( $server_timing, 'on_template_include' ) ), 'template_include filter not added' );
$this->assertFalse( has_action( 'template_redirect', array( $server_timing, 'start_output_buffer' ) ), 'template_redirect action added' );
$this->assertSame( 10, has_action( 'wp_before_include_template', array( $server_timing, 'send_header' ) ), 'wp_before_include_template action not added' );
$this->assertFalse( has_action( 'wp_finalized_template_enhancement_output_buffer', array( $server_timing, 'send_header' ) ), 'wp_finalized_template_enhancement_output_buffer action added' );

$server_timing2 = perflab_server_timing();
$this->assertSame( $server_timing, $server_timing2, 'Different instance returned' );
Expand All @@ -28,15 +28,15 @@ public function test_perflab_server_timing(): void {
* @covers Perflab_Server_Timing::add_hooks
*/
public function test_perflab_server_timing_with_output_buffering(): void {
remove_all_actions( 'template_redirect' );
remove_all_filters( 'template_include' );
remove_all_actions( 'wp_before_include_template' );
remove_all_actions( 'wp_finalized_template_enhancement_output_buffer' );

$server_timing = perflab_server_timing();
add_filter( 'perflab_server_timing_use_output_buffer', '__return_true' );
$this->assertTrue( $server_timing->use_output_buffer() );
$server_timing->add_hooks();
$this->assertFalse( has_filter( 'template_include', array( $server_timing, 'on_template_include' ) ), 'template_include filter added' );
$this->assertSame( PHP_INT_MIN, has_action( 'template_redirect', array( $server_timing, 'start_output_buffer' ) ), 'template_redirect action not added' );
$this->assertFalse( has_action( 'wp_before_include_template', array( $server_timing, 'send_header' ) ), 'wp_before_include_template action added' );
$this->assertSame( 10, has_action( 'wp_finalized_template_enhancement_output_buffer', array( $server_timing, 'send_header' ) ), 'wp_finalized_template_enhancement_output_buffer action not added' );
}

public function test_perflab_server_timing_register_metric(): void {
Expand Down
Loading