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
← All changes | lib/ajax.php +489 -71 1.51.7.4 View file →
@@ -6,15 +6,17 @@
6 6 | | | / _ \ / _` |/ _ \ | |_) | '__/ _ \| |_| | |/ _ \ '__| |
7 7 | | |__| (_) | (_| | __/ | __/| | | (_) | _| | | __/ | |
8 8 | \____\___/ \__,_|\___| |_| |_| \___/|_| |_|_|\___|_| |
9 9 | |
10 - | (c) Jerome Bruandet ~ https://code-profiler.com/ |
10 + | (c) Jerome Bruandet ~ https://nintechnet.com/codeprofiler/ |
11 11 +=====================================================================+
12 12 */
13 13
14 -if (! defined('ABSPATH') ) { die('Forbidden'); }
14 +if (! defined('ABSPATH') ) {
15 + die('Forbidden');
16 +}
15 17
16 -// =====================================================================
18 +// ===================================================================== 2023-11-17
17 19 // Start the profiler.
18 20
19 21 add_action('wp_ajax_codeprofiler_start_profiler', 'codeprofiler_start_profiler');
20 22
@@ -21,10 +23,16 @@
21 23 function codeprofiler_start_profiler() {
22 24
23 25 $response = ['status' => 'error'];
24 26
27 + code_profiler_hide_errors();
28 +
25 29 $cp_options = get_option('code-profiler');
26 30
31 + code_profiler_log_debug(
32 + esc_html__('Entering AJAX endpoint (profiler initialization)', 'code-profiler')
33 + );
34 +
27 35 // If this is an AJAX call, make sure it comes from an admin/superadmin.
28 36 if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_start_profiler') {
29 37 // Admin/Superadmin only
30 38 if (! is_super_admin() ) {
@@ -34,16 +42,29 @@
34 42 code_profiler_wp_send_json( $response );
35 43 }
36 44 }
37 45
46 + code_profiler_log_debug(
47 + esc_html__('Verifying security nonce', 'code-profiler')
48 + );
49 +
38 50 // Verify the security nonce
39 - if ( empty( $_POST['cp_nonce'] ) || ! wp_verify_nonce( $_POST['cp_nonce'], 'start_profiler_nonce') ) {
40 - $msg = esc_html__('Missing or wrong security nonce. Reload the page and try again', 'code-profiler');
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 + );
41 58 $response['message'] = $msg;
42 59 code_profiler_log_error( $msg );
43 60 code_profiler_wp_send_json( $response );
44 61 }
45 62
63 + code_profiler_log_debug(
64 + esc_html__('Checking MU plugin availability', 'code-profiler')
65 + );
66 +
46 67 // Verify the MU plugin is loaded
47 68 if (! defined('CODE_PROFILER_MU_ON') ) {
48 69 $msg = esc_html__('The MU plugin is not loaded, please check the log', 'code-profiler');
49 70 $response['message'] = $msg;
@@ -50,14 +71,19 @@
50 71 code_profiler_log_error( $msg );
51 72 code_profiler_wp_send_json( $response );
52 73 }
53 74
54 - // Clean-up temp files left in the profiles folder
55 - code_profiler_cleantmpfiles();
75 + code_profiler_log_debug(
76 + esc_html__('Retrieving parameters #1', 'code-profiler')
77 + );
56 78
57 79 // Frontend or backend
58 - if ( empty( $_POST['where'] ) || ! in_array( $_POST['where'], ['frontend', 'backend', 'custom'] ) ) {
59 - $msg = sprintf( esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'where');
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 + );
60 86 $response['message'] = $msg;
61 87 code_profiler_log_error( $msg );
62 88 code_profiler_wp_send_json( $response );
63 89 }
@@ -62,8 +88,12 @@
62 88 code_profiler_wp_send_json( $response );
63 89 }
64 90 $cp_options['mem_where'] = $_POST['where'];
65 91
92 + code_profiler_log_debug(
93 + esc_html__('Retrieving parameters #2', 'code-profiler')
94 + );
95 +
66 96 if ( empty( $_POST['post'] ) ) {
67 97 $msg = sprintf( esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'post');
68 98 $response['message'] = $msg;
69 99 code_profiler_log_error( $msg );
@@ -74,17 +104,22 @@
74 104 // Make sure we have no more that 4 decimals, because when returning
75 105 // it via AJAX, it will display more decimals than that
76 106 $microtime = number_format( microtime( true ), 4, '.', '');
77 107
108 + code_profiler_log_debug(
109 + esc_html__('Retrieving parameters #3', 'code-profiler')
110 + );
111 +
78 112 // Authentication
79 - if ( empty( $_POST['user'] ) || ! in_array( $_POST['user'], ['authenticated', 'unauthenticated'] ) ) {
80 - $msg = sprintf( esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'user');
81 - $response['message'] = $msg;
82 - code_profiler_log_error( $msg );
83 - code_profiler_wp_send_json( $response );
113 + if ( empty( $_POST['user'] ) || $_POST['user'] != 'authenticated' ) {
114 + $_POST['user'] = 'unauthenticated';
84 115 }
85 116 $cp_options['mem_user'] = $_POST['user'];
86 117
118 + code_profiler_log_debug(
119 + esc_html__('Retrieving parameters #4', 'code-profiler')
120 + );
121 +
87 122 if ( empty( $_POST['profile'] ) || strlen( $_POST['profile'] ) > 100 ) {
88 123 $profile = code_profiler_profile_name();
89 124 } else {
90 125 $profile = sanitize_file_name( $_POST['profile'] );
@@ -90,16 +125,23 @@
90 125 $profile = sanitize_file_name( $_POST['profile'] );
91 126 }
92 127
93 128 // URI to profile
94 - $url = esc_url_raw( $_POST['post'] );
129 + $url = esc_url_raw( $_POST['post'] );
130 + $siteurl = esc_html( site_url() );
95 131 code_profiler_log_info( sprintf(
96 - esc_html__('Starting Code Profiler v%s for %s (profile: %s)', 'code-profiler'),
132 + /* Translators: version, site url, profile name, profile url */
133 + esc_html__('Initializing Code Profiler v%s on %s. Profile: %s - %s', 'code-profiler'),
97 134 CODE_PROFILER_VERSION,
98 - $url,
99 - $profile
135 + $siteurl,
136 + $profile,
137 + $url
100 138 ) );
101 139
140 + code_profiler_log_debug(
141 + esc_html__('Retrieving parameters #5', 'code-profiler')
142 + );
143 +
102 144 // User-agent
103 145 if ( empty( $_POST['ua'] ) ) {
104 146 $ua = 'Firefox';
105 147 } else {
@@ -117,13 +159,43 @@
117 159 $ua_signature = CODE_PROFILER_UA['Desktop']['Firefox'];
118 160 }
119 161 $cp_options['ua'] = $ua;
120 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 +
121 186 // Create security key
122 187 $profiler_key = bin2hex( random_bytes( 16 ) );
123 - $cp_options['hash'] = sha1( $profiler_key );
188 + touch( CODE_PROFILER_UPLOAD_DIR .'/key_'. sha1( $profiler_key ) .'.tmp');
124 189
125 - // Build query
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;
126 198 $url = add_query_arg( [
127 199 'CODE_PROFILER_ON' => $microtime,
128 200 'profiler_key' => $profiler_key
129 201 ], $url );
@@ -135,17 +207,54 @@
135 207 'Expires' => '0',
136 208 'httpversion' => '1.1',
137 209 // Devs must be allowed to use it on localhost over TLS too
138 210 'sslverify' => apply_filters('https_local_ssl_verify', false ),
139 - 'timeout' => 180, // 180-second timeout instead of the default 5s
211 + 'timeout' => 300, // 300-second timeout instead of the default 5s
140 212 'redirection' => 0, // We don't want to be redirected
141 213 'headers' => [
142 - 'code-profiler-key' => $profiler_key,
143 - 'Accept-Language' => 'en-US,en;q=0.5',
144 - 'User-Agent' => $ua_signature
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
145 219 ]
146 220 ];
147 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 +
148 257 // Forward basic authentication if any (not available from WP CLI)
149 258 if ( function_exists('apache_request_headers') ) {
150 259 $apache_headers = apache_request_headers();
151 260 if ( isset( $apache_headers['Authorization'] ) ) {
@@ -150,21 +259,26 @@
150 259 $apache_headers = apache_request_headers();
151 260 if ( isset( $apache_headers['Authorization'] ) ) {
152 261 $headers['headers']['Authorization'] = $apache_headers['Authorization'];
153 262 }
154 - // WP-CLI (wp code-profiler run --u=FOO --p=BAR)
263 + // WP-CLI ($ wp code-profiler run --u=FOO --p=BAR)
155 264 } elseif ( defined('WP_CLI') && ! empty( $_POST['Authorization'] ) ) {
156 265 $headers['headers']['Authorization'] = $_POST['Authorization'];
157 266 }
158 267
159 268 if ( $_POST['user'] == 'authenticated') {
269 +
270 + code_profiler_log_debug(
271 + esc_html__('Creating authentication cookies', 'code-profiler')
272 + );
273 +
160 274 // Used for authentication
161 275 if ( is_ssl() ) {
162 276 $cookie_auth = SECURE_AUTH_COOKIE;
163 - $scheme = 'secure_auth';
277 + $scheme = 'secure_auth';
164 278 } else {
165 279 $cookie_auth = AUTH_COOKIE;
166 - $scheme = 'auth';
280 + $scheme = 'auth';
167 281 }
168 282
169 283 // Retrieve the user name (since 1.4.3)
170 284 if (! defined('WP_CLI') ) {
@@ -182,58 +296,132 @@
182 296 code_profiler_log_error( $msg );
183 297 code_profiler_wp_send_json( $response );
184 298 }
185 299 $cp_options['mem_username'] = strtolower( $username );
186 - $headers['cookies'][ $cookie_auth ] = wp_generate_auth_cookie( $user_object->ID, time() + 180, $scheme );
187 - $headers['cookies'][ LOGGED_IN_COOKIE ] = wp_generate_auth_cookie( $user_object->ID, time() + 180, 'logged_in');
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 + }
188 322 // WP CLI
189 323 } else {
190 - $id = get_current_user_id();
191 - $headers['cookies'][ $cookie_auth ] = wp_generate_auth_cookie( $id, time() + 180, $scheme );
192 - $headers['cookies'][ LOGGED_IN_COOKIE ] = wp_generate_auth_cookie( $id, time() + 180, 'logged_in');
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 + }
193 347 }
194 - $session_id = session_id();
348 + $session_id = session_id();
195 349 if ( $session_id !== false ) {
196 - $session_name = session_name();
197 - $headers['cookies'][ $session_name ] = $session_id;
350 + $session_name = session_name();
351 + $headers['cookies'][ $session_name ] = $session_id;
198 352 }
199 353 }
200 354
201 355 if ( function_exists('opcache_reset') ) {
356 + code_profiler_log_debug(
357 + esc_html__('Clearing opcode cache', 'code-profiler')
358 + );
202 359 opcache_reset();
203 360 }
204 361
205 362 // GET or POST method
206 363 if (! empty( $_POST['method'] ) && $_POST['method'] == 'post') {
207 - $safe_method = 'wp_safe_remote_post';
208 - $cp_options['mem_method'] = 'post';
364 + $safe_method = 'wp_safe_remote_post';
365 + $cp_options['mem_method'] = 'post';
209 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 +
210 381 // Optional POST payload
211 382 if (! empty( $_POST['payload'] ) ) {
212 - $payload_array = explode( PHP_EOL, trim( $_POST['payload'] ) );
213 - foreach( $payload_array as $item ) {
214 - $payload = explode('=', trim( $item ), 2 );
215 - if ( isset( $payload[1] ) ) {
216 - $payload[0] = trim( $payload[0] );
217 - $payload[1] = trim( $payload[1] );
218 - $headers['body'][ $payload[0] ] = $payload[1];
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 + }
219 399 }
400 + } else {
401 +
402 + $headers['body'] = $_payload;
220 403 }
221 - $cp_options['payload'] = json_encode( $_POST['payload'] );
222 - $cp_options['mem_payload'] = $_POST['payload'];
404 + $cp_options['payload'] = json_encode( $_payload );
405 +
223 406 } else {
224 407 // POST request without a payload
225 408 unset( $cp_options['payload'] );
226 - $cp_options['mem_payload'] = '';
227 409 }
410 +
228 411 } else {
229 - $safe_method = 'wp_safe_remote_get';
230 - $cp_options['mem_method'] = 'get';
412 + $safe_method = 'wp_safe_remote_get';
413 + $cp_options['mem_method'] = 'get';
231 414 }
232 415
233 416 // Optional user-defined cookies
234 417 if (! empty( $_POST['cookies'] ) ) {
235 - $cookies_array = explode( PHP_EOL, trim( $_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'] ) ) );
236 424 foreach( $cookies_array as $item ) {
237 425 $cookie = explode('=', trim( $item ), 2 );
238 426 if ( isset( $cookie[1] ) ) {
239 427 $cookie[0] = trim( $cookie[0] );
@@ -245,10 +433,38 @@
245 433 } else {
246 434 unset( $cp_options['cookies'] );
247 435 }
248 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 +
249 461 update_option('code-profiler', $cp_options );
250 462
463 + code_profiler_log_debug(
464 + esc_html__('Sending HTTP request', 'code-profiler')
465 + );
466 +
251 467 // We must allow developers to run the profiler
252 468 // on a local IP (e.g, http://127.0.0.1/)
253 469 add_filter('http_request_host_is_external', '__return_true');
254 470 $res = $safe_method( $url, $headers );
@@ -263,23 +479,115 @@
263 479 code_profiler_log_error( sprintf( $msg, $res->get_error_message() ) );
264 480 code_profiler_wp_send_json( $response );
265 481 }
266 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 +
267 544 // HTTP status code
268 545 if (! empty( $cp_options['http_response'] ) ) {
269 546 if ( preg_match( "/{$cp_options['http_response']}/", $res['response']['code'] ) ) {
270 - $msg = esc_html__('The website returned the following HTTP status code: %s %s.', 'code-profiler').
271 - ' '.
272 - 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');
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 +
273 561 $response['message'] = sprintf(
274 562 $msg,
275 563 (int) $res['response']['code'],
276 - esc_html( $res['response']['message'] )
564 + $res['response']['message']
277 565 );
278 - code_profiler_log_error( sprintf( $msg, $res['response']['code'], $res['response']['message'] ) );
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 + }
279 582 code_profiler_wp_send_json( $response );
280 583 }
281 584 }
585 +
586 + code_profiler_log_debug(
587 + esc_html__('Decoding body', 'code-profiler')
588 + );
589 +
282 590 // Check response
283 591 $message = json_decode( $res['body'], true );
284 592 if ( isset( $message['status'] ) && isset( $message['message'] ) ) {
285 593 $response['status'] = $message['status'];
@@ -285,24 +593,30 @@
285 593 $response['status'] = $message['status'];
286 594 $response['message'] = $message['message'];
287 595 code_profiler_wp_send_json( $response );
288 596 }
289 - code_profiler_log_info( esc_html__('Collecting data to analyze', 'code-profiler') );
597 + code_profiler_log_info(
598 + esc_html__('Collecting data to analyze', 'code-profiler')
599 + );
290 600 // Return success
291 601 $response = ['status' => 'success'];
292 602 $response['message'] = 'success';
293 603 $response['microtime'] = $microtime;
294 604
605 + code_profiler_log_debug(
606 + esc_html__('Leaving AJAX endpoint', 'code-profiler')
607 + );
608 +
295 609 // AJAX action?
296 610 if ( defined('DOING_AJAX') && DOING_AJAX ) {
297 611 code_profiler_wp_send_json( $response );
298 - } else {
299 - return json_encode( $response );
300 612 }
301 613
614 + return json_encode( $response );
615 +
302 616 }
303 617
304 -// =====================================================================
618 +// ===================================================================== 2023-11-17
305 619
306 620 add_action('wp_ajax_codeprofiler_prepare_report', 'codeprofiler_prepare_report');
307 621
308 622 function codeprofiler_prepare_report() {
@@ -319,16 +633,33 @@
319 633 code_profiler_wp_send_json( $response );
320 634 }
321 635 }
322 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 +
323 647 // Verify the security nonce
324 - if ( empty( $_POST['cp_nonce'] ) || ! wp_verify_nonce( $_POST['cp_nonce'], 'start_profiler_nonce') ) {
325 - $msg = esc_html__('Missing or wrong security nonce. Reload the page and try again', 'code-profiler');
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');
326 653 $response['message'] = $msg;
327 654 code_profiler_log_error( $msg );
328 655 code_profiler_wp_send_json( $response );
329 656 }
330 657
658 + code_profiler_log_debug(
659 + esc_html__('Retrieving profile ID', 'code-profiler')
660 + );
661 +
331 662 if ( empty( $_POST['microtime'] ) || ! preg_match('/^\d{10}\.\d+$/', $_POST['microtime'] ) ) {
332 663 $msg = esc_html__('Missing parameter (microtime).', 'code-profiler');
333 664 $response['message'] = $msg;
334 665 code_profiler_log_error( $msg );
@@ -335,8 +666,12 @@
335 666 code_profiler_wp_send_json( $response );
336 667 }
337 668 $microtime = sanitize_text_field( $_POST['microtime'] );
338 669
670 + code_profiler_log_debug(
671 + esc_html__('Retrieving profile name', 'code-profiler')
672 + );
673 +
339 674 $profile = sanitize_file_name( $_POST['profile'] );
340 675 if ( empty( $profile ) ) {
341 676 $msg = esc_html__('Missing profile name.', 'code-profiler');
342 677 $response['message'] = $msg;
@@ -343,9 +678,11 @@
343 678 code_profiler_log_error( $msg );
344 679 code_profiler_wp_send_json( $response );
345 680 }
346 681
347 - code_profiler_log_info( esc_html__('Preparing report', 'code-profiler') );
682 + code_profiler_log_info(
683 + esc_html__('Preparing the report', 'code-profiler')
684 + );
348 685 require 'class-report.php';
349 686 $report = new CodeProfiler_Report( $profile, $microtime );
350 687 $report->prepare_report();
351 688
@@ -350,26 +687,107 @@
350 687 $report->prepare_report();
351 688
352 689 // Take a 1s break so that we can spot any potential error
353 690 // in the backend before AJAX refresh the page
354 - usleep(1000000);
691 + usleep( 1000000 );
355 692
356 - // Clear hash
357 - $cp_options = get_option('code-profiler');
358 - unset( $cp_options['hash'] );
359 - update_option('code-profiler', $cp_options );
360 -
361 - code_profiler_log_info( esc_html__('All done, exiting profiler', 'code-profiler') );
693 + code_profiler_log_info(
694 + esc_html__('All done, exiting profiler', 'code-profiler')
695 + );
362 696 $response['cp_profile'] = $microtime;
363 697 $response['status'] = 'success';
364 698 $response['message'] = 'success';
365 699
700 + code_profiler_log_debug(
701 + esc_html__('Leaving AJAX endpoint', 'code-profiler')
702 + );
703 +
366 704 // AJAX action?
367 705 if ( defined('DOING_AJAX') && DOING_AJAX ) {
368 706 code_profiler_wp_send_json( $response );
369 - } else {
370 - return json_encode( $response );
371 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 );
372 790
373 791 }
374 792
375 793 // =====================================================================