Skip to content

Commit

Permalink
Implement logic to measure specific hook execution time with Server-T…
Browse files Browse the repository at this point in the history
…iming controlled by a WP Admin screen.
  • Loading branch information
felixarntz committed Jul 18, 2023
1 parent 450761e commit a1fd89c
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 0 deletions.
131 changes: 131 additions & 0 deletions admin/server-timing.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php
/**
* Server-Timing API admin integration file.
*
* @package performance-lab
*/

/**
* Adds the Server-Timing page to the Tools menu.
*
* @since n.e.x.t
*/
function perflab_add_server_timing_page() {
$hook_suffix = add_management_page(
__( 'Server-Timing', 'performance-lab' ),
__( 'Server-Timing', 'performance-lab' ),
'manage_options',
PERFLAB_SERVER_TIMING_SCREEN,
'perflab_render_server_timing_page'
);

// Add the following hooks only if the screen was successfully added.
if ( false !== $hook_suffix ) {
add_action( "load-{$hook_suffix}", 'perflab_load_server_timing_page' );
}

return $hook_suffix;
}
add_action( 'admin_menu', 'perflab_add_server_timing_page' );

/**
* Initializes settings sections and fields for the Server-Timing page.
*
* @since n.e.x.t
*/
function perflab_load_server_timing_page() {
add_settings_section(
'benchmarking',
__( 'Benchmarking', 'performance-lab' ),
static function() {
?>
<p>
<?php esc_html_e( 'In this section, you can provide hook names to include measurements for them in the Server-Timing header.', 'performance-lab' ); ?>
</p>
<?php
},
PERFLAB_SERVER_TIMING_SCREEN
);

/*
* For all settings fields, the field slug, option sub key, and label
* suffix have to match for the rendering in the callback to be
* semantically correct.
*/
add_settings_field(
'benchmarking_actions',
__( 'Actions', 'performance-lab' ),
static function() {
perflab_render_server_timing_page_field( 'benchmarking_actions' );
},
PERFLAB_SERVER_TIMING_SCREEN,
'benchmarking',
array( 'label_for' => 'server_timing_benchmarking_actions' )
);
add_settings_field(
'benchmarking_filters',
__( 'Filters', 'performance-lab' ),
static function() {
perflab_render_server_timing_page_field( 'benchmarking_filters' );
},
PERFLAB_SERVER_TIMING_SCREEN,
'benchmarking',
array( 'label_for' => 'server_timing_benchmarking_filters' )
);
}

/**
* Renders the Server-Timing page.
*
* @since n.e.x.t
*/
function perflab_render_server_timing_page() {
?>
<div class="wrap">
<?php settings_errors(); ?>
<h1>
<?php esc_html_e( 'Server-Timing', 'performance-lab' ); ?>
</h1>

<form action="options.php" method="post" novalidate="novalidate">
<?php settings_fields( PERFLAB_SERVER_TIMING_SCREEN ); ?>
<?php do_settings_sections( PERFLAB_SERVER_TIMING_SCREEN ); ?>
<?php submit_button(); ?>
</form>
</div>
<?php
}

/**
* Renders a 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 ) {
$options = (array) get_option( PERFLAB_SERVER_TIMING_SETTING, array() );

// Value for the sub-key is an array of hook names.
$value = '';
if ( isset( $options[ $slug ] ) ) {
$value = implode( "\n", $options[ $slug ] );
}

$field_id = "server_timing_{$slug}";
$field_name = PERFLAB_SERVER_TIMING_SETTING . '[' . $slug . ']';
$description_id = "{$field_id}_description";

?>
<textarea
id="<?php echo esc_attr( $field_id ); ?>"
name="<?php echo esc_attr( $field_name ); ?>"
aria-describedby="<?php echo esc_attr( $description_id ); ?>"
class="large-text code"
rows="8"
><?php echo esc_textarea( $value ); ?></textarea>
<p id="<?php echo esc_attr( $description_id ); ?>" class="description">
<?php esc_html_e( 'Enter a single hook name per line.', 'performance-lab' ); ?>
</p>
<?php
}
1 change: 1 addition & 0 deletions load.php
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ function perflab_maybe_remove_object_cache_dropin() {
// Only load admin integration when in admin.
if ( is_admin() ) {
require_once PERFLAB_PLUGIN_DIR_PATH . 'admin/load.php';
require_once PERFLAB_PLUGIN_DIR_PATH . 'admin/server-timing.php';
}

/**
Expand Down
81 changes: 81 additions & 0 deletions server-timing/defaults.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,84 @@ function( $acc, $query ) {
}
}
add_action( 'wp_loaded', 'perflab_register_default_server_timing_template_metrics' );

/**
* Registers additional Server-Timing metrics as configured in the setting.
*
* These metrics should be registered as soon as possible. They can be added
* and modified in the "Tools > Server-Timing" screen.
*
* @since n.e.x.t
*/
function perflab_register_additional_server_timing_metrics_from_setting() {
$options = (array) get_option( PERFLAB_SERVER_TIMING_SETTING, array() );

if ( isset( $options['benchmarking_actions'] ) ) {
foreach ( $options['benchmarking_actions'] as $action ) {
$metric_slug = 'action-' . $action;

$measure_callback = function( $metric ) use ( $action ) {
$metric->measure_before();
add_action( $action, array( $metric, 'measure_after' ), PHP_INT_MAX, 0 );
};

add_action(
$action,
static function() use ( $metric_slug, $measure_callback ) {
perflab_server_timing_register_metric(
$metric_slug,
array(
'measure_callback' => $measure_callback,
'access_cap' => 'exist',
)
);
},
defined( 'PHP_INT_MIN' ) ? PHP_INT_MIN : -9999
);
}
}

if ( isset( $options['benchmarking_filters'] ) ) {
foreach ( $options['benchmarking_filters'] as $filter ) {
$metric_slug = 'filter-' . $filter;

$measure_callback = function( $metric ) use ( $filter ) {
$metric->measure_before();
add_filter(
$filter,
static function( $passthrough ) use ( $metric ) {
$metric->measure_after();
return $passthrough;
},
PHP_INT_MAX
);
};

add_filter(
$filter,
static function( $passthrough ) use ( $metric_slug, $measure_callback ) {
perflab_server_timing_register_metric(
$metric_slug,
array(
'measure_callback' => $measure_callback,
'access_cap' => 'exist',
)
);
return $passthrough;
},
defined( 'PHP_INT_MIN' ) ? PHP_INT_MIN : -9999
);
}
}
}

/*
* If this file is loaded from the Server-Timing logic in the object-cache.php
* drop-in, it must not call this function right away since otherwise the cache
* will not be loaded yet.
*/
if ( ! did_action( 'muplugins_loaded' ) ) {
add_action( 'muplugins_loaded', 'perflab_register_additional_server_timing_metrics_from_setting' );
} else {
perflab_register_additional_server_timing_metrics_from_setting();
}
48 changes: 48 additions & 0 deletions server-timing/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,54 @@
* @since 1.8.0
*/

define( 'PERFLAB_SERVER_TIMING_SETTING', 'perflab_server_timing_settings' );
define( 'PERFLAB_SERVER_TIMING_SCREEN', 'perflab-server-timing' );

/**
* Registers the Server-Timing setting.
*
* @since n.e.x.t
*/
function perflab_register_server_timing_setting() {
register_setting(
PERFLAB_SERVER_TIMING_SCREEN,
PERFLAB_SERVER_TIMING_SETTING,
array(
'type' => 'object',
'sanitize_callback' => 'perflab_sanitize_server_timing_setting',
'default' => array(),
)
);
}
add_action( 'init', 'perflab_register_server_timing_setting' );

/**
* Sanitizes the Server-Timing setting.
*
* @since n.e.x.t
*
* @param mixed $value Server-Timing setting value.
* @return array Sanitized Server-Timing setting value.
*/
function perflab_sanitize_server_timing_setting( $value ) {
if ( ! is_array( $value ) ) {
return array();
}

// Ensure that every element is an indexed array of hook names.
return array_filter(
array_map(
static function( $hooks ) {
if ( ! is_array( $hooks ) ) {
$hooks = explode( "\n", $hooks );
}
return array_filter( array_map( 'sanitize_key', $hooks ) );
},
$value
)
);
}

/**
* Provides access the Server-Timing API.
*
Expand Down

0 comments on commit a1fd89c

Please sign in to comment.