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 all 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
131 changes: 115 additions & 16 deletions admin/server-timing.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,34 @@ function perflab_add_server_timing_page() {
* @since n.e.x.t
*/
function perflab_load_server_timing_page() {
/*
* This settings section technically includes a field, however it is directly rendered as part of the section
* callback due to requiring custom markup.
*/
add_settings_section(
'output-buffering',
__( 'Output Buffering', 'performance-lab' ),
'perflab_render_server_timing_page_output_buffering_section',
PERFLAB_SERVER_TIMING_SCREEN
);

// Minor style tweaks to improve appearance similar to other core settings screen instances.
add_action(
'admin_print_styles',
static function () {
?>
<style>
.wrap p {
max-width: 800px;
}
.wrap .form-table .td-full {
padding-top: 0;
}
</style>
<?php
}
);

add_settings_section(
'benchmarking',
__( 'Benchmarking', 'performance-lab' ),
Expand All @@ -58,28 +86,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 +126,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 +136,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 +167,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 +199,71 @@ class="large-text code"
</p>
<?php
}

/**
* Renders the section for enabling output buffering for Server-Timing.
*
* @since n.e.x.t
*/
function perflab_render_server_timing_page_output_buffering_section() {
$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();

/*
* The hard-coded .form-table output here overall matches the WordPress core markup generated by
* `do_settings_sections()` and `do_settings_fields()`, however since it is impossible to modify the CSS classes on
* the `<td>` elements, it needs to be hard-coded to achieve the same appearance as e.g. the UI control for the
* `uploads_use_yearmonth_folders` option in the _Settings > Media_ screen, which is hard-coded as well.
*/
?>
<table class="form-table" role="presentation">
<tr>
<td class="td-full">
<label for="<?php echo esc_attr( $field_id ); ?>">
<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 ); ?>
>
<?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() )
);
?>
<?php endif; ?>
<?php endif; ?>
<?php esc_html_e( 'Output buffering 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' ); ?>
</p>
</td>
</tr>
</table>
<?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
56 changes: 52 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,58 @@ 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.
*/
public function test_use_output_buffer( callable $set_up, $expected ) {
$set_up();
$this->assertSame( $expected, $this->server_timing->use_output_buffer() );
}
}