PluginProbe
Performance Lab / 3.6.1
Performance Lab v3.6.1
trunk 1.0.0 1.0.0-beta.1 1.0.0-beta.2 1.0.0-beta.3 1.0.0-rc.1 1.1.0 1.2.0 1.3.0 1.4.0 1.5.0 1.6.0 1.7.0 1.8.0 1.9.0 2.0.0 2.1.0 2.2.0 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.7.0 2.8.0 All 44 releases
performance-lab / includes / server-timing / defaults.php

defaults.php in Performance Lab 3.6.1, at includes/server-timing/defaults.php

285 lines 9.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Server-Timing API default metrics
4 *
5 * @package performance-lab
6 * @since 1.8.0
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit; // Exit if accessed directly.
11 }
12
13 // Do not add any of the hooks if Server-Timing is disabled.
14 if ( defined( 'PERFLAB_DISABLE_SERVER_TIMING' ) && PERFLAB_DISABLE_SERVER_TIMING ) {
15 return;
16 }
17
18 /**
19 * Registers the default Server-Timing metrics for before rendering the template.
20 *
21 * These metrics should be registered as soon as possible.
22 *
23 * @since 1.8.0
24 */
25 function perflab_register_default_server_timing_before_template_metrics(): void {
26 $calculate_before_template_metrics = static function (): void {
27 // WordPress execution prior to serving the template.
28 perflab_server_timing_register_metric(
29 'before-template',
30 array(
31 'measure_callback' => static function ( $metric ): void {
32 // The 'timestart' global is set right at the beginning of WordPress execution.
33 $metric->set_value( ( microtime( true ) - $GLOBALS['timestart'] ) * 1000.0 );
34 },
35 'access_cap' => 'exist',
36 )
37 );
38
39 // SQL query time is only measured if the SAVEQUERIES constant is set to true.
40 if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
41 // WordPress database query time before template.
42 perflab_server_timing_register_metric(
43 'before-template-db-queries',
44 array(
45 'measure_callback' => static function ( $metric ): void {
46 // This should never happen, but some odd database implementations may be doing it wrong.
47 if ( ! isset( $GLOBALS['wpdb']->queries ) || ! is_array( $GLOBALS['wpdb']->queries ) ) {
48 return;
49 }
50
51 // Store this value in a global to later subtract it from total query time after template.
52 $GLOBALS['perflab_query_time_before_template'] = array_reduce(
53 $GLOBALS['wpdb']->queries,
54 static function ( $acc, $query ) {
55 return $acc + $query[1];
56 },
57 0.0
58 );
59 $metric->set_value( $GLOBALS['perflab_query_time_before_template'] * 1000.0 );
60 },
61 'access_cap' => 'exist',
62 )
63 );
64 }
65 };
66
67 // If output buffering is used, explicitly measure only the time before serving the template.
68 // Otherwise, the Server-Timing header will be sent before serving the template anyway.
69 // We need to check for output buffer usage in the callback so that e.g. plugins and theme can
70 // modify the value prior to the check.
71 add_filter(
72 'template_include',
73 static function ( $passthrough ) use ( $calculate_before_template_metrics ) {
74 if ( perflab_server_timing_use_output_buffer() ) {
75 $calculate_before_template_metrics();
76 }
77 return $passthrough;
78 },
79 PHP_INT_MAX
80 );
81 add_action(
82 'perflab_server_timing_send_header',
83 static function () use ( $calculate_before_template_metrics ): void {
84 if ( ! perflab_server_timing_use_output_buffer() ) {
85 $calculate_before_template_metrics();
86 }
87 },
88 PHP_INT_MAX
89 );
90 }
91 perflab_register_default_server_timing_before_template_metrics();
92
93 /**
94 * Registers the default Server-Timing metrics while rendering the template.
95 *
96 * These metrics should be registered at a later point, e.g. the 'wp_loaded' action.
97 * They will only be registered if the Server-Timing API is configured to use an
98 * output buffer for the site's template.
99 *
100 * @since 1.8.0
101 */
102 function perflab_register_default_server_timing_template_metrics(): void {
103 // Template-related metrics can only be recorded if output buffering is used.
104 if ( ! perflab_server_timing_use_output_buffer() ) {
105 return;
106 }
107
108 add_filter(
109 'template_include',
110 static function ( $passthrough = null ) {
111 // WordPress execution while serving the template.
112 perflab_server_timing_register_metric(
113 'template',
114 array(
115 'measure_callback' => static function ( Perflab_Server_Timing_Metric $metric ): void {
116 $metric->measure_before();
117 add_action( 'perflab_server_timing_send_header', array( $metric, 'measure_after' ), PHP_INT_MAX );
118 },
119 'access_cap' => 'exist',
120 )
121 );
122
123 return $passthrough;
124 },
125 PHP_INT_MAX
126 );
127
128 add_action(
129 'perflab_server_timing_send_header',
130 static function (): void {
131 // WordPress total load time.
132 perflab_server_timing_register_metric(
133 'total',
134 array(
135 'measure_callback' => static function ( $metric ): void {
136 // The 'timestart' global is set right at the beginning of WordPress execution.
137 $metric->set_value( ( microtime( true ) - $GLOBALS['timestart'] ) * 1000.0 );
138 },
139 'access_cap' => 'exist',
140 )
141 );
142 }
143 );
144
145 // SQL query time is only measured if the SAVEQUERIES constant is set to true.
146 if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
147 add_action(
148 'perflab_server_timing_send_header',
149 static function (): void {
150 // WordPress database query time within template.
151 perflab_server_timing_register_metric(
152 'template-db-queries',
153 array(
154 'measure_callback' => static function ( $metric ): void {
155 // This global should typically be set when this is called, but check just in case.
156 if ( ! isset( $GLOBALS['perflab_query_time_before_template'] ) ) {
157 return;
158 }
159
160 // This should never happen, but some odd database implementations may be doing it wrong.
161 if ( ! isset( $GLOBALS['wpdb']->queries ) || ! is_array( $GLOBALS['wpdb']->queries ) ) {
162 return;
163 }
164
165 $total_query_time = array_reduce(
166 $GLOBALS['wpdb']->queries,
167 static function ( $acc, $query ) {
168 return $acc + $query[1];
169 },
170 0.0
171 );
172 $metric->set_value( ( $total_query_time - $GLOBALS['perflab_query_time_before_template'] ) * 1000.0 );
173 },
174 'access_cap' => 'exist',
175 )
176 );
177 },
178 PHP_INT_MAX
179 );
180 }
181 }
182 add_action( 'wp_loaded', 'perflab_register_default_server_timing_template_metrics' );
183
184 /**
185 * Registers additional Server-Timing metrics as configured in the setting.
186 *
187 * These metrics should be registered as soon as possible. They can be added
188 * and modified in the "Tools > Server-Timing" screen.
189 *
190 * @since 2.6.0
191 */
192 function perflab_register_additional_server_timing_metrics_from_setting(): void {
193 $options = (array) get_option( PERFLAB_SERVER_TIMING_SETTING, array() );
194
195 $hooks_to_measure = array();
196
197 if ( isset( $options['benchmarking_actions'] ) ) {
198 foreach ( $options['benchmarking_actions'] as $action ) {
199 $hooks_to_measure[ $action ] = 'action';
200 }
201 }
202
203 if ( isset( $options['benchmarking_filters'] ) ) {
204 foreach ( $options['benchmarking_filters'] as $filter ) {
205 $hooks_to_measure[ $filter ] = 'filter';
206 }
207 }
208
209 // Bail early if there are no hooks to measure.
210 if ( count( $hooks_to_measure ) === 0 ) {
211 return;
212 }
213
214 /*
215 * This logic measures performance of a hook (action or filter).
216 *
217 * Currently, only hooks that run once are properly supported.
218 * For hooks that run multiple times, only the first occurrence will be measured.
219 *
220 * Here is an outline of the logic:
221 *
222 * 1. Use the 'all' hook at the minimum (i.e. earliest) priority possible.
223 * 2. In that callback, check that the hook should be measured and that it has not already been registered yet, and
224 * if so, register the metric for the hook, with a prefix of either "action" or "filter".
225 * 3. Provide a measuring callback which captures the time span between beginning to end of the hook:
226 * 1. Capture the current time immediately, i.e. within the 'all' hook.
227 * 2. Add another hook callback at the maximum (i.e. latest) priority possible.
228 * 3. In that callback, capture the current time, leading the Server-Timing API to calculate the difference.
229 */
230 add_action(
231 'all',
232 static function ( $hook_name ) use ( $hooks_to_measure ): void {
233 if ( ! isset( $hooks_to_measure[ $hook_name ] ) ) {
234 return;
235 }
236
237 $hook_type = $hooks_to_measure[ $hook_name ];
238 $metric_slug = "{$hook_type}-{$hook_name}";
239
240 if ( perflab_server_timing()->has_registered_metric( $metric_slug ) ) {
241 return;
242 }
243
244 $measure_callback = static function ( $metric ) use ( $hook_name, $hook_type ): void {
245 $metric->measure_before();
246
247 if ( 'action' === $hook_type ) {
248 $cb = static function () use ( $metric, $hook_name, &$cb ): void {
249 $metric->measure_after();
250 remove_action( $hook_name, $cb, PHP_INT_MAX );
251 };
252 add_action( $hook_name, $cb, PHP_INT_MAX );
253 } else {
254 $cb = static function ( $passthrough ) use ( $metric, $hook_name, &$cb ) {
255 $metric->measure_after();
256 remove_filter( $hook_name, $cb, PHP_INT_MAX );
257 return $passthrough;
258 };
259 add_filter( $hook_name, $cb, PHP_INT_MAX );
260 }
261 };
262
263 perflab_server_timing_register_metric(
264 $metric_slug,
265 array(
266 'measure_callback' => $measure_callback,
267 'access_cap' => 'exist',
268 )
269 );
270 },
271 PHP_INT_MIN
272 );
273 }
274
275 /*
276 * If this file is loaded from the Server-Timing logic in the object-cache.php
277 * drop-in, it must not call this function right away since otherwise the cache
278 * will not be loaded yet.
279 */
280 if ( 0 === did_action( 'muplugins_loaded' ) ) {
281 add_action( 'muplugins_loaded', 'perflab_register_additional_server_timing_metrics_from_setting' );
282 } else {
283 perflab_register_additional_server_timing_metrics_from_setting();
284 }
285