PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.6
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.6
1.9.5 1.9.4 1.9.3 trunk 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.6 1.6.1 1.6.10 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 All 40 releases
← All changes | lib/ajax.php +203 -14 1.51.6 View file →
@@ -23,8 +23,12 @@
23 23 $response = ['status' => 'error'];
24 24
25 25 $cp_options = get_option('code-profiler');
26 26
27 + code_profiler_log_debug(
28 + esc_html__('Entering AJAX endpoint (profiler initialization)', 'code-profiler')
29 + );
30 +
27 31 // If this is an AJAX call, make sure it comes from an admin/superadmin.
28 32 if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_start_profiler') {
29 33 // Admin/Superadmin only
30 34 if (! is_super_admin() ) {
@@ -34,8 +38,12 @@
34 38 code_profiler_wp_send_json( $response );
35 39 }
36 40 }
37 41
42 + code_profiler_log_debug(
43 + esc_html__('Verifying security nonce', 'code-profiler')
44 + );
45 +
38 46 // Verify the security nonce
39 47 if ( empty( $_POST['cp_nonce'] ) || ! wp_verify_nonce( $_POST['cp_nonce'], 'start_profiler_nonce') ) {
40 48 $msg = esc_html__('Missing or wrong security nonce. Reload the page and try again', 'code-profiler');
41 49 $response['message'] = $msg;
@@ -42,8 +50,12 @@
42 50 code_profiler_log_error( $msg );
43 51 code_profiler_wp_send_json( $response );
44 52 }
45 53
54 + code_profiler_log_debug(
55 + esc_html__('Checking MU plugin availability', 'code-profiler')
56 + );
57 +
46 58 // Verify the MU plugin is loaded
47 59 if (! defined('CODE_PROFILER_MU_ON') ) {
48 60 $msg = esc_html__('The MU plugin is not loaded, please check the log', 'code-profiler');
49 61 $response['message'] = $msg;
@@ -50,11 +62,19 @@
50 62 code_profiler_log_error( $msg );
51 63 code_profiler_wp_send_json( $response );
52 64 }
53 65
66 + code_profiler_log_debug(
67 + esc_html__('Cleaning up the temporary folder', 'code-profiler')
68 + );
69 +
54 70 // Clean-up temp files left in the profiles folder
55 71 code_profiler_cleantmpfiles();
56 72
73 + code_profiler_log_debug(
74 + esc_html__('Retrieving parameters #1', 'code-profiler')
75 + );
76 +
57 77 // Frontend or backend
58 78 if ( empty( $_POST['where'] ) || ! in_array( $_POST['where'], ['frontend', 'backend', 'custom'] ) ) {
59 79 $msg = sprintf( esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'where');
60 80 $response['message'] = $msg;
@@ -62,8 +82,12 @@
62 82 code_profiler_wp_send_json( $response );
63 83 }
64 84 $cp_options['mem_where'] = $_POST['where'];
65 85
86 + code_profiler_log_debug(
87 + esc_html__('Retrieving parameters #2', 'code-profiler')
88 + );
89 +
66 90 if ( empty( $_POST['post'] ) ) {
67 91 $msg = sprintf( esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'post');
68 92 $response['message'] = $msg;
69 93 code_profiler_log_error( $msg );
@@ -74,8 +98,12 @@
74 98 // Make sure we have no more that 4 decimals, because when returning
75 99 // it via AJAX, it will display more decimals than that
76 100 $microtime = number_format( microtime( true ), 4, '.', '');
77 101
102 + code_profiler_log_debug(
103 + esc_html__('Retrieving parameters #3', 'code-profiler')
104 + );
105 +
78 106 // Authentication
79 107 if ( empty( $_POST['user'] ) || ! in_array( $_POST['user'], ['authenticated', 'unauthenticated'] ) ) {
80 108 $msg = sprintf( esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'user');
81 109 $response['message'] = $msg;
@@ -83,8 +111,12 @@
83 111 code_profiler_wp_send_json( $response );
84 112 }
85 113 $cp_options['mem_user'] = $_POST['user'];
86 114
115 + code_profiler_log_debug(
116 + esc_html__('Retrieving parameters #4', 'code-profiler')
117 + );
118 +
87 119 if ( empty( $_POST['profile'] ) || strlen( $_POST['profile'] ) > 100 ) {
88 120 $profile = code_profiler_profile_name();
89 121 } else {
90 122 $profile = sanitize_file_name( $_POST['profile'] );
@@ -92,14 +124,18 @@
92 124
93 125 // URI to profile
94 126 $url = esc_url_raw( $_POST['post'] );
95 127 code_profiler_log_info( sprintf(
96 - esc_html__('Starting Code Profiler v%s for %s (profile: %s)', 'code-profiler'),
128 + esc_html__('Initializing Code Profiler v%s for %s (profile: %s)', 'code-profiler'),
97 129 CODE_PROFILER_VERSION,
98 130 $url,
99 131 $profile
100 132 ) );
101 133
134 + code_profiler_log_debug(
135 + esc_html__('Retrieving parameters #5', 'code-profiler')
136 + );
137 +
102 138 // User-agent
103 139 if ( empty( $_POST['ua'] ) ) {
104 140 $ua = 'Firefox';
105 141 } else {
@@ -117,12 +153,20 @@
117 153 $ua_signature = CODE_PROFILER_UA['Desktop']['Firefox'];
118 154 }
119 155 $cp_options['ua'] = $ua;
120 156
157 + code_profiler_log_debug(
158 + esc_html__('Creating security key', 'code-profiler')
159 + );
160 +
121 161 // Create security key
122 162 $profiler_key = bin2hex( random_bytes( 16 ) );
123 163 $cp_options['hash'] = sha1( $profiler_key );
124 164
165 + code_profiler_log_debug(
166 + esc_html__('Building HTTP query', 'code-profiler')
167 + );
168 +
125 169 // Build query
126 170 $url = add_query_arg( [
127 171 'CODE_PROFILER_ON' => $microtime,
128 172 'profiler_key' => $profiler_key
@@ -135,9 +179,9 @@
135 179 'Expires' => '0',
136 180 'httpversion' => '1.1',
137 181 // Devs must be allowed to use it on localhost over TLS too
138 182 'sslverify' => apply_filters('https_local_ssl_verify', false ),
139 - 'timeout' => 180, // 180-second timeout instead of the default 5s
183 + 'timeout' => 300, // 300-second timeout instead of the default 5s
140 184 'redirection' => 0, // We don't want to be redirected
141 185 'headers' => [
142 186 'code-profiler-key' => $profiler_key,
143 187 'Accept-Language' => 'en-US,en;q=0.5',
@@ -144,8 +188,12 @@
144 188 'User-Agent' => $ua_signature
145 189 ]
146 190 ];
147 191
192 + code_profiler_log_debug(
193 + esc_html__('Checking HTTP options', 'code-profiler')
194 + );
195 +
148 196 // Forward basic authentication if any (not available from WP CLI)
149 197 if ( function_exists('apache_request_headers') ) {
150 198 $apache_headers = apache_request_headers();
151 199 if ( isset( $apache_headers['Authorization'] ) ) {
@@ -150,14 +198,19 @@
150 198 $apache_headers = apache_request_headers();
151 199 if ( isset( $apache_headers['Authorization'] ) ) {
152 200 $headers['headers']['Authorization'] = $apache_headers['Authorization'];
153 201 }
154 - // WP-CLI (wp code-profiler run --u=FOO --p=BAR)
202 + // WP-CLI ($ wp code-profiler run --u=FOO --p=BAR)
155 203 } elseif ( defined('WP_CLI') && ! empty( $_POST['Authorization'] ) ) {
156 204 $headers['headers']['Authorization'] = $_POST['Authorization'];
157 205 }
158 206
159 207 if ( $_POST['user'] == 'authenticated') {
208 +
209 + code_profiler_log_debug(
210 + esc_html__('Creating authentication cookies', 'code-profiler')
211 + );
212 +
160 213 // Used for authentication
161 214 if ( is_ssl() ) {
162 215 $cookie_auth = SECURE_AUTH_COOKIE;
163 216 $scheme = 'secure_auth';
@@ -181,25 +234,52 @@
181 234 $response['message'] = $msg;
182 235 code_profiler_log_error( $msg );
183 236 code_profiler_wp_send_json( $response );
184 237 }
185 - $cp_options['mem_username'] = strtolower( $username );
238 + $cp_options['mem_username'] = strtolower( $username );
186 239 $headers['cookies'][ $cookie_auth ] = wp_generate_auth_cookie( $user_object->ID, time() + 180, $scheme );
240 + if ( empty( $headers['cookies'][ $cookie_auth ] ) ) {
241 + $msg = esc_html__('Unable to create the authentication cookie', 'code-profiler');
242 + code_profiler_log_error( $msg );
243 + $response['message'] = $msg;
244 + code_profiler_wp_send_json( $response );
245 + }
187 246 $headers['cookies'][ LOGGED_IN_COOKIE ] = wp_generate_auth_cookie( $user_object->ID, time() + 180, 'logged_in');
247 + if ( empty( $headers['cookies'][ LOGGED_IN_COOKIE ] ) ) {
248 + $msg = esc_html__('Unable to create the "logged_in" cookie', 'code-profiler');
249 + code_profiler_log_error( $msg );
250 + $response['message'] = $msg;
251 + code_profiler_wp_send_json( $response );
252 + }
188 253 // WP CLI
189 254 } else {
190 255 $id = get_current_user_id();
191 256 $headers['cookies'][ $cookie_auth ] = wp_generate_auth_cookie( $id, time() + 180, $scheme );
257 + if ( empty( $headers['cookies'][ $cookie_auth ] ) ) {
258 + $msg = esc_html__('Unable to create the authentication cookie', 'code-profiler');
259 + code_profiler_log_error( $msg );
260 + $response['message'] = $msg;
261 + code_profiler_wp_send_json( $response );
262 + }
192 263 $headers['cookies'][ LOGGED_IN_COOKIE ] = wp_generate_auth_cookie( $id, time() + 180, 'logged_in');
264 + if ( empty( $headers['cookies'][ LOGGED_IN_COOKIE ] ) ) {
265 + $msg = esc_html__('Unable to create the "logged_in" cookie', 'code-profiler');
266 + code_profiler_log_error( $msg );
267 + $response['message'] = $msg;
268 + code_profiler_wp_send_json( $response );
269 + }
193 270 }
194 - $session_id = session_id();
271 + $session_id = session_id();
195 272 if ( $session_id !== false ) {
196 - $session_name = session_name();
197 - $headers['cookies'][ $session_name ] = $session_id;
273 + $session_name = session_name();
274 + $headers['cookies'][ $session_name ] = $session_id;
198 275 }
199 276 }
200 277
201 278 if ( function_exists('opcache_reset') ) {
279 + code_profiler_log_debug(
280 + esc_html__('Clearing opcode cache', 'code-profiler')
281 + );
202 282 opcache_reset();
203 283 }
204 284
205 285 // GET or POST method
@@ -208,8 +288,13 @@
208 288 $cp_options['mem_method'] = 'post';
209 289
210 290 // Optional POST payload
211 291 if (! empty( $_POST['payload'] ) ) {
292 +
293 + code_profiler_log_debug(
294 + esc_html__('Building POST payload', 'code-profiler')
295 + );
296 +
212 297 $payload_array = explode( PHP_EOL, trim( $_POST['payload'] ) );
213 298 foreach( $payload_array as $item ) {
214 299 $payload = explode('=', trim( $item ), 2 );
215 300 if ( isset( $payload[1] ) ) {
@@ -231,8 +316,13 @@
231 316 }
232 317
233 318 // Optional user-defined cookies
234 319 if (! empty( $_POST['cookies'] ) ) {
320 +
321 + code_profiler_log_debug(
322 + esc_html__('Building HTTP Cookies', 'code-profiler')
323 + );
324 +
235 325 $cookies_array = explode( PHP_EOL, trim( $_POST['cookies'] ) );
236 326 foreach( $cookies_array as $item ) {
237 327 $cookie = explode('=', trim( $item ), 2 );
238 328 if ( isset( $cookie[1] ) ) {
@@ -247,8 +337,12 @@
247 337 }
248 338
249 339 update_option('code-profiler', $cp_options );
250 340
341 + code_profiler_log_debug(
342 + esc_html__('Sending HTTP request', 'code-profiler')
343 + );
344 +
251 345 // We must allow developers to run the profiler
252 346 // on a local IP (e.g, http://127.0.0.1/)
253 347 add_filter('http_request_host_is_external', '__return_true');
254 348 $res = $safe_method( $url, $headers );
@@ -263,8 +357,12 @@
263 357 code_profiler_log_error( sprintf( $msg, $res->get_error_message() ) );
264 358 code_profiler_wp_send_json( $response );
265 359 }
266 360
361 + code_profiler_log_debug(
362 + esc_html__('Fetching HTTP response', 'code-profiler')
363 + );
364 +
267 365 // HTTP status code
268 366 if (! empty( $cp_options['http_response'] ) ) {
269 367 if ( preg_match( "/{$cp_options['http_response']}/", $res['response']['code'] ) ) {
270 368 $msg = esc_html__('The website returned the following HTTP status code: %s %s.', 'code-profiler').
@@ -278,8 +376,13 @@
278 376 code_profiler_log_error( sprintf( $msg, $res['response']['code'], $res['response']['message'] ) );
279 377 code_profiler_wp_send_json( $response );
280 378 }
281 379 }
380 +
381 + code_profiler_log_debug(
382 + esc_html__('Decoding body', 'code-profiler')
383 + );
384 +
282 385 // Check response
283 386 $message = json_decode( $res['body'], true );
284 387 if ( isset( $message['status'] ) && isset( $message['message'] ) ) {
285 388 $response['status'] = $message['status'];
@@ -285,21 +388,27 @@
285 388 $response['status'] = $message['status'];
286 389 $response['message'] = $message['message'];
287 390 code_profiler_wp_send_json( $response );
288 391 }
289 - code_profiler_log_info( esc_html__('Collecting data to analyze', 'code-profiler') );
392 + code_profiler_log_info(
393 + esc_html__('Collecting data to analyze', 'code-profiler')
394 + );
290 395 // Return success
291 396 $response = ['status' => 'success'];
292 397 $response['message'] = 'success';
293 398 $response['microtime'] = $microtime;
294 399
400 + code_profiler_log_debug(
401 + esc_html__('Leaving AJAX endpoint', 'code-profiler')
402 + );
403 +
295 404 // AJAX action?
296 405 if ( defined('DOING_AJAX') && DOING_AJAX ) {
297 406 code_profiler_wp_send_json( $response );
298 - } else {
299 - return json_encode( $response );
300 407 }
301 408
409 + return json_encode( $response );
410 +
302 411 }
303 412
304 413 // =====================================================================
305 414
@@ -308,8 +417,12 @@
308 417 function codeprofiler_prepare_report() {
309 418
310 419 $response = ['status' => 'error'];
311 420
421 + code_profiler_log_debug(
422 + esc_html__('Entering AJAX endpoint (report preparation)', 'code-profiler')
423 + );
424 +
312 425 // If this is an AJAX call, make sure it comes from an admin/superadmin.
313 426 if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_prepare_report') {
314 427 // Admin/Superadmin only
315 428 if (! is_super_admin() ) {
@@ -319,8 +432,12 @@
319 432 code_profiler_wp_send_json( $response );
320 433 }
321 434 }
322 435
436 + code_profiler_log_debug(
437 + esc_html__('Verifying security nonce', 'code-profiler')
438 + );
439 +
323 440 // Verify the security nonce
324 441 if ( empty( $_POST['cp_nonce'] ) || ! wp_verify_nonce( $_POST['cp_nonce'], 'start_profiler_nonce') ) {
325 442 $msg = esc_html__('Missing or wrong security nonce. Reload the page and try again', 'code-profiler');
326 443 $response['message'] = $msg;
@@ -327,8 +444,12 @@
327 444 code_profiler_log_error( $msg );
328 445 code_profiler_wp_send_json( $response );
329 446 }
330 447
448 + code_profiler_log_debug(
449 + esc_html__('Retrieving profile ID', 'code-profiler')
450 + );
451 +
331 452 if ( empty( $_POST['microtime'] ) || ! preg_match('/^\d{10}\.\d+$/', $_POST['microtime'] ) ) {
332 453 $msg = esc_html__('Missing parameter (microtime).', 'code-profiler');
333 454 $response['message'] = $msg;
334 455 code_profiler_log_error( $msg );
@@ -335,8 +456,12 @@
335 456 code_profiler_wp_send_json( $response );
336 457 }
337 458 $microtime = sanitize_text_field( $_POST['microtime'] );
338 459
460 + code_profiler_log_debug(
461 + esc_html__('Retrieving profile name', 'code-profiler')
462 + );
463 +
339 464 $profile = sanitize_file_name( $_POST['profile'] );
340 465 if ( empty( $profile ) ) {
341 466 $msg = esc_html__('Missing profile name.', 'code-profiler');
342 467 $response['message'] = $msg;
@@ -343,9 +468,11 @@
343 468 code_profiler_log_error( $msg );
344 469 code_profiler_wp_send_json( $response );
345 470 }
346 471
347 - code_profiler_log_info( esc_html__('Preparing report', 'code-profiler') );
472 + code_profiler_log_info(
473 + esc_html__('Preparing the report', 'code-profiler')
474 + );
348 475 require 'class-report.php';
349 476 $report = new CodeProfiler_Report( $profile, $microtime );
350 477 $report->prepare_report();
351 478
@@ -357,19 +484,81 @@
357 484 $cp_options = get_option('code-profiler');
358 485 unset( $cp_options['hash'] );
359 486 update_option('code-profiler', $cp_options );
360 487
361 - code_profiler_log_info( esc_html__('All done, exiting profiler', 'code-profiler') );
488 + code_profiler_log_info(
489 + esc_html__('All done, exiting profiler', 'code-profiler')
490 + );
362 491 $response['cp_profile'] = $microtime;
363 492 $response['status'] = 'success';
364 493 $response['message'] = 'success';
365 494
495 + code_profiler_log_debug(
496 + esc_html__('Leaving AJAX endpoint', 'code-profiler')
497 + );
498 +
366 499 // AJAX action?
367 500 if ( defined('DOING_AJAX') && DOING_AJAX ) {
368 501 code_profiler_wp_send_json( $response );
369 - } else {
370 - return json_encode( $response );
371 502 }
503 +
504 + return json_encode( $response );
505 +
506 +}
507 +
508 +// =====================================================================
509 +// Rename a profile.
510 +
511 +add_action('wp_ajax_codeprofiler_rename', 'codeprofiler_rename');
512 +
513 +function codeprofiler_rename() {
514 +
515 + $response = ['status' => 'error'];
516 +
517 + // Admin/Superadmin only
518 + if (! is_super_admin() ) {
519 + $response['message'] = esc_html__('You are not allowed to performed this action', 'code-profiler');
520 + wp_send_json( $response );
521 + }
522 +
523 + // Verify the security nonce
524 + if ( empty( $_POST['cp_nonce'] ) || ! wp_verify_nonce( $_POST['cp_nonce'], 'rename-profile') ) {
525 + $response['message'] = esc_html__('Missing or wrong security nonce. Reload the page and try again', 'code-profiler');
526 + wp_send_json( $response );
527 + }
528 +
529 + if ( empty( $_POST['new_name'] ) ) {
530 + $response['message'] = esc_html__('Please enter a name for this profile.', 'code-profiler');
531 + wp_send_json( $response );
532 + }
533 + $new_name = sanitize_file_name( $_POST['new_name'] );
534 + if ( strlen( $new_name ) > 100 ) {
535 + $new_name = substr( $new_name, 0, 100 );
536 + }
537 + if ( empty( $new_name ) ) {
538 + $response['message'] = esc_html__('Please enter a name for this profile.', 'code-profiler');
539 + wp_send_json( $response );
540 + }
541 +
542 + if ( empty( $_POST['profile'] ) || ! preg_match('/^\d{10}\.\d{4}$/', $_POST['profile'] ) ) {
543 + $response['message'] = esc_html__('Missing profile identifier.', 'code-profiler');
544 + wp_send_json( $response );
545 + }
546 + $profile = $_POST['profile'];
547 +
548 + $glob = glob( CODE_PROFILER_UPLOAD_DIR ."/$profile*" );
549 + if ( is_array( $glob ) ) {
550 + foreach( $glob as $path ) {
551 + // preg_quote is needed for Windows servers because ABSPATH will contain backslashes
552 + if ( preg_match('`^'. preg_quote( CODE_PROFILER_UPLOAD_DIR ) .'/(\d{10}\.\d{4})\..+?\.([a-z]+?\.profile)$`', $path, $match ) ) {
553 + rename( $path, CODE_PROFILER_UPLOAD_DIR . "/{$match[1]}.$new_name.{$match[2]}" );
554 + }
555 + }
556 + }
557 +
558 + $response['status'] = 'success';
559 + $response['newname'] = $new_name;
560 + wp_send_json( $response );
372 561
373 562 }
374 563
375 564 // =====================================================================