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 +565 -71 1.41.7.4 View file →
@@ -6,27 +6,35 @@
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 -add_action( 'wp_ajax_codeprofiler_start_profiler', 'codeprofiler_start_profiler' );
21 +add_action('wp_ajax_codeprofiler_start_profiler', 'codeprofiler_start_profiler');
20 22
21 23 function codeprofiler_start_profiler() {
22 24
23 25 $response = ['status' => 'error'];
24 26
25 - $cp_options = get_option( 'code-profiler' );
27 + code_profiler_hide_errors();
26 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 +
27 35 // If this is an AJAX call, make sure it comes from an admin/superadmin.
28 - if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_start_profiler' ) {
36 + if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_start_profiler') {
29 37 // Admin/Superadmin only
30 38 if (! is_super_admin() ) {
31 39 $msg = esc_html__('You are not allowed to performed this action', 'code-profiler');
32 40 $response['message'] = $msg;
@@ -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,34 +71,55 @@
50 71 code_profiler_log_error( $msg );
51 72 code_profiler_wp_send_json( $response );
52 73 }
53 74
75 + code_profiler_log_debug(
76 + esc_html__('Retrieving parameters #1', 'code-profiler')
77 + );
78 +
54 79 // Frontend or backend
55 - if ( empty( $_POST['where'] ) || ! in_array( $_POST['where'], [ 'frontend', 'backend', 'custom' ] ) ) {
56 - $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 + );
57 86 $response['message'] = $msg;
58 87 code_profiler_log_error( $msg );
59 88 code_profiler_wp_send_json( $response );
60 89 }
90 + $cp_options['mem_where'] = $_POST['where'];
91 +
92 + code_profiler_log_debug(
93 + esc_html__('Retrieving parameters #2', 'code-profiler')
94 + );
95 +
61 96 if ( empty( $_POST['post'] ) ) {
62 - $msg = sprintf( esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'post' );
97 + $msg = sprintf( esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'post');
63 98 $response['message'] = $msg;
64 99 code_profiler_log_error( $msg );
65 100 code_profiler_wp_send_json( $response );
66 101 }
102 + $cp_options['mem_post'] = $_POST['post'];
67 103
68 104 // Make sure we have no more that 4 decimals, because when returning
69 105 // it via AJAX, it will display more decimals than that
70 - $microtime = number_format( microtime( true ), 4, '.', '' );
106 + $microtime = number_format( microtime( true ), 4, '.', '');
71 107
108 + code_profiler_log_debug(
109 + esc_html__('Retrieving parameters #3', 'code-profiler')
110 + );
111 +
72 112 // Authentication
73 - if ( empty( $_POST['user'] ) || ! in_array( $_POST['user'], [ 'authenticated', 'unauthenticated' ] ) ) {
74 - $msg = sprintf( esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'user' );
75 - $response['message'] = $msg;
76 - code_profiler_log_error( $msg );
77 - code_profiler_wp_send_json( $response );
113 + if ( empty( $_POST['user'] ) || $_POST['user'] != 'authenticated' ) {
114 + $_POST['user'] = 'unauthenticated';
78 115 }
116 + $cp_options['mem_user'] = $_POST['user'];
79 117
118 + code_profiler_log_debug(
119 + esc_html__('Retrieving parameters #4', 'code-profiler')
120 + );
121 +
80 122 if ( empty( $_POST['profile'] ) || strlen( $_POST['profile'] ) > 100 ) {
81 123 $profile = code_profiler_profile_name();
82 124 } else {
83 125 $profile = sanitize_file_name( $_POST['profile'] );
@@ -83,19 +125,26 @@
83 125 $profile = sanitize_file_name( $_POST['profile'] );
84 126 }
85 127
86 128 // URI to profile
87 - $url = esc_url_raw( $_POST['post'] );
129 + $url = esc_url_raw( $_POST['post'] );
130 + $siteurl = esc_html( site_url() );
88 131 code_profiler_log_info( sprintf(
89 - 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'),
90 134 CODE_PROFILER_VERSION,
91 - $url,
92 - $profile
135 + $siteurl,
136 + $profile,
137 + $url
93 138 ) );
94 139
140 + code_profiler_log_debug(
141 + esc_html__('Retrieving parameters #5', 'code-profiler')
142 + );
143 +
95 144 // User-agent
96 145 if ( empty( $_POST['ua'] ) ) {
97 - $ua = 'FireFox';
146 + $ua = 'Firefox';
98 147 } else {
99 148 $ua = sanitize_text_field( $_POST['ua'] );
100 149 }
101 150 foreach( CODE_PROFILER_UA as $types => $types_array ) {
@@ -106,20 +155,50 @@
106 155 }
107 156 }
108 157 }
109 158 if ( empty( $ua_signature ) ) {
110 - $ua_signature = CODE_PROFILER_UA['Desktop']['FireFox'];
159 + $ua_signature = CODE_PROFILER_UA['Desktop']['Firefox'];
111 160 }
161 + $cp_options['ua'] = $ua;
112 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 +
113 186 // Create security key
114 187 $profiler_key = bin2hex( random_bytes( 16 ) );
115 - $cp_options['hash'] = sha1( $profiler_key );
116 - update_option( 'code-profiler', $cp_options );
188 + touch( CODE_PROFILER_UPLOAD_DIR .'/key_'. sha1( $profiler_key ) .'.tmp');
117 189
118 - // 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;
119 198 $url = add_query_arg( [
120 199 'CODE_PROFILER_ON' => $microtime,
121 - 'profiler_key' => $profiler_key,
200 + 'profiler_key' => $profiler_key
122 201 ], $url );
123 202
124 203 global $wp_version;
125 204 $headers = [
@@ -127,18 +206,55 @@
127 206 'Pragma' => 'no-cache',
128 207 'Expires' => '0',
129 208 'httpversion' => '1.1',
130 209 // Devs must be allowed to use it on localhost over TLS too
131 - 'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
132 - 'timeout' => 180, // 180-second timeout instead of the default 5s
210 + 'sslverify' => apply_filters('https_local_ssl_verify', false ),
211 + 'timeout' => 300, // 300-second timeout instead of the default 5s
133 212 'redirection' => 0, // We don't want to be redirected
134 213 'headers' => [
135 - 'code-profiler-key' => $profiler_key,
136 - 'Accept-Language' => 'en-US,en;q=0.5',
137 - '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
138 219 ]
139 220 ];
140 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 +
141 257 // Forward basic authentication if any (not available from WP CLI)
142 258 if ( function_exists('apache_request_headers') ) {
143 259 $apache_headers = apache_request_headers();
144 260 if ( isset( $apache_headers['Authorization'] ) ) {
@@ -143,40 +259,216 @@
143 259 $apache_headers = apache_request_headers();
144 260 if ( isset( $apache_headers['Authorization'] ) ) {
145 261 $headers['headers']['Authorization'] = $apache_headers['Authorization'];
146 262 }
147 - // WP-CLI (wp code-profiler run --u=FOO --p=BAR)
263 + // WP-CLI ($ wp code-profiler run --u=FOO --p=BAR)
148 264 } elseif ( defined('WP_CLI') && ! empty( $_POST['Authorization'] ) ) {
149 265 $headers['headers']['Authorization'] = $_POST['Authorization'];
150 266 }
151 267
152 - if ( $_POST['user'] == 'authenticated' ) {
268 + if ( $_POST['user'] == 'authenticated') {
269 +
270 + code_profiler_log_debug(
271 + esc_html__('Creating authentication cookies', 'code-profiler')
272 + );
273 +
153 274 // Used for authentication
154 275 if ( is_ssl() ) {
155 276 $cookie_auth = SECURE_AUTH_COOKIE;
156 - $scheme = 'secure_auth';
277 + $scheme = 'secure_auth';
157 278 } else {
158 279 $cookie_auth = AUTH_COOKIE;
159 - $scheme = 'auth';
280 + $scheme = 'auth';
160 281 }
161 - $id = get_current_user_id();
162 - $headers['cookies'][ $cookie_auth ] = wp_generate_auth_cookie( $id, time() + 180, $scheme );
163 - $headers['cookies'][ LOGGED_IN_COOKIE ] = wp_generate_auth_cookie( $id, time() + 180, 'logged_in' );
164 - $session_id = session_id();
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();
165 349 if ( $session_id !== false ) {
166 - $session_name = session_name();
167 - $headers['cookies'][ $session_name ] = $session_id;
350 + $session_name = session_name();
351 + $headers['cookies'][ $session_name ] = $session_id;
168 352 }
169 353 }
170 354
171 - if ( function_exists( 'opcache_reset' ) ) {
355 + if ( function_exists('opcache_reset') ) {
356 + code_profiler_log_debug(
357 + esc_html__('Clearing opcode cache', 'code-profiler')
358 + );
172 359 opcache_reset();
173 360 }
174 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 +
175 467 // We must allow developers to run the profiler
176 468 // on a local IP (e.g, http://127.0.0.1/)
177 - add_filter( 'http_request_host_is_external', '__return_true' );
178 - $res = wp_safe_remote_get( $url, $headers );
469 + add_filter('http_request_host_is_external', '__return_true');
470 + $res = $safe_method( $url, $headers );
179 471
180 472 // Connection error
181 473 if ( is_wp_error( $res ) ) {
182 474 $msg = esc_html__('Cannot connect to the requested page: %s', 'code-profiler');
@@ -187,23 +479,115 @@
187 479 code_profiler_log_error( sprintf( $msg, $res->get_error_message() ) );
188 480 code_profiler_wp_send_json( $response );
189 481 }
190 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 +
191 544 // HTTP status code
192 545 if (! empty( $cp_options['http_response'] ) ) {
193 546 if ( preg_match( "/{$cp_options['http_response']}/", $res['response']['code'] ) ) {
194 - $msg = esc_html__('The website returned the following HTTP status code: %s %s.', 'code-profiler').
195 - ' '.
196 - 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 +
197 561 $response['message'] = sprintf(
198 562 $msg,
199 563 (int) $res['response']['code'],
200 - esc_html( $res['response']['message'] )
564 + $res['response']['message']
201 565 );
202 - 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 + }
203 582 code_profiler_wp_send_json( $response );
204 583 }
205 584 }
585 +
586 + code_profiler_log_debug(
587 + esc_html__('Decoding body', 'code-profiler')
588 + );
589 +
206 590 // Check response
207 591 $message = json_decode( $res['body'], true );
208 592 if ( isset( $message['status'] ) && isset( $message['message'] ) ) {
209 593 $response['status'] = $message['status'];
@@ -209,26 +593,32 @@
209 593 $response['status'] = $message['status'];
210 594 $response['message'] = $message['message'];
211 595 code_profiler_wp_send_json( $response );
212 596 }
213 - 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 + );
214 600 // Return success
215 601 $response = ['status' => 'success'];
216 602 $response['message'] = 'success';
217 603 $response['microtime'] = $microtime;
218 604
605 + code_profiler_log_debug(
606 + esc_html__('Leaving AJAX endpoint', 'code-profiler')
607 + );
608 +
219 609 // AJAX action?
220 - if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
610 + if ( defined('DOING_AJAX') && DOING_AJAX ) {
221 611 code_profiler_wp_send_json( $response );
222 - } else {
223 - return json_encode( $response );
224 612 }
225 613
614 + return json_encode( $response );
615 +
226 616 }
227 617
228 -// =====================================================================
618 +// ===================================================================== 2023-11-17
229 619
230 -add_action( 'wp_ajax_codeprofiler_prepare_report', 'codeprofiler_prepare_report' );
620 +add_action('wp_ajax_codeprofiler_prepare_report', 'codeprofiler_prepare_report');
231 621
232 622 function codeprofiler_prepare_report() {
233 623
234 624 $response = ['status' => 'error'];
@@ -233,9 +623,9 @@
233 623
234 624 $response = ['status' => 'error'];
235 625
236 626 // If this is an AJAX call, make sure it comes from an admin/superadmin.
237 - if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_prepare_report' ) {
627 + if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_prepare_report') {
238 628 // Admin/Superadmin only
239 629 if (! is_super_admin() ) {
240 630 $msg = esc_html__('You are not allowed to performed this action', 'code-profiler');
241 631 $response['message'] = $msg;
@@ -243,17 +633,34 @@
243 633 code_profiler_wp_send_json( $response );
244 634 }
245 635 }
246 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 +
247 647 // Verify the security nonce
248 - if ( empty( $_POST['cp_nonce'] ) || ! wp_verify_nonce( $_POST['cp_nonce'], 'start_profiler_nonce' ) ) {
249 - $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');
250 653 $response['message'] = $msg;
251 654 code_profiler_log_error( $msg );
252 655 code_profiler_wp_send_json( $response );
253 656 }
254 657
255 - if ( empty( $_POST['microtime'] ) || ! preg_match( '/^\d{10}\.\d+$/', $_POST['microtime'] ) ) {
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'] ) ) {
256 663 $msg = esc_html__('Missing parameter (microtime).', 'code-profiler');
257 664 $response['message'] = $msg;
258 665 code_profiler_log_error( $msg );
259 666 code_profiler_wp_send_json( $response );
@@ -259,8 +666,12 @@
259 666 code_profiler_wp_send_json( $response );
260 667 }
261 668 $microtime = sanitize_text_field( $_POST['microtime'] );
262 669
670 + code_profiler_log_debug(
671 + esc_html__('Retrieving profile name', 'code-profiler')
672 + );
673 +
263 674 $profile = sanitize_file_name( $_POST['profile'] );
264 675 if ( empty( $profile ) ) {
265 676 $msg = esc_html__('Missing profile name.', 'code-profiler');
266 677 $response['message'] = $msg;
@@ -267,9 +678,11 @@
267 678 code_profiler_log_error( $msg );
268 679 code_profiler_wp_send_json( $response );
269 680 }
270 681
271 - code_profiler_log_info( esc_html__('Preparing report', 'code-profiler') );
682 + code_profiler_log_info(
683 + esc_html__('Preparing the report', 'code-profiler')
684 + );
272 685 require 'class-report.php';
273 686 $report = new CodeProfiler_Report( $profile, $microtime );
274 687 $report->prepare_report();
275 688
@@ -274,26 +687,107 @@
274 687 $report->prepare_report();
275 688
276 689 // Take a 1s break so that we can spot any potential error
277 690 // in the backend before AJAX refresh the page
278 - usleep(1000000);
691 + usleep( 1000000 );
279 692
280 - // Clear hash
281 - $cp_options = get_option( 'code-profiler' );
282 - unset( $cp_options['hash'] );
283 - update_option( 'code-profiler', $cp_options );
284 -
285 - 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 + );
286 696 $response['cp_profile'] = $microtime;
287 697 $response['status'] = 'success';
288 698 $response['message'] = 'success';
289 699
700 + code_profiler_log_debug(
701 + esc_html__('Leaving AJAX endpoint', 'code-profiler')
702 + );
703 +
290 704 // AJAX action?
291 - if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
705 + if ( defined('DOING_AJAX') && DOING_AJAX ) {
292 706 code_profiler_wp_send_json( $response );
293 - } else {
294 - return json_encode( $response );
295 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 );
296 790
297 791 }
298 792
299 793 // =====================================================================