PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.8
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.8
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
code-profiler / lib / ajax.php

ajax.php in Code Profiler – WordPress Performance Profiling and Debugging Made Easy 1.8, at lib/ajax.php

855 lines 24.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 +=====================================================================+
4 | ____ _ ____ __ _ _ |
5 | / ___|___ __| | ___ | _ \ _ __ ___ / _(_) | ___ _ __ |
6 | | | / _ \ / _` |/ _ \ | |_) | '__/ _ \| |_| | |/ _ \ '__| |
7 | | |__| (_) | (_| | __/ | __/| | | (_) | _| | | __/ | |
8 | \____\___/ \__,_|\___| |_| |_| \___/|_| |_|_|\___|_| |
9 | |
10 | (c) Jerome Bruandet ~ https://nintechnet.com/codeprofiler/ |
11 +=====================================================================+
12 */
13
14 if (! defined('ABSPATH') ) {
15 die('Forbidden');
16 }
17
18 // ===================================================================== 2023-11-17
19 // Start the profiler.
20
21 add_action('wp_ajax_codeprofiler_start_profiler', 'codeprofiler_start_profiler');
22
23 function codeprofiler_start_profiler() {
24
25 $response = ['status' => 'error'];
26
27 code_profiler_hide_errors();
28
29 $cp_options = get_option('code-profiler');
30 if (! empty( $cp_options['mem'] ) ) {
31 $mem = $cp_options['mem'];
32 } else {
33 $mem = [];
34 }
35
36 code_profiler_log_debug(
37 esc_html__('Entering AJAX endpoint (profiler initialization)', 'code-profiler')
38 );
39
40 // If this is an AJAX call, make sure it comes from an admin/superadmin.
41 if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_start_profiler') {
42 // Admin/Superadmin only
43 if (! is_super_admin() ) {
44 $msg = esc_html__('You are not allowed to performed this action', 'code-profiler');
45 $response['message'] = $msg;
46 code_profiler_log_error( $msg );
47 code_profiler_wp_send_json( $response );
48 }
49 }
50
51 code_profiler_log_debug(
52 esc_html__('Verifying security nonce', 'code-profiler')
53 );
54
55 // Verify the security nonce
56 if ( empty( $_POST['cp_nonce'] ) ||
57 ! wp_verify_nonce( $_POST['cp_nonce'], 'start_profiler_nonce') ) {
58
59 $msg = esc_html__(
60 'Missing or wrong security nonce. Reload the page and try again',
61 'code-profiler'
62 );
63 $response['message'] = $msg;
64 code_profiler_log_error( $msg );
65 code_profiler_wp_send_json( $response );
66 }
67
68 code_profiler_log_debug(
69 esc_html__('Checking MU plugin availability', 'code-profiler')
70 );
71
72 // Verify the MU plugin is loaded
73 if (! defined('CODE_PROFILER_MU_ON') ) {
74 $msg = esc_html__('The MU plugin is not loaded, please check the log', 'code-profiler');
75 $response['message'] = $msg;
76 code_profiler_log_error( $msg );
77 code_profiler_wp_send_json( $response );
78 }
79
80 code_profiler_log_debug(
81 esc_html__('Retrieving parameters #1', 'code-profiler')
82 );
83
84 // Frontend or backend
85 if ( empty( $_POST['x_end'] ) ||
86 ! in_array( $_POST['x_end'], ['frontend', 'backend', 'custom'] ) ) {
87
88 $msg = sprintf(
89 esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'x_end'
90 );
91 $response['message'] = $msg;
92 code_profiler_log_error( $msg );
93 code_profiler_wp_send_json( $response );
94 }
95 $mem['x_end'] = $_POST['x_end'];
96
97 code_profiler_log_debug(
98 esc_html__('Retrieving parameters #2', 'code-profiler')
99 );
100
101 if ( empty( $_POST['post'] ) ) {
102 $msg = sprintf( esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'post');
103 $response['message'] = $msg;
104 code_profiler_log_error( $msg );
105 code_profiler_wp_send_json( $response );
106 }
107 $mem['post'] = $_POST['post'];
108
109 // Make sure we have no more that 4 decimals, because when returning
110 // it via AJAX, it will display more decimals than that
111 $microtime = number_format( microtime( true ), 4, '.', '');
112
113 code_profiler_log_debug(
114 esc_html__('Retrieving parameters #3', 'code-profiler')
115 );
116
117 // Authentication
118 if ( empty( $_POST['x_auth'] ) || $_POST['x_auth'] != 'authenticated' ) {
119 $_POST['x_auth'] = 'unauthenticated';
120 }
121 $mem['x_auth'] = $_POST['x_auth'];
122
123 code_profiler_log_debug(
124 esc_html__('Retrieving parameters #4', 'code-profiler')
125 );
126
127 if ( empty( $_POST['profile'] ) || strlen( $_POST['profile'] ) > 100 ) {
128 $profile = code_profiler_profile_name();
129 } else {
130 $profile = sanitize_file_name( $_POST['profile'] );
131 }
132
133 // URI to profile
134 $url = esc_url_raw( $_POST['post'] );
135 $siteurl = esc_html( site_url() );
136 code_profiler_log_info( sprintf(
137 /* Translators: version, site url, profile name, profile url */
138 esc_html__('Initializing Code Profiler v%s on %s. Profile: %s - %s', 'code-profiler'),
139 CODE_PROFILER_VERSION,
140 $siteurl,
141 $profile,
142 $url
143 ) );
144
145 code_profiler_log_debug(
146 esc_html__('Retrieving parameters #5', 'code-profiler')
147 );
148
149 // User-agent
150 if ( empty( $_POST['user_agent'] ) ) {
151 $user_agent = 'Firefox';
152 } else {
153 $user_agent = sanitize_text_field( $_POST['user_agent'] );
154 }
155 foreach( CODE_PROFILER_UA as $types => $types_array ) {
156 foreach( $types_array as $name => $value ) {
157 if ( $user_agent == $name ) {
158 $ua_signature = $value;
159 break;
160 }
161 }
162 }
163 if ( empty( $ua_signature ) ) {
164 $ua_signature = CODE_PROFILER_UA['Desktop']['Firefox'];
165 }
166 $mem['user_agent'] = $user_agent;
167
168 // Theme
169 $themes = code_profiler_get_themes();
170 if ( empty( $_POST['theme'] ) || empty( $themes[ $_POST['theme'] ] ) ) {
171 $theme = '';
172 unset( $mem['theme'] );
173 } else {
174 code_profiler_log_debug(
175 esc_html__('Retrieving parameters #6', 'code-profiler')
176 );
177 $theme = $_POST['theme'];
178 $mem['theme'] = $theme;
179 // Append the template to the stylesheet
180 if (! empty( $themes[ $theme ]['t'] ) ) {
181 $theme .= "::{$themes[ $theme ]['t']}";
182 } else {
183 $theme .= "::$theme";
184 }
185 }
186
187 code_profiler_log_debug(
188 esc_html__('Creating security key', 'code-profiler')
189 );
190
191 // Create security key
192 $profiler_key = bin2hex( random_bytes( 16 ) );
193 touch( CODE_PROFILER_UPLOAD_DIR .'/key_'. sha1( $profiler_key ) .'.tmp');
194
195 code_profiler_log_debug(
196 esc_html__('Building HTTP query', 'code-profiler')
197 );
198
199 /**
200 * Build the query.
201 */
202 $raw_url = $url;
203 $url = add_query_arg( [
204 'CODE_PROFILER_ON' => $microtime,
205 'profiler_key' => $profiler_key
206 ], $url );
207
208 global $wp_version;
209 $headers = [
210 'Cache-Control' => 'no-cache, no-store, must-revalidate',
211 'Pragma' => 'no-cache',
212 'Expires' => '0',
213 'httpversion' => '1.1',
214 // Devs must be allowed to use it on localhost over TLS too
215 'sslverify' => apply_filters('https_local_ssl_verify', false ),
216 'timeout' => 300, // 300-second timeout instead of the default 5s
217 'redirection' => 0, // We don't want to be redirected
218 'headers' => [
219 // Lowercase header name
220 'code-profiler-key' => $profiler_key,
221 'accept-language' => 'en-US,en;q=0.5',
222 'user-agent' => $ua_signature,
223 'theme' => $theme
224 ]
225 ];
226
227 // Custom HTTP headers
228 if (! empty( $_POST['custom_headers'] ) ) {
229 $custom_headers = explode( PHP_EOL, trim( stripslashes( $_POST['custom_headers'] ) ) );
230 if (! empty( $custom_headers[0] ) ) {
231 code_profiler_log_debug(
232 esc_html__('Building custom HTTP headers', 'code-profiler')
233 );
234 $is_custom_headers = '';
235 foreach( $custom_headers as $custom_header ) {
236 if ( strpos( $custom_header, ':') === false ) {
237 continue;
238 }
239 list( $key, $value ) = explode(':', $custom_header, 2 );
240 // Lowercase header name
241 $key = trim( strtolower( $key ) );
242 $value = trim( $value );
243 // We want printable ASCII characters only
244 $value = code_profiler_ASCII_filter( $value );
245 if (! empty( $key ) && ! empty( $value ) ) {
246 $headers['headers'][ $key ] = $value;
247 $is_custom_headers .= "$key: $value\n";
248 }
249 }
250 }
251 }
252 if (! empty( $is_custom_headers ) ) {
253 $mem['custom_headers'] = json_encode( $is_custom_headers );
254 } else {
255 unset( $mem['custom_headers'] );
256 }
257
258 code_profiler_log_debug(
259 esc_html__('Checking HTTP options', 'code-profiler')
260 );
261
262 // Forward basic authentication if any (not available from WP CLI)
263 if ( function_exists('apache_request_headers') ) {
264 $apache_headers = apache_request_headers();
265 if ( isset( $apache_headers['Authorization'] ) ) {
266 $headers['headers']['Authorization'] = $apache_headers['Authorization'];
267 }
268 // WP-CLI ($ wp code-profiler run --u=FOO --p=BAR)
269 } elseif ( defined('WP_CLI') && ! empty( $_POST['Authorization'] ) ) {
270 $headers['headers']['Authorization'] = $_POST['Authorization'];
271 }
272
273 if ( $_POST['x_auth'] == 'authenticated') {
274
275 code_profiler_log_debug(
276 esc_html__('Creating authentication cookies', 'code-profiler')
277 );
278
279 // Used for authentication
280 if ( is_ssl() ) {
281 $cookie_auth = SECURE_AUTH_COOKIE;
282 $scheme = 'secure_auth';
283 } else {
284 $cookie_auth = AUTH_COOKIE;
285 $scheme = 'auth';
286 }
287
288 // Retrieve the user name (since 1.4.3)
289 if (! defined('WP_CLI') ) {
290 if ( empty( $_POST['username'] ) ) {
291 $msg = esc_html__('Missing authenticated username', 'code-profiler');
292 $response['message'] = $msg;
293 code_profiler_log_error( $msg );
294 code_profiler_wp_send_json( $response );
295 }
296 $username = sanitize_user( $_POST['username'] );
297 $user_object = get_user_by('login', $username );
298 if ( $user_object === false ) {
299 $msg = sprintf( esc_html__('User [%s] does not exist.', 'code-profiler'), $username);
300 $response['message'] = $msg;
301 code_profiler_log_error( $msg );
302 code_profiler_wp_send_json( $response );
303 }
304 $mem['username'] = strtolower( $username );
305 $headers['cookies'][ $cookie_auth ] = wp_generate_auth_cookie(
306 $user_object->ID,
307 time() + 180,
308 $scheme
309 );
310 if ( empty( $headers['cookies'][ $cookie_auth ] ) ) {
311 $msg = esc_html__('Unable to create the authentication cookie', 'code-profiler');
312 code_profiler_log_error( $msg );
313 $response['message'] = $msg;
314 code_profiler_wp_send_json( $response );
315 }
316 $headers['cookies'][ LOGGED_IN_COOKIE ] = wp_generate_auth_cookie(
317 $user_object->ID,
318 time() + 180,
319 'logged_in'
320 );
321 if ( empty( $headers['cookies'][ LOGGED_IN_COOKIE ] ) ) {
322 $msg = esc_html__('Unable to create the "logged_in" cookie', 'code-profiler');
323 code_profiler_log_error( $msg );
324 $response['message'] = $msg;
325 code_profiler_wp_send_json( $response );
326 }
327 // WP CLI
328 } else {
329 $id = get_current_user_id();
330 $headers['cookies'][ $cookie_auth ] = wp_generate_auth_cookie(
331 $id,
332 time() + 180,
333 $scheme
334 );
335 if ( empty( $headers['cookies'][ $cookie_auth ] ) ) {
336 $msg = esc_html__('Unable to create the authentication cookie', 'code-profiler');
337 code_profiler_log_error( $msg );
338 $response['message'] = $msg;
339 code_profiler_wp_send_json( $response );
340 }
341 $headers['cookies'][ LOGGED_IN_COOKIE ] = wp_generate_auth_cookie(
342 $id,
343 time() + 180,
344 'logged_in'
345 );
346 if ( empty( $headers['cookies'][ LOGGED_IN_COOKIE ] ) ) {
347 $msg = esc_html__('Unable to create the "logged_in" cookie', 'code-profiler');
348 code_profiler_log_error( $msg );
349 $response['message'] = $msg;
350 code_profiler_wp_send_json( $response );
351 }
352 }
353 $session_id = session_id();
354 if ( $session_id !== false ) {
355 $session_name = session_name();
356 $headers['cookies'][ $session_name ] = $session_id;
357 }
358 }
359
360 if ( function_exists('opcache_reset') ) {
361 code_profiler_log_debug(
362 esc_html__('Clearing opcode cache', 'code-profiler')
363 );
364 opcache_reset();
365 }
366
367 // GET or POST method
368 if (! empty( $_POST['method'] ) && $_POST['method'] == 'post') {
369 $safe_method = 'wp_safe_remote_post';
370 $mem['method'] = 'post';
371
372 // Content-type
373 $content_type = [
374 1 => 'application/x-www-form-urlencoded', // Formatted
375 3 => 'application/x-www-form-urlencoded', // Raw
376 2 => 'application/json'
377 ];
378 if ( empty( $_POST['content_type'] ) ||
379 ! in_array( $_POST['content_type'], [ 1, 2, 3 ] ) ) {
380
381 $mem['content_type'] = 1;
382 } else {
383 $mem['content_type'] = (int) $_POST['content_type'];
384 }
385 $headers['headers']['content-type'] = $content_type[ $mem['content_type'] ];
386
387 // Optional POST payload
388 if (! empty( $_POST['payload'] ) ) {
389 $_payload = trim( stripslashes( $_POST['payload'] ) );
390
391 code_profiler_log_debug(
392 esc_html__('Building POST payload', 'code-profiler')
393 );
394 /**
395 * application/x-www-form-urlencoded (formatted)
396 */
397 if ( $mem['content_type'] == 1 ) {
398 $payload_array = explode( PHP_EOL, $_payload );
399 foreach( $payload_array as $item ) {
400 $payload = explode('=', trim( $item ), 2 );
401 if ( isset( $payload[1] ) ) {
402 $payload[0] = trim( $payload[0] );
403 $payload[1] = trim( $payload[1] );
404 $headers['body'][ $payload[0] ] = $payload[1];
405 }
406 }
407 /**
408 * application/x-www-form-urlencoded (raw)
409 */
410 } elseif ( $mem['content_type'] == 3 ) {
411 parse_str( $_payload , $payload_array );
412 foreach( $payload_array as $key => $item ) {
413 $headers['body'][ $key ] = $item;
414 }
415 /**
416 * application/json
417 */
418 } else {
419 $headers['body'] = $_payload;
420 }
421 $mem['payload'] = json_encode( $_payload );
422
423 } else {
424 // POST request without a payload
425 unset( $mem['payload'] );
426 }
427
428 } else {
429 $safe_method = 'wp_safe_remote_get';
430 $mem['method'] = 'get';
431 }
432
433 // Optional user-defined cookies
434 if (! empty( $_POST['cookies'] ) ) {
435
436 code_profiler_log_debug(
437 esc_html__('Building HTTP Cookies', 'code-profiler')
438 );
439
440 $cookies_array = explode( PHP_EOL, trim( stripslashes( $_POST['cookies'] ) ) );
441 foreach( $cookies_array as $item ) {
442 $cookie = explode('=', trim( $item ), 2 );
443 if ( isset( $cookie[1] ) ) {
444 $cookie[0] = trim( $cookie[0] );
445 $cookie[1] = trim( $cookie[1] );
446 $headers['cookies'][ $cookie[0] ] = $cookie[1];
447 }
448 }
449 $mem['cookies'] = json_encode( $_POST['cookies'] );
450 } else {
451 unset( $mem['cookies'] );
452 }
453
454 /**
455 * Optional file and folder exclusions.
456 */
457 $tmp_exclusions = [];
458 if (! empty( $_POST['exclusions'] ) ) {
459 $tmp_array = explode( PHP_EOL, trim( stripslashes( $_POST['exclusions'] ) ) );
460 foreach( $tmp_array as $item ) {
461 $item = trim( code_profiler_ASCII_filter( $item ) );
462 if ( $item ) {
463 $tmp_exclusions[] = $item;
464 }
465 }
466 }
467 /**
468 * Remove duplicates.
469 */
470 $exclusions = array_unique( $tmp_exclusions );
471
472 if ( $exclusions) {
473 $mem['exclusions'] = json_encode( $exclusions );
474 } else {
475 unset( $mem['exclusions'] );
476 }
477
478 $cp_options['mem'] = $mem;
479 update_option('code-profiler', $cp_options );
480
481 /**
482 * Save the profile configuration into a temporary file (used by the re-run feature).
483 */
484 file_put_contents(
485 CODE_PROFILER_UPLOAD_DIR ."/$microtime.". CODE_PROFILER_TMP_RERUN_LOG,
486 json_encode( $mem )
487 );
488
489 code_profiler_log_debug(
490 esc_html__('Sending HTTP request', 'code-profiler')
491 );
492
493 // We must allow developers to run the profiler
494 // on a local IP (e.g, http://127.0.0.1/)
495 add_filter('http_request_host_is_external', '__return_true');
496 $res = $safe_method( $url, $headers );
497
498 // Connection error
499 if ( is_wp_error( $res ) ) {
500 $msg = esc_html__('Cannot connect to the requested page: %s', 'code-profiler');
501 $response['message'] = sprintf(
502 $msg,
503 esc_html( $res->get_error_message() )
504 );
505 code_profiler_log_error( sprintf( $msg, $res->get_error_message() ) );
506 code_profiler_wp_send_json( $response );
507 }
508
509 code_profiler_log_debug(
510 esc_html__('Fetching HTTP response', 'code-profiler')
511 );
512
513 /**
514 * Always log last HTTP response headers and body,
515 * except sensitive data (cookies & PHP session ID).
516 */
517 if ( isset( $res['headers'] ) && isset( $res['body'] ) ) {
518 /**
519 * Search for an existing log or create it.
520 */
521 $last_log = code_profiler_glob(
522 CODE_PROFILER_UPLOAD_DIR,
523 '^last_request\.\d+?\.\d+?\.log$',
524 true
525 );
526 if ( empty( $last_log[0] ) ) {
527 $last_log[0] = CODE_PROFILER_UPLOAD_DIR .'/last_request.'. microtime( true ) .'.log';
528 }
529 /**
530 * Parse headers.
531 */
532 $response_headers = "HTTP {$res['response']['code']} {$res['response']['message']}\n";
533
534 foreach( $res['headers'] as $key => $value ) {
535 /**
536 * Remove cookies.
537 */
538 if ( $key == 'set-cookie') {
539 $response_headers .= ucfirst( $key ) .': *** '. __('Removed', 'code-profiler') ." ***\n";
540 } else {
541 /**
542 * HTTP headers can contain arrays.
543 */
544 if ( is_array( $value ) ) {
545 foreach( $value as $k => $v ) {
546 $response_headers .= ucfirst( $key ) .": $v\n";
547 }
548 } else {
549 $response_headers .= ucfirst( $key ) .": $value\n";
550 }
551 }
552 }
553 /**
554 * Save to the log.
555 */
556 $contents = "==================================================\n".
557 __('Requested page:', 'code-profiler') ."\n\n".
558 "$raw_url\n".
559 "==================================================\n";
560
561 if ( ! empty( $headers['body'] ) ) {
562
563 $contents .= __('Post payload:', 'code-profiler') ."\n\n";
564 if ( $mem['content_type'] == 2 ) {
565 /**
566 * application/json: must be decoded.
567 */
568 $contents .= print_r( json_decode( $headers['body'] ), true );
569 } else {
570 $contents .= print_r( $headers['body'], true );
571 }
572 $contents .= "\n==================================================\n";
573 }
574
575 $contents .= __('Response headers:', 'code-profiler') ."\n\n".
576 $response_headers .
577 "==================================================\n".
578 __('Response body:', 'code-profiler') ."\n\n".
579 print_r( $res['body'], true ).
580 "\n==================================================\n";
581
582 file_put_contents( $last_log[0], $contents );
583 }
584
585 // HTTP status code
586 if (! empty( $cp_options['http_response'] ) ) {
587 if ( preg_match( "/{$cp_options['http_response']}/", $res['response']['code'] ) ) {
588
589 $msg = '';
590
591 $log = esc_html__(
592 /* Translators: HTTP response code and message */
593 'The website returned the following HTTP status code: %s %s.', 'code-profiler'
594 );
595
596 if ( $res['response']['code'] < 500 ) {
597 $log .= ' '. esc_html__('By default, the profiler will always abort and throw an error if the server did not return a 200 HTTP status code. You can change that behaviour in the "Settings" section if the page you are profiling needs to return a different code (3xx, 4xx or 5xx).', 'code-profiler');
598 }
599
600 $msg .= $log .' '. esc_html__('You may find more details about this error in your PHP error log and/or in the "Logs" section.', 'code-profiler');
601
602 $response['message'] = sprintf(
603 $msg,
604 (int) $res['response']['code'],
605 $res['response']['message']
606 );
607 code_profiler_log_error(
608 sprintf( $log, $res['response']['code'], $res['response']['message'] )
609 );
610
611 // If it is a 301/302 redirection, we write the new URL to the log
612 if ( in_array( $res['response']['code'], [301, 302] ) &&
613 isset( $res['headers']['location'] ) ) {
614
615 code_profiler_log_error(
616 sprintf(
617 /* Translators: URL */
618 esc_html__('The URL redirects to: %s', 'code-profiler'),
619 $res['headers']['location']
620 )
621 );
622 }
623 code_profiler_wp_send_json( $response );
624 }
625 }
626
627 code_profiler_log_debug(
628 esc_html__('Decoding body', 'code-profiler')
629 );
630
631 // Check response
632 $message = json_decode( $res['body'], true );
633 if ( isset( $message['status'] ) && isset( $message['message'] ) ) {
634 $response['status'] = $message['status'];
635 $response['message'] = $message['message'];
636 code_profiler_wp_send_json( $response );
637 }
638 code_profiler_log_info(
639 esc_html__('Collecting data to analyze', 'code-profiler')
640 );
641 // Return success
642 $response = ['status' => 'success'];
643 $response['message'] = 'success';
644 $response['microtime'] = $microtime;
645
646 code_profiler_log_debug(
647 esc_html__('Leaving AJAX endpoint', 'code-profiler')
648 );
649
650 // AJAX action?
651 if ( defined('DOING_AJAX') && DOING_AJAX ) {
652 code_profiler_wp_send_json( $response );
653 }
654
655 return json_encode( $response );
656
657 }
658
659 // ===================================================================== 2023-11-17
660
661 add_action('wp_ajax_codeprofiler_prepare_report', 'codeprofiler_prepare_report');
662
663 function codeprofiler_prepare_report() {
664
665 $response = ['status' => 'error'];
666
667 // If this is an AJAX call, make sure it comes from an admin/superadmin.
668 if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_prepare_report') {
669 // Admin/Superadmin only
670 if (! is_super_admin() ) {
671 $msg = esc_html__('You are not allowed to performed this action', 'code-profiler');
672 $response['message'] = $msg;
673 code_profiler_log_error( $msg );
674 code_profiler_wp_send_json( $response );
675 }
676 }
677
678 code_profiler_log_debug(
679 esc_html__('Entering AJAX endpoint (report preparation)', 'code-profiler')
680 );
681
682 code_profiler_hide_errors();
683
684 code_profiler_log_debug(
685 esc_html__('Verifying security nonce', 'code-profiler')
686 );
687
688 // Verify the security nonce
689 if ( empty( $_POST['cp_nonce'] ) ||
690 ! wp_verify_nonce( $_POST['cp_nonce'], 'start_profiler_nonce') ) {
691
692 $msg = esc_html__('Missing or wrong security nonce. Reload the page and try again',
693 'code-profiler');
694 $response['message'] = $msg;
695 code_profiler_log_error( $msg );
696 code_profiler_wp_send_json( $response );
697 }
698
699 code_profiler_log_debug(
700 esc_html__('Retrieving profile ID', 'code-profiler')
701 );
702
703 if ( empty( $_POST['microtime'] ) || ! preg_match('/^\d{10}\.\d+$/', $_POST['microtime'] ) ) {
704 $msg = esc_html__('Missing parameter (microtime).', 'code-profiler');
705 $response['message'] = $msg;
706 code_profiler_log_error( $msg );
707 code_profiler_wp_send_json( $response );
708 }
709 $microtime = sanitize_text_field( $_POST['microtime'] );
710
711 code_profiler_log_debug(
712 esc_html__('Retrieving profile name', 'code-profiler')
713 );
714
715 $profile = sanitize_file_name( $_POST['profile'] );
716 if ( empty( $profile ) ) {
717 $msg = esc_html__('Missing profile name.', 'code-profiler');
718 $response['message'] = $msg;
719 code_profiler_log_error( $msg );
720 code_profiler_wp_send_json( $response );
721 }
722
723 code_profiler_log_info(
724 esc_html__('Preparing the report', 'code-profiler')
725 );
726 require 'class-report.php';
727 $report = new CodeProfiler_Report( $profile, $microtime );
728 $report->prepare_report();
729
730 // Take a 1s break so that we can spot any potential error
731 // in the backend before AJAX refresh the page
732 usleep( 1000000 );
733
734 code_profiler_log_info(
735 esc_html__('All done, exiting profiler', 'code-profiler')
736 );
737 $response['cp_profile'] = $microtime;
738 $response['status'] = 'success';
739 $response['message'] = 'success';
740
741 code_profiler_log_debug(
742 esc_html__('Leaving AJAX endpoint', 'code-profiler')
743 );
744
745 // AJAX action?
746 if ( defined('DOING_AJAX') && DOING_AJAX ) {
747 code_profiler_wp_send_json( $response );
748 }
749
750 return json_encode( $response );
751
752 }
753
754 // ===================================================================== 2023-11-17
755 // Rename a profile.
756
757 add_action('wp_ajax_codeprofiler_rename', 'codeprofiler_rename');
758
759 function codeprofiler_rename() {
760
761 $response = ['status' => 'error'];
762
763 code_profiler_hide_errors();
764
765 // Admin/Superadmin only
766 if (! is_super_admin() ) {
767 $response['message'] = esc_html__(
768 'You are not allowed to performed this action.', 'code-profiler'
769 );
770 wp_send_json( $response );
771 }
772
773 // Verify the security nonce
774 if ( empty( $_POST['cp_nonce'] ) || ! wp_verify_nonce( $_POST['cp_nonce'], 'rename-profile') ) {
775 $response['message'] = esc_html__(
776 'Missing or wrong security nonce. Reload the page and try again.', 'code-profiler'
777 );
778 wp_send_json( $response );
779 }
780
781 if ( empty( $_POST['new_name'] ) ) {
782 $response['message'] = esc_html__('Please enter a name for this profile.', 'code-profiler');
783 wp_send_json( $response );
784 }
785 $new_name = sanitize_file_name( $_POST['new_name'] );
786 if ( strlen( $new_name ) > 100 ) {
787 $new_name = substr( $new_name, 0, 100 );
788 }
789 if ( empty( $new_name ) ) {
790 $response['message'] = esc_html__('Please enter a name for this profile.', 'code-profiler');
791 wp_send_json( $response );
792 }
793
794 if ( empty( $_POST['profile'] ) || ! preg_match('/^\d{10}\.\d{4}$/', $_POST['profile'] ) ) {
795 $response['message'] = esc_html__('Missing profile identifier.', 'code-profiler');
796 wp_send_json( $response );
797 }
798 $profile = $_POST['profile'];
799
800 $glob = code_profiler_glob( CODE_PROFILER_UPLOAD_DIR, "^$profile", true );
801
802 $res = false;
803
804 if ( is_array( $glob ) ) {
805 foreach( $glob as $path ) {
806 // preg_quote is needed for Windows servers because ABSPATH will contain backslashes
807 if ( preg_match('`^'. preg_quote( CODE_PROFILER_UPLOAD_DIR . DIRECTORY_SEPARATOR ) .
808 '(\d{10}\.\d{4})\..+?\.([a-z]+?\.profile)$`', $path, $match ) ) {
809
810 $res = rename( $path, CODE_PROFILER_UPLOAD_DIR . "/{$match[1]}.$new_name.{$match[2]}" );
811 if ( $res === false ) {
812 $response['message'] = esc_html__(
813 'The operation failed.', 'code-profiler'
814 );
815 wp_send_json( $response );
816 }
817 }
818 }
819 }
820
821 if ( $res === false ) {
822 $response['message'] = esc_html__(
823 'The operation failed.', 'code-profiler'
824 );
825 wp_send_json( $response );
826 }
827
828 /**
829 * Rename the profile in the summary file,
830 * so that it can be used by the re-run feature.
831 */
832 if (! empty( $match[1] ) &&
833 is_file( CODE_PROFILER_UPLOAD_DIR ."/{$match[1]}.$new_name.summary.profile" ) ) {
834
835 $data = json_decode(
836 file_get_contents( CODE_PROFILER_UPLOAD_DIR ."/{$match[1]}.$new_name.summary.profile" ),
837 true
838 );
839 if (! empty( $data['rerun']['profile'] ) ) {
840 $data['rerun']['profile'] = $new_name;
841 file_put_contents(
842 CODE_PROFILER_UPLOAD_DIR ."/{$match[1]}.$new_name.summary.profile", json_encode( $data )
843 );
844 }
845 }
846
847 $response['status'] = 'success';
848 $response['newname'] = $new_name;
849 wp_send_json( $response );
850
851 }
852
853 // =====================================================================
854 // EOF
855