PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / trunk
Code Profiler – WordPress Performance Profiling and Debugging Made Easy vtrunk
1.9.5 1.9.4 1.9.3 trunk 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.6 1.6.1 1.6.10 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 All 40 releases
code-profiler / lib / helper.php

helper.php in Code Profiler – WordPress Performance Profiling and Debugging Made Easy trunk, at lib/helper.php

900 lines 24.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 +=====================================================================+
4 | ____ _ ____ __ _ _ |
5 | / ___|___ __| | ___ | _ \ _ __ ___ / _(_) | ___ _ __ |
6 | | | / _ \ / _` |/ _ \ | |_) | '__/ _ \| |_| | |/ _ \ '__| |
7 | | |__| (_) | (_| | __/ | __/| | | (_) | _| | | __/ | |
8 | \____\___/ \__,_|\___| |_| |_| \___/|_| |_|_|\___|_| |
9 | |
10 | (c) Jerome Bruandet ~ https://nintechnet.com/codeprofiler/ |
11 +=====================================================================+
12 */
13
14 if (! defined('ABSPATH') ) {
15 die('Forbidden');
16 }
17
18 // =====================================================================
19 // Various constants
20
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 }
33 define('CODE_PROFILER_LOG', CODE_PROFILER_UPLOAD_DIR .'/log.php');
34 define('CODE_PROFILER_TMP_IOSTATS_LOG', 'iostats.tmp');
35 define('CODE_PROFILER_TMP_SUMMARY_LOG', 'summary.tmp');
36 define('CODE_PROFILER_TMP_RERUN_LOG', 'rerun.tmp');
37 define('CODE_PROFILER_TMP_CALLS_LOG', 'calls.tmp');
38 define('CODE_PROFILER_TMP_DISKIO_LOG', 'diskio.tmp');
39 define('CODE_PROFILER_TMP_CONNECTIONS_LOG', 'connections.tmp');
40 define('CODE_PROFILER_UPDATE_NOTICE', '<div class="updated notice is-dismissible"><p>%s</p></div>');
41 define('CODE_PROFILER_ERROR_NOTICE', '<div class="error notice is-dismissible"><p>%s</p></div>');
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 }
47
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 ]);
88 }
89
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];
112 }
113
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 // =====================================================================
129 // Update version in the DB and check if options must be updated.
130
131 function code_profiler_init_update() {
132
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 }
139
140 if ( empty( $cp_options['version'] ) ) {
141 $cp_options['version'] = CODE_PROFILER_VERSION;
142 update_option('code-profiler', $cp_options );
143
144 } elseif ( version_compare( $cp_options['version'], CODE_PROFILER_VERSION, '<') ) {
145
146 // Version 1.1
147 if ( version_compare( $cp_options['version'], '1.1', '<' ) ) {
148 if (! isset( $cp_options['enable_wpcli'] ) ) {
149 $cp_options['enable_wpcli'] = 1;
150 }
151 }
152
153 // Version 1.2
154 if ( version_compare( $cp_options['version'], '1.2', '<' ) ) {
155 if (! isset( $cp_options['disable_wpcron'] ) ) {
156 $cp_options['disable_wpcron'] = 1;
157 }
158 }
159
160 // Version 1.3.1
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 }
165 }
166
167 // Version 1.5
168 if ( version_compare( $cp_options['version'], '1.5', '<' ) ) {
169 if (! isset( $cp_options['accuracy'] ) ) {
170 $cp_options['accuracy'] = 1;
171 }
172 }
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
248 // Update version in the DB
249 update_option('code-profiler', $cp_options );
250 }
251
252 }
253 add_action('admin_init', 'code_profiler_init_update');
254
255 // =====================================================================
256 // Verify or create our storage folder in the uploads directory.
257 // Call during activation and access to the plugin.
258
259 function code_profiler_check_uploadsdir() {
260
261 if (! file_exists( CODE_PROFILER_UPLOAD_DIR ) ) {
262 wp_mkdir_p( CODE_PROFILER_UPLOAD_DIR );
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 }
272 if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/index.html') ) {
273 touch( CODE_PROFILER_UPLOAD_DIR .'/index.html');
274 }
275 // For Apache & Litespeed
276 if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/.htaccess') ) {
277 file_put_contents(
278 CODE_PROFILER_UPLOAD_DIR .'/.htaccess',
279 'Require all denied'
280 );
281 }
282 // Make sure there's a MU plugin directory
283 if (! is_dir( WPMU_PLUGIN_DIR ) ) {
284 wp_mkdir_p( WPMU_PLUGIN_DIR );
285 }
286
287
288 if (! file_exists( WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ) ) {
289
290 if (! is_writable( WPMU_PLUGIN_DIR ) ) {
291 wp_die(
292 sprintf(
293 __('Error: The MU folder is read only, please make it writable: %s', 'code-profiler'),
294 esc_html( WPMU_PLUGIN_DIR ) .'/'
295 )
296 );
297 }
298
299 if (! file_exists( __DIR__ .'/mu.plugin') ) {
300 code_profiler_log_error( sprintf(
301 esc_html__('Cannot find the MU plugin: %s', 'code-profiler'),
302 __DIR__ .'/mu.plugin'
303 ));
304 } else {
305 $res = copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN );
306 if ( $res === false ) {
307 code_profiler_log_error( sprintf(
308 esc_html__('Cannot copy the MU plugin to %s', 'code-profiler'),
309 WPMU_PLUGIN_DIR
310 ));
311 }
312 }
313 } else {
314 // Update MU plugin it if needed
315 if ( md5_file( WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ) !== md5_file( __DIR__ .'/mu.plugin') ) {
316 copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN );
317 }
318 }
319
320 // Log file
321 if (! file_exists( CODE_PROFILER_LOG ) ) {
322 file_put_contents( CODE_PROFILER_LOG, "<?php exit; ?>\n" );
323 }
324
325 }
326
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 // =====================================================================
373 // Create the default options.
374
375 function code_profiler_default_options() {
376
377 $buffer = code_profiler_suggested_memory();
378
379 $cp_options = [
380 'show_paths' => 'relative',
381 'display_name' => 'full',
382 'truncate_name' => 30,
383 'chart_type' => 'x',
384 'chart_max_plugins' => 25,
385 'hide_empty_value' => 1,
386 'table_max_rows' => 30,
387 'enable_wpcli' => 1,
388 'disable_wpcron' => 1,
389 'http_response' => '^(?:3|4|5)\d{2}$',
390 'accuracy' => 1,
391 'buffer' => (int) $buffer
392 ];
393
394 update_option('code-profiler', $cp_options );
395
396 }
397
398 // ===================================================================== 2023-06-14
399 // Create a profile name.
400
401 function code_profiler_profile_name() {
402
403 return date('Y-m-d_') . substr( time(), 4 );
404 }
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 );
413 }
414
415 // =====================================================================
416 // Disable opcode cache.
417
418 function code_profiler_disable_opcode() {
419
420 try {
421 if ( extension_loaded('Zend OPcache') ) {
422 ini_set('opcache.enable', 0);
423 } elseif ( extension_loaded('wincache') ) {
424 ini_set('wincache.fcenabled', 0);
425 }
426 set_time_limit(0);
427 ini_set('memory_limit', -1);
428
429 } catch ( Exception $e ) { }
430
431 $cp_options = get_option('code-profiler');
432 /**
433 * Disable WP-CRON.
434 */
435 if (! empty( $cp_options['disable_wpcron'] ) ) {
436 if (! defined('DISABLE_WP_CRON') ) {
437 define('DISABLE_WP_CRON', true );
438 }
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 }
453
454 // =====================================================================
455 // Verify the security key when running the profiler.
456
457 function code_profiler_verify_key() {
458
459 $response = [
460 'status' => 'error',
461 // We cannot load translation here.
462 'message' => 'Security keys do not match. Reload the page and try again (%s)'
463 ];
464
465 if ( empty( $_REQUEST['profiler_key'] ) ) {
466 $response['message'] = sprintf( $response['message'], '001');
467
468 } else {
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 }
480
481 // Delete it and accept the request
482 unlink( $file );
483 return;
484 }
485 $response['message'] = sprintf( $response['message'], '002');
486 }
487 wp_send_json( $response );
488 }
489
490 // =====================================================================
491 // Write message to the log.
492 // Log level can be a combination of INFO (1), WARN (2), ERROR (4)
493 // and DEBUG (8).
494
495 function code_profiler_write2log( $message, $level, $create ) {
496
497 if ( empty( $create ) ) {
498 file_put_contents(
499 CODE_PROFILER_LOG,
500 time() ."~~$level~~$message\n",
501 FILE_APPEND
502 );
503 } else {
504 file_put_contents(
505 CODE_PROFILER_LOG,
506 time() ."~~$level~~$message\n"
507 );
508 }
509 }
510
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 }
523
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 // =====================================================================
544 // Verify if a profile exists and return its full path.
545
546 function code_profiler_get_profile_path( $id, $type = 'slugs') {
547
548 if (! empty( $id ) && preg_match('/^\d{10}\.\d+$/', $id ) ) {
549
550 $glob = code_profiler_glob(CODE_PROFILER_UPLOAD_DIR, "$id\..+\.$type\.profile$", true);
551
552 if ( is_array( $glob ) && ! empty( $glob[0] ) ) {
553 if ( preg_match( "`$id\.(.+?).$type.profile$`", $glob[0], $match ) ) {
554
555 return CODE_PROFILER_UPLOAD_DIR. "/$id.{$match[1]}";
556 }
557 }
558 }
559 return false;
560 }
561
562 // =====================================================================
563 // Open, parse and return the content of a profile file.
564
565 function code_profiler_get_profile_data( $file, $type = 'slugs') {
566
567 $buffer = [];
568
569 $fh = fopen( "$file.$type.profile", 'rb');
570
571 if ( $fh === false ) {
572 $err = sprintf(
573 CODE_PROFILER_ERROR_NOTICE,
574 sprintf(
575 esc_html__('Unable to open profile file: %s.', 'code-profiler'),
576 esc_html( "$file.$type.profile" )
577 )
578 );
579 $buffer['error'] = $err;
580 return $buffer;
581 }
582
583 /**
584 * We don't use fgetcsv() as it requires the $escape parameter since PHP 8.4
585 */
586 while (! feof( $fh ) ) {
587 $tmp = trim( fgets( $fh, 1000 ) );
588 if (! $tmp ) {
589 continue;
590 }
591 $buffer[] = explode( "\t", $tmp );
592 }
593
594 fclose( $fh );
595
596 if ( empty( $buffer ) ) {
597 $err = sprintf(
598 CODE_PROFILER_ERROR_NOTICE,
599 sprintf(
600 esc_html__('Profile file is empty or corrupted: %s.', 'code-profiler'),
601 esc_html( "$file.$type.profile" )
602 )
603 );
604 $buffer['error'] = $err;
605 return $buffer;
606 }
607
608 // Get rid of the empty field created by fgetcsv
609 return array_filter( $buffer );
610 }
611
612 // =====================================================================
613 // Exit with a json-encoded error message except if we're running
614 // from WP CLI
615
616 function code_profiler_wp_send_json( $response ) {
617
618 if ( defined('WP_CLI') && WP_CLI ) {
619 WP_CLI::error( $response['message'] );
620 }
621 wp_send_json( $response );
622 }
623
624 // =====================================================================
625 // Retrieve the summary stats for a given profile.
626
627 function code_profiler_getsummarystats( $profile_path, $type = 'html') {
628
629 if ( $type == 'html') {
630 $open = '<ul><li>&#10148; ';
631 $div = '</li><li>&#10148; ';
632 $close = '</li></ul>';
633 } else {
634 // WP-CLI
635 $open = " \u{27A4} ";
636 $div = "\n \u{27A4} ";
637 $close = "\n\n";
638 }
639
640 if (! file_exists( "$profile_path.summary.profile" ) ) {
641 return false;
642 }
643 $decode = json_decode( file_get_contents( "$profile_path.summary.profile" ) );
644
645 $string = $open;
646
647 if ( empty( $decode->items ) ) {
648 $tmp = __('N/A', 'code-profiler');
649 } else {
650 $tmp = (int) --$decode->items;
651 }
652 $string .= sprintf(
653 __('%s plugins and 1 theme', 'code-profiler'),
654 $tmp
655 );
656
657 $string .= $div;
658
659 if ( empty( $decode->time ) ) {
660 $tmp = __('N/A', 'code-profiler');
661 } else {
662 $tmp = number_format( $decode->time, 4 );
663 }
664 $string .= sprintf(
665 __('Execution time: %ss', 'code-profiler'),
666 $tmp
667 );
668
669 $string .= $div;
670
671 if ( empty( $decode->memory ) ) {
672 $tmp = __('N/A', 'code-profiler');
673 } else {
674 $tmp = number_format( $decode->memory, 2);
675 }
676 $string .= sprintf(
677 __('Peak memory: %s MB', 'code-profiler'),
678 $tmp
679 );
680
681 $string .= $div;
682
683 if ( empty( $decode->io ) ) {
684 $tmp = __('N/A', 'code-profiler');
685 } else {
686 $tmp = number_format( (int) $decode->io );
687 }
688 $string .= sprintf(
689 __('File I/O operations: %s', 'code-profiler'),
690 $tmp
691 );
692
693 $string .= $div;
694
695 if ( empty( $decode->queries ) ) {
696 $tmp = __('N/A', 'code-profiler');
697 } else {
698 $tmp = number_format( (int) $decode->queries );
699 }
700 $string .= sprintf(
701 __('SQL queries: %s', 'code-profiler'),
702 $tmp
703 );
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
717 $string .= $close;
718
719 return $string;
720 }
721
722 // =====================================================================
723 // Remove *tmp files left in the profiles folder.
724
725 function code_profiler_cleantmpfiles() {
726
727 $glob = code_profiler_glob( CODE_PROFILER_UPLOAD_DIR, '\.tmp$', true );
728
729 if ( is_array( $glob ) ) {
730
731 $now = time();
732 foreach( $glob as $file ) {
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 */
750 unlink( $file );
751 }
752 }
753 }
754
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 // =====================================================================
764 // Clear the log if it's bigger than 100KB.
765
766 function code_profiler_clearlog() {
767
768 if ( file_exists( CODE_PROFILER_LOG ) && filesize( CODE_PROFILER_LOG ) > 100000 ) {
769 file_put_contents( CODE_PROFILER_LOG, "<?php exit; ?>\n" );
770 }
771 }
772
773 // ===================================================================== 2023-06-15
774 // We don't want to be bothered by other themes/plugins' admin notices.
775
776 add_action('admin_head', 'code_profiler_hide_admin_notices');
777
778 function code_profiler_hide_admin_notices() {
779 if ( isset( $_GET['page'] ) && $_GET['page'] == 'code-profiler') {
780 remove_all_actions('admin_notices');
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 );
895 }
896 }
897
898 // =====================================================================
899 // EOF
900