PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.9.6
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.9.6
1.9.6 1.9.5 1.9.4 1.9.3 trunk 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.6 1.6.1 1.6.10 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 All 41 releases
← All changes | lib/helper.php +522 -102 1.5.51.9.6 View file →
@@ -1,6 +1,6 @@
1 1 <?php
2 -/*
2 +/**
3 3 +=====================================================================+
4 4 | ____ _ ____ __ _ _ |
5 5 | / ___|___ __| | ___ | _ \ _ __ ___ / _(_) | ___ _ __ |
6 6 | | | / _ \ / _` |/ _ \ | |_) | '__/ _ \| |_| | |/ _ \ '__| |
@@ -6,96 +6,246 @@
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 // =====================================================================
17 19 // Various constants
18 20
19 -$upload_dir = wp_upload_dir();
20 -define('CODE_PROFILER_UPLOAD_DIR', $upload_dir['basedir'] .'/code-profiler');
21 +// The profiles directory can be defined in the wp-config.php script
22 +if (! defined('CODE_PROFILER_UPLOAD_DIR') ) {
23 + // When running Code Profiler via WP CLI on a child site of a multisite installation,
24 + // we need to get the main site upload folder otherwise wp_upload_dir will return
25 + // the child site's
26 + if ( is_multisite() && ! ( is_main_network() && is_main_site() && defined('MULTISITE') ) ) {
27 + $upload_dir = code_profiler_upload_dir();
28 + } else {
29 + $upload_dir = wp_upload_dir();
30 + }
31 + define('CODE_PROFILER_UPLOAD_DIR', $upload_dir['basedir'] .'/code-profiler');
32 +}
21 33 define('CODE_PROFILER_LOG', CODE_PROFILER_UPLOAD_DIR .'/log.php');
22 34 define('CODE_PROFILER_TMP_IOSTATS_LOG', 'iostats.tmp');
23 35 define('CODE_PROFILER_TMP_SUMMARY_LOG', 'summary.tmp');
24 -define('CODE_PROFILER_TMP_TICKS_LOG', 'ticks.tmp');
36 +define('CODE_PROFILER_TMP_RERUN_LOG', 'rerun.tmp');
37 +define('CODE_PROFILER_TMP_CALLS_LOG', 'calls.tmp');
25 38 define('CODE_PROFILER_TMP_DISKIO_LOG', 'diskio.tmp');
39 +define('CODE_PROFILER_TMP_CONNECTIONS_LOG', 'connections.tmp');
26 40 define('CODE_PROFILER_UPDATE_NOTICE', '<div class="updated notice is-dismissible"><p>%s</p></div>');
27 41 define('CODE_PROFILER_ERROR_NOTICE', '<div class="error notice is-dismissible"><p>%s</p></div>');
28 -global $wp_version;
29 -if (! defined('CODE_PROFILER_UA') ) { // UA signatures can be user-defined in the wp-config.php
30 - define ('CODE_PROFILER_UA', [
31 - esc_html__('Desktop', 'code-profiler') => [
32 - 'Firefox' => 'Mozilla/5.0 (Linux x86_64; rv:104.0) Gecko/20100101 Firefox/104.0',
33 - 'Chrome' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)'.
34 - ' Chrome/105.0.0.0 Safari/537.36',
35 - 'Edge' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,'.
36 - ' like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.27'
37 - ],
38 - esc_html__('Mobile', 'code-profiler') => [
39 - 'Android Phone' => 'Mozilla/5.0 (Android 12; Mobile; rv:68.0) Gecko/68.0 Firefox/104.0',
40 - 'Android Tablet' => 'Mozilla/5.0 (Linux; Android 12.0; SAMSUNG-SM-T377A Build/NMF26X)'.
41 - ' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36',
42 - 'iPhone' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_5 like Mac OS X) AppleWebKit/605.1.15'.
43 - ' (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
44 - 'iPad' => 'Mozilla/5.0 (iPad; CPU OS 15_5 like Mac OS X) AppleWebKit/605.1.15'.
45 - ' (KHTML, like Gecko) GSA/213.0.449417121 Mobile/15E148 Safari/604.1'
46 - ],
47 - esc_html__('Bot', 'code-profiler') => [
48 - 'Google Bot' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
49 - 'WordPress' => 'Mozilla/5.0 (compatible; CodeProfiler for WordPress/'. $wp_version .
50 - '; https://code-profiler.com/)'
51 - ]
42 +define('CODE_PROFILER_WARNING_NOTICE', '<div class="notice notice-warning"><p>%s</p></div>');
43 +if (! defined('CODE_PROFILER_MUPLUGIN') ) {
44 + // MU plugin's name can be defined in the wp-config.php
45 + define('CODE_PROFILER_MUPLUGIN', '0----code-profiler.php');
46 +}
52 47
53 - ] );
48 +/**
49 + * Since WP 6.7, translation loading must not be triggered too early.
50 + */
51 +add_action('init', 'code_profiler_i18n_constants');
52 +function code_profiler_i18n_constants() {
53 +
54 + global $wp_version;
55 + if (! defined('CODE_PROFILER_UA') ) { // UA signatures can be user-defined in the wp-config.php
56 + define ('CODE_PROFILER_UA', [
57 + esc_html__('Desktop', 'code-profiler') => [
58 + 'Firefox' => 'Mozilla/5.0 (Linux x86_64; rv:149.0) Gecko/20100101 Firefox/149.0',
59 + 'Chrome' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML,'.
60 + ' like Gecko) Chrome/147.0.0.0 Safari/537.36',
61 + 'Edge' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,'.
62 + ' like Gecko) Chrome/147.0.0.0 Safari/537.36 Edg/Chrome/147.0.7727.50'
63 + ],
64 + esc_html__('Mobile', 'code-profiler') => [
65 + 'Android Phone' => 'Mozilla/5.0 (Android 16; Mobile; rv:68.0) Gecko/68.0 Firefox/149.0',
66 + 'Android Tablet' => 'Mozilla/5.0 (Linux; Android 16.0; SAMSUNG-SM-T377A Build/NMF26X)'.
67 + ' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.7727.50 Mobile Safari/537.36',
68 + 'iPhone' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7_7 like Mac OS X) AppleWebKit/605.1.15'.
69 + ' (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1',
70 + 'iPad' => 'Mozilla/5.0 (iPad; CPU OS 18_7_7 like Mac OS X) AppleWebKit/605.1.15'.
71 + ' (KHTML, like Gecko) GSA/213.0.449417121 Mobile/15E148 Safari/605.1.15'
72 + ],
73 + esc_html__('Bot', 'code-profiler') => [
74 + 'Google Bot' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
75 + 'WordPress' => 'Mozilla/5.0 (compatible; CodeProfiler for WordPress/'. $wp_version .
76 + '; https://nintechnet.com/codeprofiler/)'
77 + ]
78 +
79 + ] );
80 + }
81 + define('CODE_PROFILER_ACCURACY', [
82 + 1 => __('Highest', 'code-profiler'),
83 + 5 => __('High', 'code-profiler'),
84 + 10 => __('Moderate', 'code-profiler'),
85 + 15 => __('Low', 'code-profiler'),
86 + 20 => __('Lowest', 'code-profiler')
87 + ]);
54 88 }
55 89
56 -if (! defined('CODE_PROFILER_MUPLUGIN') ) {
57 - // MU plugin's name can be defined in the wp-config.php
58 - define('CODE_PROFILER_MUPLUGIN', '0----code-profiler.php');
90 +// =====================================================================
91 +// Find the main site upload dir on a multisite installation.
92 +// Used when running the profiler via WP CLI.
93 +// This code is based on the WP private _wp_upload_dir function.
94 +
95 +function code_profiler_upload_dir() {
96 +
97 + $upload_path = trim( get_option('upload_path') );
98 + if ( empty( $upload_path ) || 'wp-content/uploads' === $upload_path ) {
99 + $basedir = WP_CONTENT_DIR . '/uploads';
100 + } elseif ( strpos( $upload_path, ABSPATH ) !== 0 ) {
101 + // $basedir is absolute, $upload_path is (maybe) relative to ABSPATH.
102 + $basedir = path_join( ABSPATH, $upload_path );
103 + } else {
104 + $basedir = $upload_path;
105 + }
106 +
107 + if ( defined('UPLOADS') && ! ( is_multisite() && get_site_option('ms_files_rewriting') ) ) {
108 + $basedir = ABSPATH . UPLOADS;
109 + }
110 +
111 + return ['basedir' => $basedir];
59 112 }
60 -define('CODE_PROFILER_ACCURACY', [
61 - 1 => __('Highest (default)', 'code-profiler'),
62 - 5 => __('High', 'code-profiler'),
63 - 10 => __('Moderate', 'code-profiler'),
64 - 15 => __('Low', 'code-profiler'),
65 - 20 => __('Lowest', 'code-profiler')
66 -]);
113 +
67 114 // =====================================================================
115 +// Prevent cURL timeout if a plugin changes its timeout options.
116 +
117 +function code_profiler_curl_timeout( $handle, $parsed_args, $url ) {
118 +
119 + if ( isset( $_REQUEST['action'] ) &&
120 + $_REQUEST['action'] == 'codeprofiler_start_profiler') {
121 +
122 + curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, 300 );
123 + curl_setopt( $handle, CURLOPT_TIMEOUT, 300 );
124 + }
125 +}
126 +add_action('http_api_curl', 'code_profiler_curl_timeout', 1000, 3 );
127 +
128 +// =====================================================================
68 129 // Update version in the DB and check if options must be updated.
69 130
70 131 function code_profiler_init_update() {
71 132
72 - $cp_options = get_option('code-profiler');
73 - if ( empty( $cp_options['version'] ) ||
74 - version_compare( $cp_options['version'], CODE_PROFILER_VERSION, '<') ) {
133 + if ( ( $cp_options = get_option('code-profiler') ) == false ) {
134 + /**
135 + * "Automatic conversion of false to array is deprecated" since PHP 8.1
136 + */
137 + $cp_options = [];
138 + }
75 139
140 + if ( empty( $cp_options['version'] ) ) {
76 141 $cp_options['version'] = CODE_PROFILER_VERSION;
142 + update_option('code-profiler', $cp_options );
77 143
144 + } elseif ( version_compare( $cp_options['version'], CODE_PROFILER_VERSION, '<') ) {
145 +
78 146 // Version 1.1
79 - if (! isset( $cp_options['enable_wpcli'] ) ) {
80 - $cp_options['enable_wpcli'] = 1;
147 + if ( version_compare( $cp_options['version'], '1.1', '<' ) ) {
148 + if (! isset( $cp_options['enable_wpcli'] ) ) {
149 + $cp_options['enable_wpcli'] = 1;
150 + }
81 151 }
82 152
83 153 // Version 1.2
84 - if (! isset( $cp_options['disable_wpcron'] ) ) {
85 - $cp_options['disable_wpcron'] = 1;
154 + if ( version_compare( $cp_options['version'], '1.2', '<' ) ) {
155 + if (! isset( $cp_options['disable_wpcron'] ) ) {
156 + $cp_options['disable_wpcron'] = 1;
157 + }
86 158 }
87 159
88 160 // Version 1.3.1
89 - if (! isset( $cp_options['http_response'] ) ) {
90 - $cp_options['http_response'] = '^(?:3|4|5)\d{2}$';
161 + if ( version_compare( $cp_options['version'], '1.3.1', '<' ) ) {
162 + if (! isset( $cp_options['http_response'] ) ) {
163 + $cp_options['http_response'] = '^(?:3|4|5)\d{2}$';
164 + }
91 165 }
92 166
93 167 // Version 1.5
94 - if (! isset( $cp_options['accuracy'] ) ) {
95 - $cp_options['accuracy'] = 1;
168 + if ( version_compare( $cp_options['version'], '1.5', '<' ) ) {
169 + if (! isset( $cp_options['accuracy'] ) ) {
170 + $cp_options['accuracy'] = 1;
171 + }
96 172 }
97 173
174 + // Version 1.7.5
175 + if ( version_compare( $cp_options['version'], '1.7.5', '<' ) ) {
176 + if (! isset( $cp_options['php_error'] ) ) {
177 + $cp_options['php_error'] = 1;
178 + }
179 + }
180 +
181 + // Version 1.8
182 + if ( version_compare( $cp_options['version'], '1.8', '<' ) ) {
183 + if ( isset( $cp_options['mem_where'] ) ) {
184 + $cp_options['mem']['x_end'] = $cp_options['mem_where'];
185 + unset( $cp_options['mem_where'] );
186 + }
187 + if ( isset( $cp_options['mem_post'] ) ) {
188 + $cp_options['mem']['post'] = $cp_options['mem_post'];
189 + unset( $cp_options['mem_post'] );
190 + }
191 + if ( isset( $cp_options['mem_user'] ) ) {
192 + $cp_options['mem']['x_auth'] = $cp_options['mem_user'];
193 + unset( $cp_options['mem_user'] );
194 + }
195 + if ( isset( $cp_options['mem_username'] ) ) {
196 + $cp_options['mem']['username'] = $cp_options['mem_username'];
197 + unset( $cp_options['mem_username'] );
198 + }
199 + if ( isset( $cp_options['mem_method'] ) ) {
200 + $cp_options['mem']['method'] = $cp_options['mem_method'];
201 + unset( $cp_options['mem_method'] );
202 + }
203 + if ( isset( $cp_options['mem_theme'] ) ) {
204 + $cp_options['mem']['theme'] = $cp_options['mem_theme'];
205 + unset( $cp_options['mem_theme'] );
206 + }
207 + if ( isset( $cp_options['ua'] ) ) {
208 + $cp_options['mem']['user_agent'] = $cp_options['ua'];
209 + unset( $cp_options['ua'] );
210 + }
211 + if ( isset( $cp_options['cookies'] ) ) {
212 + $cp_options['mem']['cookies'] = $cp_options['cookies'];
213 + unset( $cp_options['cookies'] );
214 + }
215 + if ( isset( $cp_options['mem_content_type'] ) ) {
216 + $cp_options['mem']['content_type'] = $cp_options['mem_content_type'];
217 + unset( $cp_options['mem_content_type'] );
218 + }
219 + if ( isset( $cp_options['payload'] ) ) {
220 + $cp_options['mem']['payload'] = $cp_options['payload'];
221 + unset( $cp_options['payload'] );
222 + }
223 + if ( isset( $cp_options['custom_headers'] ) ) {
224 + $cp_options['mem']['custom_headers'] = $cp_options['custom_headers'];
225 + unset( $cp_options['custom_headers'] );
226 + }
227 + if ( isset( $cp_options['exclusions'] ) ) {
228 + $cp_options['mem']['exclusions'] = $cp_options['exclusions'];
229 + unset( $cp_options['exclusions'] );
230 + }
231 + }
232 +
233 + // Version 1.8.1
234 + if ( version_compare( $cp_options['version'], '1.8.1', '<' ) ) {
235 + CodeProfiler_WPCron::install();
236 + }
237 +
238 + /**
239 + * Version 1.9.2
240 + */
241 + if ( version_compare( $cp_options['version'], '1.9.2', '<' ) ) {
242 + unset( $cp_options['warn_composer'] );
243 + }
244 +
245 + // Adjust current version
246 + $cp_options['version'] = CODE_PROFILER_VERSION;
247 +
98 248 // Update version in the DB
99 249 update_option('code-profiler', $cp_options );
100 250 }
101 251
@@ -108,10 +258,18 @@
108 258
109 259 function code_profiler_check_uploadsdir() {
110 260
111 261 if (! file_exists( CODE_PROFILER_UPLOAD_DIR ) ) {
112 - mkdir( CODE_PROFILER_UPLOAD_DIR, 0755 );
262 + wp_mkdir_p( CODE_PROFILER_UPLOAD_DIR );
113 263 }
264 + if (! is_writable( CODE_PROFILER_UPLOAD_DIR ) || ! is_dir( CODE_PROFILER_UPLOAD_DIR ) ) {
265 + wp_die(
266 + sprintf(
267 + __('Error: The %s folder cannot be created or is read only', 'code-profiler'),
268 + esc_html( CODE_PROFILER_UPLOAD_DIR ) .'/'
269 + )
270 + );
271 + }
114 272 if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/index.html') ) {
115 273 touch( CODE_PROFILER_UPLOAD_DIR .'/index.html');
116 274 }
117 275 // For Apache & Litespeed
@@ -166,12 +324,59 @@
166 324
167 325 }
168 326
169 327 // =====================================================================
328 +// Check the PHP memory limit and return a suggested size
329 +// for the profiler's buffer.
330 +
331 +function code_profiler_suggested_memory() {
332 +
333 + $memory_limit = ini_get('memory_limit');
334 +
335 + if ('-1' == $memory_limit ) {
336 + // Return max size
337 + return 10;
338 + }
339 +
340 + if ( preg_match('/^(\d+)([PTGMK])$/i', $memory_limit, $match ) ) {
341 + $bytes = (int) $match[1];
342 + switch ( strtoupper( $match[2] ) ) {
343 + case 'P':
344 + $bytes *= 1024;
345 + case 'T':
346 + $bytes *= 1024;
347 + case 'G':
348 + $bytes *= 1024;
349 + case 'M':
350 + $bytes *= 1024;
351 + case 'K':
352 + $bytes *= 1024;
353 + }
354 + // 256 MB
355 + if ( $bytes >= 268435456 ) {
356 + return 10;
357 + // 128 MB
358 + } elseif ( $bytes >= 134217728 ) {
359 + return 7;
360 + // 64 MB
361 + } elseif ( $bytes >= 67108864 ) {
362 + return 4;
363 + // <64 MB
364 + } else {
365 + return 1;
366 + }
367 + }
368 + // Don't know :/
369 + return 5;
370 +}
371 +
372 +// =====================================================================
170 373 // Create the default options.
171 374
172 375 function code_profiler_default_options() {
173 376
377 + $buffer = code_profiler_suggested_memory();
378 +
174 379 $cp_options = [
175 380 'show_paths' => 'relative',
176 381 'display_name' => 'full',
177 382 'truncate_name' => 30,
@@ -178,13 +383,13 @@
178 383 'chart_type' => 'x',
179 384 'chart_max_plugins' => 25,
180 385 'hide_empty_value' => 1,
181 386 'table_max_rows' => 30,
182 - 'warn_composer' => 1,
183 387 'enable_wpcli' => 1,
184 388 'disable_wpcron' => 1,
185 389 'http_response' => '^(?:3|4|5)\d{2}$',
186 - 'accuracy' => 1
390 + 'accuracy' => 1,
391 + 'buffer' => (int) $buffer
187 392 ];
188 393
189 394 update_option('code-profiler', $cp_options );
190 395
@@ -189,15 +394,23 @@
189 394 update_option('code-profiler', $cp_options );
190 395
191 396 }
192 397
193 -// =====================================================================
398 +// ===================================================================== 2023-06-14
194 399 // Create a profile name.
195 400
196 401 function code_profiler_profile_name() {
197 402
198 403 return date('Y-m-d_') . substr( time(), 4 );
404 +}
199 405
406 +// ===================================================================== 2023-06-14
407 +// Disable PHP display_errors so that notice, warning and error messages
408 +// don't show up in the AJAX response.
409 +
410 +function code_profiler_hide_errors() {
411 +
412 + ini_set('display_errors', 0 );
200 413 }
201 414
202 415 // =====================================================================
203 416 // Disable opcode cache.
@@ -204,54 +417,75 @@
204 417
205 418 function code_profiler_disable_opcode() {
206 419
207 420 try {
208 - if (extension_loaded('Zend OPcache')) {
421 + if ( extension_loaded('Zend OPcache') ) {
209 422 ini_set('opcache.enable', 0);
210 423 } elseif ( extension_loaded('wincache') ) {
211 424 ini_set('wincache.fcenabled', 0);
212 425 }
213 426 set_time_limit(0);
427 + ini_set('memory_limit', -1);
214 428
215 429 } catch ( Exception $e ) { }
216 430
217 431 $cp_options = get_option('code-profiler');
432 + /**
433 + * Disable WP-CRON.
434 + */
218 435 if (! empty( $cp_options['disable_wpcron'] ) ) {
219 436 if (! defined('DISABLE_WP_CRON') ) {
220 437 define('DISABLE_WP_CRON', true );
221 438 }
222 439 }
440 + /**
441 + * Enable PHP error logging.
442 + */
443 + if (! empty( $cp_options['php_error'] ) ) {
444 + ini_set('log_errors', 1 );
445 + $phplog = ini_get('error_log');
446 + if ( empty( $phplog ) || $phplog === false ) {
447 + ini_set('error_log', WP_CONTENT_DIR .'/debug.log');
448 + } else {
449 + ini_set('error_log', $phplog );
450 + }
451 + }
452 +}
223 453
224 -}
225 454 // =====================================================================
226 -// Verify the security key when running the profile.
455 +// Verify the security key when running the profiler.
227 456
228 457 function code_profiler_verify_key() {
229 458
230 459 $response = [
231 460 'status' => 'error',
232 - 'message' => __('Security keys do not match. Reload the page and try again (%s)', 'code-profiler')
461 + // We cannot load translation here.
462 + 'message' => 'Security keys do not match. Reload the page and try again (%s)'
233 463 ];
234 464
235 - $cp_options = get_option('code-profiler');
465 + if ( empty( $_REQUEST['profiler_key'] ) ) {
466 + $response['message'] = sprintf( $response['message'], '001');
236 467
237 - if ( empty( $cp_options['hash'] ) ) {
238 - $response['message'] = sprintf( $response['message'], '#1');
239 -
240 468 } else {
241 - if ( empty( $_REQUEST['profiler_key'] ) ) {
242 - $response['message'] = sprintf( $response['message'], '#2');
469 + $file = CODE_PROFILER_UPLOAD_DIR .'/key_'. sha1( $_REQUEST['profiler_key'] ) .'.tmp';
470 + if ( file_exists( $file ) ) {
471 + /**
472 + * We limit the TTL to max $expire seconds to match code_profiler_cleantmpfiles().
473 + */
474 + $now = time();
475 + $expire = 20; // seconds
476 + if ( filemtime( $file ) + $expire < $now ) {
477 + $response['message'] = sprintf( $response['message'], '003');
478 + wp_send_json( $response );
479 + }
243 480
244 - } else {
245 - if ( $cp_options['hash'] == sha1( $_REQUEST['profiler_key'] ) ) {
246 - return;
247 - } else {
248 - $response['message'] = sprintf( $response['message'], '#3');
249 - }
481 + // Delete it and accept the request
482 + unlink( $file );
483 + return;
250 484 }
485 + $response['message'] = sprintf( $response['message'], '002');
251 486 }
252 487 wp_send_json( $response );
253 -
254 488 }
255 489
256 490 // =====================================================================
257 491 // Write message to the log.
@@ -260,30 +494,64 @@
260 494
261 495 function code_profiler_write2log( $message, $level, $create ) {
262 496
263 497 if ( empty( $create ) ) {
264 - file_put_contents( CODE_PROFILER_LOG, time() ."~~$level~~$message\n", FILE_APPEND );
498 + file_put_contents(
499 + CODE_PROFILER_LOG,
500 + time() ."~~$level~~$message\n",
501 + FILE_APPEND
502 + );
265 503 } else {
266 - file_put_contents( CODE_PROFILER_LOG, time() ."~~$level~~$message\n");
504 + file_put_contents(
505 + CODE_PROFILER_LOG,
506 + time() ."~~$level~~$message\n"
507 + );
267 508 }
268 509 }
269 510
270 -function code_profiler_log_info( $string, $create = 0 ) { code_profiler_write2log( $string, 1, $create ); }
271 -function code_profiler_log_warn( $string, $create = 0 ) { code_profiler_write2log( $string, 2, $create ); }
272 -function code_profiler_log_error( $string, $create = 0 ) { code_profiler_write2log( $string, 4, $create ); }
273 -function code_profiler_log_debug( $string, $create = 0 ) { code_profiler_write2log( $string, 8, $create ); }
511 +function code_profiler_log_info( $string, $create = 0 ) {
512 + code_profiler_write2log( $string, 1, $create );
513 +}
514 +function code_profiler_log_warn( $string, $create = 0 ) {
515 + code_profiler_write2log( $string, 2, $create );
516 +}
517 +function code_profiler_log_error( $string, $create = 0 ) {
518 + code_profiler_write2log( $string, 4, $create );
519 +}
520 +function code_profiler_log_debug( $string, $create = 0 ) {
521 + code_profiler_write2log( $string, 8, $create );
522 +}
274 523
275 524 // =====================================================================
525 +// Retrieve and return matching files from a directory.
526 +
527 +function code_profiler_glob( $directory, $regex, $pathname = false ) {
528 +
529 + $list = [];
530 +
531 + foreach ( new DirectoryIterator( $directory ) as $finfo ) {
532 + if (! $finfo->isDot() && preg_match("`$regex`", $finfo->getFilename() ) ) {
533 + if ( $pathname ) {
534 + $list[] = $finfo->getPathname();
535 + } else {
536 + $list[] = $finfo->getFilename();
537 + }
538 + }
539 + }
540 + return $list;
541 +}
542 +
543 +// =====================================================================
276 544 // Verify if a profile exists and return its full path.
277 545
278 546 function code_profiler_get_profile_path( $id, $type = 'slugs') {
279 547
280 - $return = false;
548 + if (! empty( $id ) && preg_match('/^\d{10}\.\d+$/', $id ) ) {
281 549
282 - if (! empty( $id ) && preg_match('/^\d{10}\.\d+$/', $id ) ) {
283 - $glob = glob( CODE_PROFILER_UPLOAD_DIR ."/$id.*.$type.profile" );
550 + $glob = code_profiler_glob(CODE_PROFILER_UPLOAD_DIR, "$id\..+\.$type\.profile$", true);
551 +
284 552 if ( is_array( $glob ) && ! empty( $glob[0] ) ) {
285 - if ( preg_match( "`/$id\.(.+?).$type.profile$`", $glob[0], $match ) ) {
553 + if ( preg_match( "`$id\.(.+?).$type.profile$`", $glob[0], $match ) ) {
286 554
287 555 return CODE_PROFILER_UPLOAD_DIR. "/$id.{$match[1]}";
288 556 }
289 557 }
@@ -311,10 +579,17 @@
311 579 $buffer['error'] = $err;
312 580 return $buffer;
313 581 }
314 582
583 + /**
584 + * We don't use fgetcsv() as it requires the $escape parameter since PHP 8.4
585 + */
315 586 while (! feof( $fh ) ) {
316 - $buffer[] = fgetcsv( $fh, 1000, "\t" );
587 + $tmp = trim( fgets( $fh, 1000 ) );
588 + if (! $tmp ) {
589 + continue;
590 + }
591 + $buffer[] = explode( "\t", $tmp );
317 592 }
318 593
319 594 fclose( $fh );
320 595
@@ -369,9 +644,9 @@
369 644
370 645 $string = $open;
371 646
372 647 if ( empty( $decode->items ) ) {
373 - $tmp = 'N/A';
648 + $tmp = __('N/A', 'code-profiler');
374 649 } else {
375 650 $tmp = (int) --$decode->items;
376 651 }
377 652 $string .= sprintf(
@@ -381,9 +656,9 @@
381 656
382 657 $string .= $div;
383 658
384 659 if ( empty( $decode->time ) ) {
385 - $tmp = 'N/A';
660 + $tmp = __('N/A', 'code-profiler');
386 661 } else {
387 662 $tmp = number_format( $decode->time, 4 );
388 663 }
389 664 $string .= sprintf(
@@ -393,9 +668,9 @@
393 668
394 669 $string .= $div;
395 670
396 671 if ( empty( $decode->memory ) ) {
397 - $tmp = 'N/A';
672 + $tmp = __('N/A', 'code-profiler');
398 673 } else {
399 674 $tmp = number_format( $decode->memory, 2);
400 675 }
401 676 $string .= sprintf(
@@ -405,11 +680,11 @@
405 680
406 681 $string .= $div;
407 682
408 683 if ( empty( $decode->io ) ) {
409 - $tmp = 'N/A';
684 + $tmp = __('N/A', 'code-profiler');
410 685 } else {
411 - $tmp = number_format( $decode->io );
686 + $tmp = number_format( (int) $decode->io );
412 687 }
413 688 $string .= sprintf(
414 689 __('File I/O operations: %s', 'code-profiler'),
415 690 $tmp
@@ -417,11 +692,11 @@
417 692
418 693 $string .= $div;
419 694
420 695 if ( empty( $decode->queries ) ) {
421 - $tmp = 'N/A';
696 + $tmp = __('N/A', 'code-profiler');
422 697 } else {
423 - $tmp = number_format( $decode->queries );
698 + $tmp = number_format( (int) $decode->queries );
424 699 }
425 700 $string .= sprintf(
426 701 __('SQL queries: %s', 'code-profiler'),
427 702 $tmp
@@ -426,8 +701,20 @@
426 701 __('SQL queries: %s', 'code-profiler'),
427 702 $tmp
428 703 );
429 704
705 + $string .= $div;
706 +
707 + if ( empty( CODE_PROFILER_ACCURACY[ $decode->precision ] ) ) {
708 + $tmp = __('N/A', 'code-profiler');
709 + } else {
710 + $tmp = CODE_PROFILER_ACCURACY[ $decode->precision ];
711 + }
712 + $string .= sprintf(
713 + __('Accuracy: %s', 'code-profiler'),
714 + $tmp
715 + );
716 +
430 717 $string .= $close;
431 718
432 719 return $string;
433 720 }
@@ -432,29 +719,49 @@
432 719 return $string;
433 720 }
434 721
435 722 // =====================================================================
436 -// Remove *tmp files left after an HTTP error (4xx or 5xx).
723 +// Remove *tmp files left in the profiles folder.
437 724
438 725 function code_profiler_cleantmpfiles() {
439 726
440 - $glob = glob( CODE_PROFILER_UPLOAD_DIR .'/*.tmp');
727 + $glob = code_profiler_glob( CODE_PROFILER_UPLOAD_DIR, '\.tmp$', true );
728 +
441 729 if ( is_array( $glob ) ) {
442 - $count = 0;
730 +
731 + $now = time();
443 732 foreach( $glob as $file ) {
444 - $count++;
733 + /**
734 + * Temporary files deletion:
735 + * - +20 seconds for temp sessions (to match the TTL checked at runtime).
736 + * - +10 minutes for unmatched temp profiles.
737 + */
738 + if ( strpos( $file, CODE_PROFILER_UPLOAD_DIR .'/key_') === 0 ) {
739 + if ( filemtime( $file ) + 20 > $now ) {
740 + continue;
741 + }
742 + } else {
743 + if ( filemtime( $file ) + 60 * 10 > $now ) {
744 + continue;
745 + }
746 + }
747 + /**
748 + * Delete all temp files.
749 + */
445 750 unlink( $file );
446 751 }
447 - if ( $count ) {
448 - code_profiler_log_info( sprintf(
449 - __('Deleting %s temporary files found in the profiles folder', 'code-profiler'),
450 - (int) $count
451 - ) );
452 - }
453 752 }
454 753 }
455 754
456 755 // =====================================================================
756 +// Remove non-ASCII characters from a string.
757 +
758 +function code_profiler_ASCII_filter( $string ) {
759 +
760 + return preg_replace('/[\x00-\x1f\x7f-\xff]/', '', $string );
761 +}
762 +
763 +// =====================================================================
457 764 // Clear the log if it's bigger than 100KB.
458 765
459 766 function code_profiler_clearlog() {
460 767
@@ -462,9 +769,9 @@
462 769 file_put_contents( CODE_PROFILER_LOG, "<?php exit; ?>\n" );
463 770 }
464 771 }
465 772
466 -// =====================================================================
773 +// ===================================================================== 2023-06-15
467 774 // We don't want to be bothered by other themes/plugins' admin notices.
468 775
469 776 add_action('admin_head', 'code_profiler_hide_admin_notices');
470 777
@@ -471,8 +778,121 @@
471 778 function code_profiler_hide_admin_notices() {
472 779 if ( isset( $_GET['page'] ) && $_GET['page'] == 'code-profiler') {
473 780 remove_all_actions('admin_notices');
474 781 remove_all_actions('all_admin_notices');
782 + add_filter('admin_footer_text', 'code_profiler_admin_footer');
783 + }
784 +}
785 +
786 +function code_profiler_admin_footer () {
787 + echo '<span id="footer-thankyou">'.
788 + sprintf(
789 + /* Translators: %s are the '<a href="">' and '</a>' anchors */
790 + esc_html(
791 + 'Thank you for using %sCode Profiler%s.',
792 + 'code-profiler'
793 + ),
794 + '<a href="https://nintechnet.com/codeprofiler/" target="_blank">',
795 + '</a>'
796 + ).
797 + '</span>';
798 +}
799 +
800 +
801 +// =====================================================================
802 +// Hide/display an element depending on some value.
803 +
804 +function code_profiler_showhide( $var, $val ) {
805 +
806 + if ( $var == $val ) {
807 + echo " style='display:block'";
808 + } else {
809 + echo " style='display:none'";
810 + }
811 +}
812 +
813 +// =====================================================================
814 +// Retrieve all themes.
815 +
816 +function code_profiler_get_themes() {
817 +
818 + $list = [];
819 +
820 + // Make sure the function is loaded
821 + if (! function_exists('wp_get_themes') ) {
822 + require_once ABSPATH . 'wp-includes/theme.php';
823 + }
824 + $themes = wp_get_themes();
825 + foreach( $themes as $k => $v ) {
826 + if ( $v->Name ) {
827 + $list[ $k ] = [
828 + 'n' => $v->Name,
829 +
830 + 't' => $v->Template
831 + ];
832 + } else {
833 + $list[ $k ] = [
834 + 'n' => $k,
835 + 't' => $v->Template
836 + ];
837 + }
838 + }
839 + return $list;
840 +}
841 +
842 +// =====================================================================
843 +// Retrieve all available cron events.
844 +
845 +function code_profiler_get_crons() {
846 +
847 + $list = [];
848 +
849 + $wp_crons = _get_cron_array();
850 +
851 + if ( empty( $wp_crons ) ) {
852 + return $list;
853 + }
854 +
855 + foreach ( $wp_crons as $timestamp => $cronhooks ) {
856 + foreach ( $cronhooks as $hook => $keys ) {
857 + $list[] = $hook;
858 + }
859 + }
860 + sort( $list );
861 + return $list;
862 +}
863 +
864 +// =====================================================================
865 +// Create a file with the ABSPATH.
866 +
867 +function code_profiler_create_tmpfile() {
868 +
869 + $tmp_folder = dirname( __DIR__ ) .'/tmp';
870 + $tmp_file = "$tmp_folder/profiler.inc.php";
871 +
872 + if (! is_dir( $tmp_folder ) ) {
873 + $res = @ mkdir( $tmp_folder );
874 + if ( false === $res ) {
875 + return sprintf(
876 + __('Cannot create temporary folder: [%s]. Any attempt to profile a cron event will likely fail.', 'code-profiler'),
877 + $tmp_folder
878 + );
879 + }
880 + }
881 +
882 + if (! is_file( "$tmp_folder/index.html" ) ) {
883 + touch( "$tmp_folder/index.html" );
884 + }
885 +
886 + $res = @ file_put_contents(
887 + $tmp_file,
888 + "<?php\nconst ABSPATH = '". ABSPATH ."';\n"
889 + );
890 + if ( false === $res ) {
891 + return sprintf(
892 + __('Cannot create temporary file: [%s]. Any attempt to profile a cron event will likely fail.', 'code-profiler'),
893 + $tmp_file
894 + );
475 895 }
476 896 }
477 897
478 898 // =====================================================================