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 +486 -96 1.6.21.9.6 View file →
@@ -1,6 +1,6 @@
1 1 <?php
2 -/*
2 +/**
3 3 +=====================================================================+
4 4 | ____ _ ____ __ _ _ |
5 5 | / ___|___ __| | ___ | _ \ _ __ ___ / _(_) | ___ _ __ |
6 6 | | | / _ \ / _` |/ _ \ | |_) | '__/ _ \| |_| | |/ _ \ '__| |
@@ -6,66 +6,112 @@
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');
26 39 define('CODE_PROFILER_TMP_CONNECTIONS_LOG', 'connections.tmp');
27 40 define('CODE_PROFILER_UPDATE_NOTICE', '<div class="updated notice is-dismissible"><p>%s</p></div>');
28 41 define('CODE_PROFILER_ERROR_NOTICE', '<div class="error notice is-dismissible"><p>%s</p></div>');
29 -global $wp_version;
30 -if (! defined('CODE_PROFILER_UA') ) { // UA signatures can be user-defined in the wp-config.php
31 - define ('CODE_PROFILER_UA', [
32 - esc_html__('Desktop', 'code-profiler') => [
33 - 'Firefox' => 'Mozilla/5.0 (Linux x86_64; rv:110.0) Gecko/20100101 Firefox/110.0',
34 - 'Chrome' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)'.
35 - ' Chrome/111.0.0.0 Safari/537.36',
36 - 'Edge' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,'.
37 - ' like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/110.0.1587.63'
38 - ],
39 - esc_html__('Mobile', 'code-profiler') => [
40 - 'Android Phone' => 'Mozilla/5.0 (Android 13; Mobile; rv:68.0) Gecko/68.0 Firefox/110.0',
41 - 'Android Tablet' => 'Mozilla/5.0 (Linux; Android 13.0; SAMSUNG-SM-T377A Build/NMF26X)'.
42 - ' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Mobile Safari/537.36',
43 - 'iPhone' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_3_1 like Mac OS X) AppleWebKit/605.1.15'.
44 - ' (KHTML, like Gecko) Version/16.3 Mobile/15E148 Safari/604.1',
45 - 'iPad' => 'Mozilla/5.0 (iPad; CPU OS 16_3_1 like Mac OS X) AppleWebKit/605.1.15'.
46 - ' (KHTML, like Gecko) GSA/213.0.449417121 Mobile/15E148 Safari/605.1.15'
47 - ],
48 - esc_html__('Bot', 'code-profiler') => [
49 - 'Google Bot' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
50 - 'WordPress' => 'Mozilla/5.0 (compatible; CodeProfiler for WordPress/'. $wp_version .
51 - '; https://code-profiler.com/)'
52 - ]
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 +}
53 47
54 - ] );
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 + ]);
55 88 }
56 89
57 -if (! defined('CODE_PROFILER_MUPLUGIN') ) {
58 - // MU plugin's name can be defined in the wp-config.php
59 - 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];
60 112 }
61 -define('CODE_PROFILER_ACCURACY', [
62 - 1 => __('Highest (default)', 'code-profiler'),
63 - 5 => __('High', 'code-profiler'),
64 - 10 => __('Moderate', 'code-profiler'),
65 - 15 => __('Low', 'code-profiler'),
66 - 20 => __('Lowest', 'code-profiler')
67 -]);
113 +
68 114 // =====================================================================
69 115 // Prevent cURL timeout if a plugin changes its timeout options.
70 116
71 117 function code_profiler_curl_timeout( $handle, $parsed_args, $url ) {
@@ -83,34 +129,123 @@
83 129 // Update version in the DB and check if options must be updated.
84 130
85 131 function code_profiler_init_update() {
86 132
87 - $cp_options = get_option('code-profiler');
88 - if ( empty( $cp_options['version'] ) ||
89 - 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 + }
90 139
140 + if ( empty( $cp_options['version'] ) ) {
91 141 $cp_options['version'] = CODE_PROFILER_VERSION;
142 + update_option('code-profiler', $cp_options );
92 143
144 + } elseif ( version_compare( $cp_options['version'], CODE_PROFILER_VERSION, '<') ) {
145 +
93 146 // Version 1.1
94 - if (! isset( $cp_options['enable_wpcli'] ) ) {
95 - $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 + }
96 151 }
97 152
98 153 // Version 1.2
99 - if (! isset( $cp_options['disable_wpcron'] ) ) {
100 - $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 + }
101 158 }
102 159
103 160 // Version 1.3.1
104 - if (! isset( $cp_options['http_response'] ) ) {
105 - $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 + }
106 165 }
107 166
108 167 // Version 1.5
109 - if (! isset( $cp_options['accuracy'] ) ) {
110 - $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 + }
111 172 }
112 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 +
113 248 // Update version in the DB
114 249 update_option('code-profiler', $cp_options );
115 250 }
116 251
@@ -123,10 +258,18 @@
123 258
124 259 function code_profiler_check_uploadsdir() {
125 260
126 261 if (! file_exists( CODE_PROFILER_UPLOAD_DIR ) ) {
127 - mkdir( CODE_PROFILER_UPLOAD_DIR, 0755 );
262 + wp_mkdir_p( CODE_PROFILER_UPLOAD_DIR );
128 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 + }
129 272 if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/index.html') ) {
130 273 touch( CODE_PROFILER_UPLOAD_DIR .'/index.html');
131 274 }
132 275 // For Apache & Litespeed
@@ -181,12 +324,59 @@
181 324
182 325 }
183 326
184 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 +// =====================================================================
185 373 // Create the default options.
186 374
187 375 function code_profiler_default_options() {
188 376
377 + $buffer = code_profiler_suggested_memory();
378 +
189 379 $cp_options = [
190 380 'show_paths' => 'relative',
191 381 'display_name' => 'full',
192 382 'truncate_name' => 30,
@@ -193,13 +383,13 @@
193 383 'chart_type' => 'x',
194 384 'chart_max_plugins' => 25,
195 385 'hide_empty_value' => 1,
196 386 'table_max_rows' => 30,
197 - 'warn_composer' => 1,
198 387 'enable_wpcli' => 1,
199 388 'disable_wpcron' => 1,
200 389 'http_response' => '^(?:3|4|5)\d{2}$',
201 - 'accuracy' => 1
390 + 'accuracy' => 1,
391 + 'buffer' => (int) $buffer
202 392 ];
203 393
204 394 update_option('code-profiler', $cp_options );
205 395
@@ -204,15 +394,23 @@
204 394 update_option('code-profiler', $cp_options );
205 395
206 396 }
207 397
208 -// =====================================================================
398 +// ===================================================================== 2023-06-14
209 399 // Create a profile name.
210 400
211 401 function code_profiler_profile_name() {
212 402
213 403 return date('Y-m-d_') . substr( time(), 4 );
404 +}
214 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 );
215 413 }
216 414
217 415 // =====================================================================
218 416 // Disable opcode cache.
@@ -219,54 +417,75 @@
219 417
220 418 function code_profiler_disable_opcode() {
221 419
222 420 try {
223 - if (extension_loaded('Zend OPcache')) {
421 + if ( extension_loaded('Zend OPcache') ) {
224 422 ini_set('opcache.enable', 0);
225 423 } elseif ( extension_loaded('wincache') ) {
226 424 ini_set('wincache.fcenabled', 0);
227 425 }
228 426 set_time_limit(0);
427 + ini_set('memory_limit', -1);
229 428
230 429 } catch ( Exception $e ) { }
231 430
232 431 $cp_options = get_option('code-profiler');
432 + /**
433 + * Disable WP-CRON.
434 + */
233 435 if (! empty( $cp_options['disable_wpcron'] ) ) {
234 436 if (! defined('DISABLE_WP_CRON') ) {
235 437 define('DISABLE_WP_CRON', true );
236 438 }
237 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 +}
238 453
239 -}
240 454 // =====================================================================
241 -// Verify the security key when running the profile.
455 +// Verify the security key when running the profiler.
242 456
243 457 function code_profiler_verify_key() {
244 458
245 459 $response = [
246 460 'status' => 'error',
247 - '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)'
248 463 ];
249 464
250 - $cp_options = get_option('code-profiler');
465 + if ( empty( $_REQUEST['profiler_key'] ) ) {
466 + $response['message'] = sprintf( $response['message'], '001');
251 467
252 - if ( empty( $cp_options['hash'] ) ) {
253 - $response['message'] = sprintf( $response['message'], '#1');
254 -
255 468 } else {
256 - if ( empty( $_REQUEST['profiler_key'] ) ) {
257 - $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 + }
258 480
259 - } else {
260 - if ( $cp_options['hash'] == sha1( $_REQUEST['profiler_key'] ) ) {
261 - return;
262 - } else {
263 - $response['message'] = sprintf( $response['message'], '#3');
264 - }
481 + // Delete it and accept the request
482 + unlink( $file );
483 + return;
265 484 }
485 + $response['message'] = sprintf( $response['message'], '002');
266 486 }
267 487 wp_send_json( $response );
268 -
269 488 }
270 489
271 490 // =====================================================================
272 491 // Write message to the log.
@@ -302,18 +521,37 @@
302 521 code_profiler_write2log( $string, 8, $create );
303 522 }
304 523
305 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 +// =====================================================================
306 544 // Verify if a profile exists and return its full path.
307 545
308 546 function code_profiler_get_profile_path( $id, $type = 'slugs') {
309 547
310 - $return = false;
548 + if (! empty( $id ) && preg_match('/^\d{10}\.\d+$/', $id ) ) {
311 549
312 - if (! empty( $id ) && preg_match('/^\d{10}\.\d+$/', $id ) ) {
313 - $glob = glob( CODE_PROFILER_UPLOAD_DIR ."/$id.*.$type.profile" );
550 + $glob = code_profiler_glob(CODE_PROFILER_UPLOAD_DIR, "$id\..+\.$type\.profile$", true);
551 +
314 552 if ( is_array( $glob ) && ! empty( $glob[0] ) ) {
315 - if ( preg_match( "`/$id\.(.+?).$type.profile$`", $glob[0], $match ) ) {
553 + if ( preg_match( "`$id\.(.+?).$type.profile$`", $glob[0], $match ) ) {
316 554
317 555 return CODE_PROFILER_UPLOAD_DIR. "/$id.{$match[1]}";
318 556 }
319 557 }
@@ -341,10 +579,17 @@
341 579 $buffer['error'] = $err;
342 580 return $buffer;
343 581 }
344 582
583 + /**
584 + * We don't use fgetcsv() as it requires the $escape parameter since PHP 8.4
585 + */
345 586 while (! feof( $fh ) ) {
346 - $buffer[] = fgetcsv( $fh, 1000, "\t" );
587 + $tmp = trim( fgets( $fh, 1000 ) );
588 + if (! $tmp ) {
589 + continue;
590 + }
591 + $buffer[] = explode( "\t", $tmp );
347 592 }
348 593
349 594 fclose( $fh );
350 595
@@ -399,9 +644,9 @@
399 644
400 645 $string = $open;
401 646
402 647 if ( empty( $decode->items ) ) {
403 - $tmp = 'N/A';
648 + $tmp = __('N/A', 'code-profiler');
404 649 } else {
405 650 $tmp = (int) --$decode->items;
406 651 }
407 652 $string .= sprintf(
@@ -411,9 +656,9 @@
411 656
412 657 $string .= $div;
413 658
414 659 if ( empty( $decode->time ) ) {
415 - $tmp = 'N/A';
660 + $tmp = __('N/A', 'code-profiler');
416 661 } else {
417 662 $tmp = number_format( $decode->time, 4 );
418 663 }
419 664 $string .= sprintf(
@@ -423,9 +668,9 @@
423 668
424 669 $string .= $div;
425 670
426 671 if ( empty( $decode->memory ) ) {
427 - $tmp = 'N/A';
672 + $tmp = __('N/A', 'code-profiler');
428 673 } else {
429 674 $tmp = number_format( $decode->memory, 2);
430 675 }
431 676 $string .= sprintf(
@@ -435,11 +680,11 @@
435 680
436 681 $string .= $div;
437 682
438 683 if ( empty( $decode->io ) ) {
439 - $tmp = 'N/A';
684 + $tmp = __('N/A', 'code-profiler');
440 685 } else {
441 - $tmp = number_format( $decode->io );
686 + $tmp = number_format( (int) $decode->io );
442 687 }
443 688 $string .= sprintf(
444 689 __('File I/O operations: %s', 'code-profiler'),
445 690 $tmp
@@ -447,11 +692,11 @@
447 692
448 693 $string .= $div;
449 694
450 695 if ( empty( $decode->queries ) ) {
451 - $tmp = 'N/A';
696 + $tmp = __('N/A', 'code-profiler');
452 697 } else {
453 - $tmp = number_format( $decode->queries );
698 + $tmp = number_format( (int) $decode->queries );
454 699 }
455 700 $string .= sprintf(
456 701 __('SQL queries: %s', 'code-profiler'),
457 702 $tmp
@@ -456,8 +701,20 @@
456 701 __('SQL queries: %s', 'code-profiler'),
457 702 $tmp
458 703 );
459 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 +
460 717 $string .= $close;
461 718
462 719 return $string;
463 720 }
@@ -462,29 +719,49 @@
462 719 return $string;
463 720 }
464 721
465 722 // =====================================================================
466 -// Remove *tmp files left after an HTTP error (4xx or 5xx).
723 +// Remove *tmp files left in the profiles folder.
467 724
468 725 function code_profiler_cleantmpfiles() {
469 726
470 - $glob = glob( CODE_PROFILER_UPLOAD_DIR .'/*.tmp');
727 + $glob = code_profiler_glob( CODE_PROFILER_UPLOAD_DIR, '\.tmp$', true );
728 +
471 729 if ( is_array( $glob ) ) {
472 - $count = 0;
730 +
731 + $now = time();
473 732 foreach( $glob as $file ) {
474 - $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 + */
475 750 unlink( $file );
476 751 }
477 - if ( $count ) {
478 - code_profiler_log_info( sprintf(
479 - __('Deleting %s temporary files found in the profiles folder', 'code-profiler'),
480 - (int) $count
481 - ) );
482 - }
483 752 }
484 753 }
485 754
486 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 +// =====================================================================
487 764 // Clear the log if it's bigger than 100KB.
488 765
489 766 function code_profiler_clearlog() {
490 767
@@ -492,9 +769,9 @@
492 769 file_put_contents( CODE_PROFILER_LOG, "<?php exit; ?>\n" );
493 770 }
494 771 }
495 772
496 -// =====================================================================
773 +// ===================================================================== 2023-06-15
497 774 // We don't want to be bothered by other themes/plugins' admin notices.
498 775
499 776 add_action('admin_head', 'code_profiler_hide_admin_notices');
500 777
@@ -501,8 +778,121 @@
501 778 function code_profiler_hide_admin_notices() {
502 779 if ( isset( $_GET['page'] ) && $_GET['page'] == 'code-profiler') {
503 780 remove_all_actions('admin_notices');
504 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 + );
505 895 }
506 896 }
507 897
508 898 // =====================================================================