Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add output buffering checkbox to Server-Timing screen #801

Merged
merged 18 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
134 changes: 118 additions & 16 deletions admin/server-timing.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,47 @@ function perflab_add_server_timing_page() {
* @since n.e.x.t
*/
function perflab_load_server_timing_page() {
add_settings_section(
'output-buffering',
__( 'Output Buffering', 'performance-lab' ),
'perflab_render_server_timing_page_output_buffer_checkbox',
PERFLAB_SERVER_TIMING_SCREEN
);

add_action(
'admin_print_styles',
static function () {
?>
<style>
.wrap p {
max-width: 800px;
}

/* The following styles are copied from core where they normally include `.form-table td` in the selectors. */
.output-buffering-field {
margin-bottom: 9px;
padding-bottom: 15px;
line-height: 1.3;
vertical-align: middle;
font-size: 14px;
}
.output-buffering-field label,
.output-buffering-field p {
line-height: 1.4;
}
.output-buffering-field label {
margin: 0.35em 0 0.5em !important;
felixarntz marked this conversation as resolved.
Show resolved Hide resolved
display: inline-block;
vertical-align: middle;
felixarntz marked this conversation as resolved.
Show resolved Hide resolved
}
.output-buffering-field p {
font-size: 14px;
}
felixarntz marked this conversation as resolved.
Show resolved Hide resolved
</style>
<?php
}
);

add_settings_section(
'benchmarking',
__( 'Benchmarking', 'performance-lab' ),
Expand All @@ -58,28 +99,31 @@ static function() {
),
array( 'code' => array() )
);
?>
<br>
<?php
felixarntz marked this conversation as resolved.
Show resolved Hide resolved
echo ' ';
echo wp_kses(
__( 'For any hook name provided, the <strong>cumulative duration between all callbacks</strong> attached to the hook is measured, in milliseconds.', 'performance-lab' ),
array( 'strong' => array() )
);
if ( ! perflab_server_timing_use_output_buffer() ) {
?>
<br>
felixarntz marked this conversation as resolved.
Show resolved Hide resolved
?>
</p>
<?php if ( ! perflab_server_timing_use_output_buffer() ) : ?>
<p>
<?php
echo wp_kses(
sprintf(
/* translators: 1: Server-Timing, 2: template_include */
__( 'Since the %1$s header is sent before the template is loaded, only hooks before the %2$s filter can be measured.', 'performance-lab' ),
/* translators: 1: Server-Timing, 2: template_include, 3: anchor link */
__( 'Since the %1$s header is sent before the template is loaded, only hooks before the %2$s filter can be measured. Enable <a href="%3$s">Output Buffering</a> to measure hooks during template rendering.', 'performance-lab' ),
felixarntz marked this conversation as resolved.
Show resolved Hide resolved
'<code>Server-Timing</code>',
'<code>template_include</code>'
'<code>template_include</code>',
esc_url( '#server_timing_output_buffering' )
),
array( 'code' => array() )
array(
'code' => array(),
'a' => array( 'href' => true ),
)
);
}
?>
?>
<?php endif; ?>
</p>
<?php
},
Expand All @@ -95,7 +139,7 @@ static function() {
'benchmarking_actions',
__( 'Actions', 'performance-lab' ),
static function() {
perflab_render_server_timing_page_field( 'benchmarking_actions' );
perflab_render_server_timing_page_hooks_field( 'benchmarking_actions' );
},
PERFLAB_SERVER_TIMING_SCREEN,
'benchmarking',
Expand All @@ -105,7 +149,7 @@ static function() {
'benchmarking_filters',
__( 'Filters', 'performance-lab' ),
static function() {
perflab_render_server_timing_page_field( 'benchmarking_filters' );
perflab_render_server_timing_page_hooks_field( 'benchmarking_filters' );
},
PERFLAB_SERVER_TIMING_SCREEN,
'benchmarking',
Expand Down Expand Up @@ -136,13 +180,13 @@ function perflab_render_server_timing_page() {
}

/**
* Renders a field for the given Server-Timing option.
* Renders a hooks field for the given Server-Timing option.
*
* @since n.e.x.t
*
* @param string $slug Slug of the field and sub-key in the Server-Timing option.
*/
function perflab_render_server_timing_page_field( $slug ) {
function perflab_render_server_timing_page_hooks_field( $slug ) {
$options = (array) get_option( PERFLAB_SERVER_TIMING_SETTING, array() );

// Value for the sub-key is an array of hook names.
Expand All @@ -168,3 +212,61 @@ class="large-text code"
</p>
<?php
}

/**
* Renders a checkbox for enabling output buffering for Server-Timing.
*
* @since n.e.x.t
*/
function perflab_render_server_timing_page_output_buffer_checkbox() {
$slug = 'output_buffering';
$field_id = "server_timing_{$slug}";
$field_name = PERFLAB_SERVER_TIMING_SETTING . '[' . $slug . ']';
$description_id = "{$field_id}_description";
$has_filter = has_filter( 'perflab_server_timing_use_output_buffer' );
$is_enabled = perflab_server_timing_use_output_buffer();

?>
<div class="output-buffering-field">
<input
type="checkbox"
id="<?php echo esc_attr( $field_id ); ?>"
name="<?php echo esc_attr( $field_name ); ?>"
aria-describedby="<?php echo esc_attr( $description_id ); ?>"
<?php disabled( $has_filter ); ?>
<?php checked( $is_enabled ); ?>
>
<label for="<?php echo esc_attr( $field_id ); ?>">
<?php esc_html_e( 'Enable output buffering of template rendering.', 'performance-lab' ); ?>
</label>
<p id="<?php echo esc_attr( $description_id ); ?>" class="description">
<?php if ( $has_filter ) : ?>
<?php if ( $is_enabled ) : ?>
<?php
echo wp_kses(
sprintf(
/* translators: %s: perflab_server_timing_use_output_buffer */
__( 'Output buffering has been forcibly enabled via the %s filter.', 'performance-lab' ),
'<code>perflab_server_timing_use_output_buffer</code>'
),
array( 'code' => array() )
);
?>
<?php else : ?>
<?php
echo wp_kses(
sprintf(
/* translators: %s: perflab_server_timing_use_output_buffer */
__( 'Output buffering has been forcibly disabled via the %s filter.', 'performance-lab' ),
'<code>perflab_server_timing_use_output_buffer</code>'
),
array( 'code' => array() )
);
?>
felixarntz marked this conversation as resolved.
Show resolved Hide resolved
<?php endif; ?>
<?php endif; ?>
<?php esc_html_e( 'This is needed to capture metrics after headers have been sent and while the template is being rendered. Note that output buffering may possibly cause an increase in TTFB if the response would be flushed multiple times.', 'performance-lab' ); ?>
felixarntz marked this conversation as resolved.
Show resolved Hide resolved
westonruter marked this conversation as resolved.
Show resolved Hide resolved
</p>
</div>
<?php
}
5 changes: 4 additions & 1 deletion server-timing/class-perflab-server-timing.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ static function( $value ) {
* @return bool True if an output buffer should be used, false otherwise.
*/
public function use_output_buffer() {
$options = (array) get_option( PERFLAB_SERVER_TIMING_SETTING, array() );
$enabled = ! empty( $options['output_buffering'] );

/**
* Filters whether an output buffer should be used to be able to gather additional Server-Timing metrics.
*
Expand All @@ -206,7 +209,7 @@ public function use_output_buffer() {
*
* @param bool $use_output_buffer Whether to use an output buffer.
*/
return apply_filters( 'perflab_server_timing_use_output_buffer', false );
return (bool) apply_filters( 'perflab_server_timing_use_output_buffer', $enabled );
}

/**
Expand Down
5 changes: 4 additions & 1 deletion server-timing/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ function perflab_sanitize_server_timing_setting( $value ) {
static $allowed_keys = array(
'benchmarking_actions' => true,
'benchmarking_filters' => true,
'output_buffering' => true,
);

if ( ! is_array( $value ) ) {
Expand All @@ -175,7 +176,7 @@ function perflab_sanitize_server_timing_setting( $value ) {
* Ensure that every element is an indexed array of hook names.
* Any duplicates across a group of hooks are removed.
*/
foreach ( $value as $key => $hooks ) {
foreach ( wp_array_slice_assoc( $value, array( 'benchmarking_actions', 'benchmarking_filters' ) ) as $key => $hooks ) {
if ( ! is_array( $hooks ) ) {
$hooks = explode( "\n", $hooks );
}
Expand Down Expand Up @@ -203,5 +204,7 @@ static function( $hookname ) {
);
}

$value['output_buffering'] = ! empty( $value['output_buffering'] );

return $value;
}
8 changes: 4 additions & 4 deletions tests/admin/server-timing-tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public function test_perflab_load_server_timing_page() {
perflab_load_server_timing_page();
$this->assertArrayHasKey( PERFLAB_SERVER_TIMING_SCREEN, $wp_settings_sections );
$this->assertEqualSets(
array( 'benchmarking' ),
array( 'output-buffering', 'benchmarking' ),
array_keys( $wp_settings_sections[ PERFLAB_SERVER_TIMING_SCREEN ] )
);
$this->assertEqualSets(
Expand All @@ -75,7 +75,7 @@ public function test_perflab_render_server_timing_page_field() {
$slug = 'benchmarking_actions';

ob_start();
perflab_render_server_timing_page_field( $slug );
perflab_render_server_timing_page_hooks_field( $slug );
$output = ob_get_clean();

$this->assertStringContainsString( '<textarea', $output );
Expand All @@ -87,7 +87,7 @@ public function test_perflab_render_server_timing_page_field_empty_option() {
delete_option( PERFLAB_SERVER_TIMING_SETTING );

ob_start();
perflab_render_server_timing_page_field( 'benchmarking_actions' );
perflab_render_server_timing_page_hooks_field( 'benchmarking_actions' );
$output = ob_get_clean();

$this->assertStringContainsString( '></textarea>', $output );
Expand All @@ -100,7 +100,7 @@ public function test_perflab_render_server_timing_page_field_populated_option()
);

ob_start();
perflab_render_server_timing_page_field( 'benchmarking_actions' );
perflab_render_server_timing_page_hooks_field( 'benchmarking_actions' );
$output = ob_get_clean();

// Array is formatted/imploded as strings, one per line.
Expand Down
22 changes: 13 additions & 9 deletions tests/server-timing/load-tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,39 +103,43 @@ public function data_perflab_sanitize_server_timing_setting() {
),
'empty list, array' => array(
array( 'benchmarking_actions' => array() ),
array( 'benchmarking_actions' => array() ),
array( 'benchmarking_actions' => array(), 'output_buffering' => false ),
),
'empty list, string' => array(
array( 'benchmarking_actions' => '' ),
array( 'benchmarking_actions' => array() ),
array( 'benchmarking_actions' => array(), 'output_buffering' => false ),
),
'empty list, string with whitespace' => array(
array( 'benchmarking_actions' => ' ' ),
array( 'benchmarking_actions' => array() ),
array( 'benchmarking_actions' => array(), 'output_buffering' => false ),
),
'regular list, array' => array(
array( 'benchmarking_actions' => array( 'after_setup_theme', 'init', 'wp_loaded' ) ),
array( 'benchmarking_actions' => array( 'after_setup_theme', 'init', 'wp_loaded' ) ),
array( 'benchmarking_actions' => array( 'after_setup_theme', 'init', 'wp_loaded' ), 'output_buffering' => false ),
),
'regular list, string' => array(
array( 'benchmarking_actions' => "after_setup_theme\ninit\nwp_loaded" ),
array( 'benchmarking_actions' => array( 'after_setup_theme', 'init', 'wp_loaded' ) ),
array( 'benchmarking_actions' => array( 'after_setup_theme', 'init', 'wp_loaded' ), 'output_buffering' => false ),
),
'regular list, string with whitespace' => array(
array( 'benchmarking_actions' => "after_setup_ theme \ninit \n\nwp_loaded\n" ),
array( 'benchmarking_actions' => array( 'after_setup_theme', 'init', 'wp_loaded' ) ),
array( 'benchmarking_actions' => array( 'after_setup_theme', 'init', 'wp_loaded' ), 'output_buffering' => false ),
),
'regular list, array with duplicates' => array(
array( 'benchmarking_actions' => array( 'after_setup_theme', 'init', 'wp_loaded', 'init' ) ),
array( 'benchmarking_actions' => array( 'after_setup_theme', 'init', 'wp_loaded' ) ),
array( 'benchmarking_actions' => array( 'after_setup_theme', 'init', 'wp_loaded' ), 'output_buffering' => false ),
),
'regular list, array with special hook chars' => array(
array( 'benchmarking_actions' => array( 'namespace/hookname', 'namespace.hookname' ) ),
array( 'benchmarking_actions' => array( 'namespace/hookname', 'namespace.hookname' ) ),
array( 'benchmarking_actions' => array( 'namespace/hookname', 'namespace.hookname' ), 'output_buffering' => false ),
),
'output buffering enabled' => array(
array( 'output_buffering' => 'on' ),
array( 'output_buffering' => true ),
),
'regular list, disallowed key' => array(
array( 'not_allowed' => array( 'after_setup_theme', 'init', 'wp_loaded' ) ),
array(),
array( 'output_buffering' => false ),
),
);
}
Expand Down
54 changes: 50 additions & 4 deletions tests/server-timing/perflab-server-timing-tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,56 @@ public function data_get_header() {
);
}

public function test_use_output_buffer() {
$this->assertFalse( $this->server_timing->use_output_buffer() );
public function get_data_to_test_use_output_buffer() {
$enable_option = static function () {
$option = (array) get_option( PERFLAB_SERVER_TIMING_SETTING );
$option['output_buffering'] = true;
update_option( PERFLAB_SERVER_TIMING_SETTING, $option );
};
$disable_option = static function () {
$option = (array) get_option( PERFLAB_SERVER_TIMING_SETTING );
$option['output_buffering'] = false;
update_option( PERFLAB_SERVER_TIMING_SETTING, $option );
};

add_filter( 'perflab_server_timing_use_output_buffer', '__return_true' );
$this->assertTrue( $this->server_timing->use_output_buffer() );
return array(
'default' => array(
'set_up' => static function () {},
'expected' => false,
),
'option-enabled' => array(
'set_up' => $enable_option,
'expected' => true,
),
'option-disabled' => array(
'set_up' => $disable_option,
'expected' => false,
),
'filter-enabled' => array(
'set_up' => static function () use ( $disable_option ) {
$disable_option();
add_filter( 'perflab_server_timing_use_output_buffer', '__return_true' );
},
'expected' => true,
),
'filter-disabled' => array(
'set_up' => static function () use ( $enable_option ) {
$enable_option();
add_filter( 'perflab_server_timing_use_output_buffer', '__return_false' );
},
'expected' => false,
),
);
}

/**
* @covers Perflab_Server_Timing::use_output_buffer
* @dataProvider get_data_to_test_use_output_buffer
* @param callable $set_up Set up.
* @param bool $expected Expected value.
westonruter marked this conversation as resolved.
Show resolved Hide resolved
*/
public function test_use_output_buffer( callable $set_up, $expected ) {
$set_up();
$this->assertSame( $expected, $this->server_timing->use_output_buffer() );
}
}