PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.7.4
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.7.4
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.7.4, at lib/ajax.php

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