| 1 |
<?php |
| 2 |
/** |
| 3 |
* Server-Timing API default metrics |
| 4 |
* |
| 5 |
* @package performance-lab |
| 6 |
* @since 1.8.0 |
| 7 |
*/ |
| 8 |
|
| 9 |
/** |
| 10 |
* Registers the default Server-Timing metrics for before rendering the template. |
| 11 |
* |
| 12 |
* These metrics should be registered as soon as possible. |
| 13 |
* |
| 14 |
* @since 1.8.0 |
| 15 |
*/ |
| 16 |
function perflab_register_default_server_timing_before_template_metrics() { |
| 17 |
$calculate_before_template_metrics = function() { |
| 18 |
// WordPress execution prior to serving the template. |
| 19 |
perflab_server_timing_register_metric( |
| 20 |
'before-template', |
| 21 |
array( |
| 22 |
'measure_callback' => function( $metric ) { |
| 23 |
// The 'timestart' global is set right at the beginning of WordPress execution. |
| 24 |
$metric->set_value( ( microtime( true ) - $GLOBALS['timestart'] ) * 1000.0 ); |
| 25 |
}, |
| 26 |
'access_cap' => 'exist', |
| 27 |
) |
| 28 |
); |
| 29 |
|
| 30 |
// SQL query time is only measured if the SAVEQUERIES constant is set to true. |
| 31 |
if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) { |
| 32 |
// WordPress database query time before template. |
| 33 |
perflab_server_timing_register_metric( |
| 34 |
'before-template-db-queries', |
| 35 |
array( |
| 36 |
'measure_callback' => function( $metric ) { |
| 37 |
// This should never happen, but some odd database implementations may be doing it wrong. |
| 38 |
if ( ! isset( $GLOBALS['wpdb']->queries ) || ! is_array( $GLOBALS['wpdb']->queries ) ) { |
| 39 |
return; |
| 40 |
} |
| 41 |
|
| 42 |
// Store this value in a global to later subtract it from total query time after template. |
| 43 |
$GLOBALS['perflab_query_time_before_template'] = array_reduce( |
| 44 |
$GLOBALS['wpdb']->queries, |
| 45 |
function( $acc, $query ) { |
| 46 |
return $acc + $query[1]; |
| 47 |
}, |
| 48 |
0.0 |
| 49 |
); |
| 50 |
$metric->set_value( $GLOBALS['perflab_query_time_before_template'] * 1000.0 ); |
| 51 |
}, |
| 52 |
'access_cap' => 'exist', |
| 53 |
) |
| 54 |
); |
| 55 |
} |
| 56 |
}; |
| 57 |
|
| 58 |
// If output buffering is used, explicitly measure only the time before serving the template. |
| 59 |
// Otherwise, the Server-Timing header will be sent before serving the template anyway. |
| 60 |
// We need to check for output buffer usage in the callback so that e.g. plugins and theme can |
| 61 |
// modify the value prior to the check. |
| 62 |
add_filter( |
| 63 |
'template_include', |
| 64 |
function( $passthrough ) use ( $calculate_before_template_metrics ) { |
| 65 |
if ( perflab_server_timing_use_output_buffer() ) { |
| 66 |
$calculate_before_template_metrics(); |
| 67 |
} |
| 68 |
return $passthrough; |
| 69 |
}, |
| 70 |
PHP_INT_MAX |
| 71 |
); |
| 72 |
add_action( |
| 73 |
'perflab_server_timing_send_header', |
| 74 |
function() use ( $calculate_before_template_metrics ) { |
| 75 |
if ( ! perflab_server_timing_use_output_buffer() ) { |
| 76 |
$calculate_before_template_metrics(); |
| 77 |
} |
| 78 |
}, |
| 79 |
PHP_INT_MAX |
| 80 |
); |
| 81 |
|
| 82 |
// Measure duration of autoloaded options query. |
| 83 |
// Requires the Performance Lab object-cache.php drop-in to be present in order to work, |
| 84 |
// which is why the constant is checked below. |
| 85 |
if ( PERFLAB_OBJECT_CACHE_DROPIN_VERSION ) { |
| 86 |
add_filter( |
| 87 |
'query', |
| 88 |
function( $query ) { |
| 89 |
global $wpdb; |
| 90 |
if ( "SELECT option_name, option_value FROM $wpdb->options WHERE autoload = 'yes'" !== $query ) { |
| 91 |
return $query; |
| 92 |
} |
| 93 |
// In case the autoloaded options query is run again, prevent re-registering it and do not measure again. |
| 94 |
if ( perflab_server_timing()->has_registered_metric( 'load-alloptions-query' ) ) { |
| 95 |
return $query; |
| 96 |
} |
| 97 |
perflab_server_timing_register_metric( |
| 98 |
'load-alloptions-query', |
| 99 |
array( |
| 100 |
'measure_callback' => function( $metric ) { |
| 101 |
$metric->measure_before(); |
| 102 |
add_filter( |
| 103 |
'pre_cache_alloptions', |
| 104 |
function( $passthrough ) use ( $metric ) { |
| 105 |
$metric->measure_after(); |
| 106 |
return $passthrough; |
| 107 |
} |
| 108 |
); |
| 109 |
}, |
| 110 |
'access_cap' => 'exist', |
| 111 |
) |
| 112 |
); |
| 113 |
return $query; |
| 114 |
} |
| 115 |
); |
| 116 |
} |
| 117 |
} |
| 118 |
perflab_register_default_server_timing_before_template_metrics(); |
| 119 |
|
| 120 |
/** |
| 121 |
* Registers the default Server-Timing metrics while rendering the template. |
| 122 |
* |
| 123 |
* These metrics should be registered at a later point, e.g. the 'wp_loaded' action. |
| 124 |
* They will only be registered if the Server-Timing API is configured to use an |
| 125 |
* output buffer for the site's template. |
| 126 |
* |
| 127 |
* @since 1.8.0 |
| 128 |
*/ |
| 129 |
function perflab_register_default_server_timing_template_metrics() { |
| 130 |
// Template-related metrics can only be recorded if output buffering is used. |
| 131 |
if ( ! perflab_server_timing_use_output_buffer() ) { |
| 132 |
return; |
| 133 |
} |
| 134 |
|
| 135 |
add_filter( |
| 136 |
'template_include', |
| 137 |
function( $passthrough = null ) { |
| 138 |
// WordPress execution while serving the template. |
| 139 |
perflab_server_timing_register_metric( |
| 140 |
'template', |
| 141 |
array( |
| 142 |
'measure_callback' => function( $metric ) { |
| 143 |
$metric->measure_before(); |
| 144 |
add_action( 'perflab_server_timing_send_header', array( $metric, 'measure_after' ), PHP_INT_MAX ); |
| 145 |
}, |
| 146 |
'access_cap' => 'exist', |
| 147 |
) |
| 148 |
); |
| 149 |
|
| 150 |
return $passthrough; |
| 151 |
}, |
| 152 |
PHP_INT_MAX |
| 153 |
); |
| 154 |
|
| 155 |
add_action( |
| 156 |
'perflab_server_timing_send_header', |
| 157 |
function() { |
| 158 |
// WordPress total load time. |
| 159 |
perflab_server_timing_register_metric( |
| 160 |
'total', |
| 161 |
array( |
| 162 |
'measure_callback' => function( $metric ) { |
| 163 |
// The 'timestart' global is set right at the beginning of WordPress execution. |
| 164 |
$metric->set_value( ( microtime( true ) - $GLOBALS['timestart'] ) * 1000.0 ); |
| 165 |
}, |
| 166 |
'access_cap' => 'exist', |
| 167 |
) |
| 168 |
); |
| 169 |
} |
| 170 |
); |
| 171 |
|
| 172 |
// SQL query time is only measured if the SAVEQUERIES constant is set to true. |
| 173 |
if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) { |
| 174 |
add_action( |
| 175 |
'perflab_server_timing_send_header', |
| 176 |
function() { |
| 177 |
// WordPress database query time within template. |
| 178 |
perflab_server_timing_register_metric( |
| 179 |
'template-db-queries', |
| 180 |
array( |
| 181 |
'measure_callback' => function( $metric ) { |
| 182 |
// This global should typically be set when this is called, but check just in case. |
| 183 |
if ( ! isset( $GLOBALS['perflab_query_time_before_template'] ) ) { |
| 184 |
return; |
| 185 |
} |
| 186 |
|
| 187 |
// This should never happen, but some odd database implementations may be doing it wrong. |
| 188 |
if ( ! isset( $GLOBALS['wpdb']->queries ) || ! is_array( $GLOBALS['wpdb']->queries ) ) { |
| 189 |
return; |
| 190 |
} |
| 191 |
|
| 192 |
$total_query_time = array_reduce( |
| 193 |
$GLOBALS['wpdb']->queries, |
| 194 |
function( $acc, $query ) { |
| 195 |
return $acc + $query[1]; |
| 196 |
}, |
| 197 |
0.0 |
| 198 |
); |
| 199 |
$metric->set_value( ( $total_query_time - $GLOBALS['perflab_query_time_before_template'] ) * 1000.0 ); |
| 200 |
}, |
| 201 |
'access_cap' => 'exist', |
| 202 |
) |
| 203 |
); |
| 204 |
}, |
| 205 |
PHP_INT_MAX |
| 206 |
); |
| 207 |
} |
| 208 |
} |
| 209 |
add_action( 'wp_loaded', 'perflab_register_default_server_timing_template_metrics' ); |
| 210 |
|