PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.9.2
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.9.2
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 1.9.2, at lib/helper.php

868 lines 23.8 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 mkdir( CODE_PROFILER_UPLOAD_DIR, 0755 );
263 }
264 if (! is_writable( CODE_PROFILER_UPLOAD_DIR ) ) {
265 // PHP running as an Apache module?
266 chmod( CODE_PROFILER_UPLOAD_DIR, 0777 );
267 }
268 if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/index.html') ) {
269 touch( CODE_PROFILER_UPLOAD_DIR .'/index.html');
270 }
271 // For Apache & Litespeed
272 if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/.htaccess') ) {
273 file_put_contents(
274 CODE_PROFILER_UPLOAD_DIR .'/.htaccess',
275 'Require all denied'
276 );
277 }
278 // Make sure there's a MU plugin directory
279 if (! is_dir( WPMU_PLUGIN_DIR ) ) {
280 wp_mkdir_p( WPMU_PLUGIN_DIR );
281 }
282
283
284 if (! file_exists( WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ) ) {
285
286 if (! is_writable( WPMU_PLUGIN_DIR ) ) {
287 wp_die(
288 sprintf(
289 __('Error: The MU folder is read only, please make it writable: %s', 'code-profiler'),
290 esc_html( WPMU_PLUGIN_DIR ) .'/'
291 )
292 );
293 }
294
295 if (! file_exists( __DIR__ .'/mu.plugin') ) {
296 code_profiler_log_error( sprintf(
297 esc_html__('Cannot find the MU plugin: %s', 'code-profiler'),
298 __DIR__ .'/mu.plugin'
299 ));
300 } else {
301 $res = copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN );
302 if ( $res === false ) {
303 code_profiler_log_error( sprintf(
304 esc_html__('Cannot copy the MU plugin to %s', 'code-profiler'),
305 WPMU_PLUGIN_DIR
306 ));
307 }
308 }
309 } else {
310 // Update MU plugin it if needed
311 if ( md5_file( WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ) !== md5_file( __DIR__ .'/mu.plugin') ) {
312 copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN );
313 }
314 }
315
316 // Log file
317 if (! file_exists( CODE_PROFILER_LOG ) ) {
318 file_put_contents( CODE_PROFILER_LOG, "<?php exit; ?>\n" );
319 }
320
321 }
322
323 // =====================================================================
324 // Check the PHP memory limit and return a suggested size
325 // for the profiler's buffer.
326
327 function code_profiler_suggested_memory() {
328
329 $memory_limit = ini_get('memory_limit');
330
331 if ('-1' == $memory_limit ) {
332 // Return max size
333 return 10;
334 }
335
336 if ( preg_match('/^(\d+)([PTGMK])$/i', $memory_limit, $match ) ) {
337 $bytes = (int) $match[1];
338 switch ( strtoupper( $match[2] ) ) {
339 case 'P':
340 $bytes *= 1024;
341 case 'T':
342 $bytes *= 1024;
343 case 'G':
344 $bytes *= 1024;
345 case 'M':
346 $bytes *= 1024;
347 case 'K':
348 $bytes *= 1024;
349 }
350 // 256 MB
351 if ( $bytes >= 268435456 ) {
352 return 10;
353 // 128 MB
354 } elseif ( $bytes >= 134217728 ) {
355 return 7;
356 // 64 MB
357 } elseif ( $bytes >= 67108864 ) {
358 return 4;
359 // <64 MB
360 } else {
361 return 1;
362 }
363 }
364 // Don't know :/
365 return 5;
366 }
367
368 // =====================================================================
369 // Create the default options.
370
371 function code_profiler_default_options() {
372
373 $buffer = code_profiler_suggested_memory();
374
375 $cp_options = [
376 'show_paths' => 'relative',
377 'display_name' => 'full',
378 'truncate_name' => 30,
379 'chart_type' => 'x',
380 'chart_max_plugins' => 25,
381 'hide_empty_value' => 1,
382 'table_max_rows' => 30,
383 'enable_wpcli' => 1,
384 'disable_wpcron' => 1,
385 'http_response' => '^(?:3|4|5)\d{2}$',
386 'accuracy' => 1,
387 'buffer' => (int) $buffer
388 ];
389
390 update_option('code-profiler', $cp_options );
391
392 }
393
394 // ===================================================================== 2023-06-14
395 // Create a profile name.
396
397 function code_profiler_profile_name() {
398
399 return date('Y-m-d_') . substr( time(), 4 );
400 }
401
402 // ===================================================================== 2023-06-14
403 // Disable PHP display_errors so that notice, warning and error messages
404 // don't show up in the AJAX response.
405
406 function code_profiler_hide_errors() {
407
408 ini_set('display_errors', 0 );
409 }
410
411 // =====================================================================
412 // Disable opcode cache.
413
414 function code_profiler_disable_opcode() {
415
416 try {
417 if ( extension_loaded('Zend OPcache') ) {
418 ini_set('opcache.enable', 0);
419 } elseif ( extension_loaded('wincache') ) {
420 ini_set('wincache.fcenabled', 0);
421 }
422 set_time_limit(0);
423 ini_set('memory_limit', -1);
424
425 } catch ( Exception $e ) { }
426
427 $cp_options = get_option('code-profiler');
428 /**
429 * Disable WP-CRON.
430 */
431 if (! empty( $cp_options['disable_wpcron'] ) ) {
432 if (! defined('DISABLE_WP_CRON') ) {
433 define('DISABLE_WP_CRON', true );
434 }
435 }
436 /**
437 * Enable PHP error logging.
438 */
439 if (! empty( $cp_options['php_error'] ) ) {
440 ini_set('log_errors', 1 );
441 $phplog = ini_get('error_log');
442 if ( empty( $phplog ) || $phplog === false ) {
443 ini_set('error_log', WP_CONTENT_DIR .'/debug.log');
444 } else {
445 ini_set('error_log', $phplog );
446 }
447 }
448 }
449
450 // =====================================================================
451 // Verify the security key when running the profiler.
452
453 function code_profiler_verify_key() {
454
455 $response = [
456 'status' => 'error',
457 // We cannot load translation here.
458 'message' => 'Security keys do not match. Reload the page and try again (%s)'
459 ];
460
461 if ( empty( $_REQUEST['profiler_key'] ) ) {
462 $response['message'] = sprintf( $response['message'], '001');
463
464 } else {
465 $file = CODE_PROFILER_UPLOAD_DIR .'/key_'. sha1( $_REQUEST['profiler_key'] ) .'.tmp';
466 if ( file_exists( $file ) ) {
467 // Delete it and accept the request
468 unlink( $file );
469 return;
470 }
471 $response['message'] = sprintf( $response['message'], '002');
472 }
473 wp_send_json( $response );
474
475 }
476
477 // =====================================================================
478 // Write message to the log.
479 // Log level can be a combination of INFO (1), WARN (2), ERROR (4)
480 // and DEBUG (8).
481
482 function code_profiler_write2log( $message, $level, $create ) {
483
484 if ( empty( $create ) ) {
485 file_put_contents(
486 CODE_PROFILER_LOG,
487 time() ."~~$level~~$message\n",
488 FILE_APPEND
489 );
490 } else {
491 file_put_contents(
492 CODE_PROFILER_LOG,
493 time() ."~~$level~~$message\n"
494 );
495 }
496 }
497
498 function code_profiler_log_info( $string, $create = 0 ) {
499 code_profiler_write2log( $string, 1, $create );
500 }
501 function code_profiler_log_warn( $string, $create = 0 ) {
502 code_profiler_write2log( $string, 2, $create );
503 }
504 function code_profiler_log_error( $string, $create = 0 ) {
505 code_profiler_write2log( $string, 4, $create );
506 }
507 function code_profiler_log_debug( $string, $create = 0 ) {
508 code_profiler_write2log( $string, 8, $create );
509 }
510
511 // =====================================================================
512 // Retrieve and return matching files from a directory.
513
514 function code_profiler_glob( $directory, $regex, $pathname = false ) {
515
516 $list = [];
517
518 foreach ( new DirectoryIterator( $directory ) as $finfo ) {
519 if (! $finfo->isDot() && preg_match("`$regex`", $finfo->getFilename() ) ) {
520 if ( $pathname ) {
521 $list[] = $finfo->getPathname();
522 } else {
523 $list[] = $finfo->getFilename();
524 }
525 }
526 }
527 return $list;
528 }
529
530 // =====================================================================
531 // Verify if a profile exists and return its full path.
532
533 function code_profiler_get_profile_path( $id, $type = 'slugs') {
534
535 if (! empty( $id ) && preg_match('/^\d{10}\.\d+$/', $id ) ) {
536
537 $glob = code_profiler_glob(CODE_PROFILER_UPLOAD_DIR, "$id\..+\.$type\.profile$", true);
538
539 if ( is_array( $glob ) && ! empty( $glob[0] ) ) {
540 if ( preg_match( "`$id\.(.+?).$type.profile$`", $glob[0], $match ) ) {
541
542 return CODE_PROFILER_UPLOAD_DIR. "/$id.{$match[1]}";
543 }
544 }
545 }
546 return false;
547 }
548
549 // =====================================================================
550 // Open, parse and return the content of a profile file.
551
552 function code_profiler_get_profile_data( $file, $type = 'slugs') {
553
554 $buffer = [];
555
556 $fh = fopen( "$file.$type.profile", 'rb');
557
558 if ( $fh === false ) {
559 $err = sprintf(
560 CODE_PROFILER_ERROR_NOTICE,
561 sprintf(
562 esc_html__('Unable to open profile file: %s.', 'code-profiler'),
563 esc_html( "$file.$type.profile" )
564 )
565 );
566 $buffer['error'] = $err;
567 return $buffer;
568 }
569
570 /**
571 * We don't use fgetcsv() as it requires the $escape parameter since PHP 8.4
572 */
573 while (! feof( $fh ) ) {
574 $tmp = trim( fgets( $fh, 1000 ) );
575 if (! $tmp ) {
576 continue;
577 }
578 $buffer[] = explode( "\t", $tmp );
579 }
580
581 fclose( $fh );
582
583 if ( empty( $buffer ) ) {
584 $err = sprintf(
585 CODE_PROFILER_ERROR_NOTICE,
586 sprintf(
587 esc_html__('Profile file is empty or corrupted: %s.', 'code-profiler'),
588 esc_html( "$file.$type.profile" )
589 )
590 );
591 $buffer['error'] = $err;
592 return $buffer;
593 }
594
595 // Get rid of the empty field created by fgetcsv
596 return array_filter( $buffer );
597 }
598
599 // =====================================================================
600 // Exit with a json-encoded error message except if we're running
601 // from WP CLI
602
603 function code_profiler_wp_send_json( $response ) {
604
605 if ( defined('WP_CLI') && WP_CLI ) {
606 WP_CLI::error( $response['message'] );
607 }
608 wp_send_json( $response );
609 }
610
611 // =====================================================================
612 // Retrieve the summary stats for a given profile.
613
614 function code_profiler_getsummarystats( $profile_path, $type = 'html') {
615
616 if ( $type == 'html') {
617 $open = '<ul><li>&#10148; ';
618 $div = '</li><li>&#10148; ';
619 $close = '</li></ul>';
620 } else {
621 // WP-CLI
622 $open = " \u{27A4} ";
623 $div = "\n \u{27A4} ";
624 $close = "\n\n";
625 }
626
627 if (! file_exists( "$profile_path.summary.profile" ) ) {
628 return false;
629 }
630 $decode = json_decode( file_get_contents( "$profile_path.summary.profile" ) );
631
632 $string = $open;
633
634 if ( empty( $decode->items ) ) {
635 $tmp = __('N/A', 'code-profiler');
636 } else {
637 $tmp = (int) --$decode->items;
638 }
639 $string .= sprintf(
640 __('%s plugins and 1 theme', 'code-profiler'),
641 $tmp
642 );
643
644 $string .= $div;
645
646 if ( empty( $decode->time ) ) {
647 $tmp = __('N/A', 'code-profiler');
648 } else {
649 $tmp = number_format( $decode->time, 4 );
650 }
651 $string .= sprintf(
652 __('Execution time: %ss', 'code-profiler'),
653 $tmp
654 );
655
656 $string .= $div;
657
658 if ( empty( $decode->memory ) ) {
659 $tmp = __('N/A', 'code-profiler');
660 } else {
661 $tmp = number_format( $decode->memory, 2);
662 }
663 $string .= sprintf(
664 __('Peak memory: %s MB', 'code-profiler'),
665 $tmp
666 );
667
668 $string .= $div;
669
670 if ( empty( $decode->io ) ) {
671 $tmp = __('N/A', 'code-profiler');
672 } else {
673 $tmp = number_format( (int) $decode->io );
674 }
675 $string .= sprintf(
676 __('File I/O operations: %s', 'code-profiler'),
677 $tmp
678 );
679
680 $string .= $div;
681
682 if ( empty( $decode->queries ) ) {
683 $tmp = __('N/A', 'code-profiler');
684 } else {
685 $tmp = number_format( (int) $decode->queries );
686 }
687 $string .= sprintf(
688 __('SQL queries: %s', 'code-profiler'),
689 $tmp
690 );
691
692 $string .= $div;
693
694 if ( empty( CODE_PROFILER_ACCURACY[ $decode->precision ] ) ) {
695 $tmp = __('N/A', 'code-profiler');
696 } else {
697 $tmp = CODE_PROFILER_ACCURACY[ $decode->precision ];
698 }
699 $string .= sprintf(
700 __('Accuracy: %s', 'code-profiler'),
701 $tmp
702 );
703
704 $string .= $close;
705
706 return $string;
707 }
708
709 // =====================================================================
710 // Remove *tmp files left in the profiles folder.
711
712 function code_profiler_cleantmpfiles() {
713
714 $glob = code_profiler_glob( CODE_PROFILER_UPLOAD_DIR, '\.tmp$', true );
715
716 if ( is_array( $glob ) ) {
717 foreach( $glob as $file ) {
718 unlink( $file );
719 }
720 }
721 }
722
723 // =====================================================================
724 // Remove non-ASCII characters from a string.
725
726 function code_profiler_ASCII_filter( $string ) {
727
728 return preg_replace('/[\x00-\x1f\x7f-\xff]/', '', $string );
729 }
730
731 // =====================================================================
732 // Clear the log if it's bigger than 100KB.
733
734 function code_profiler_clearlog() {
735
736 if ( file_exists( CODE_PROFILER_LOG ) && filesize( CODE_PROFILER_LOG ) > 100000 ) {
737 file_put_contents( CODE_PROFILER_LOG, "<?php exit; ?>\n" );
738 }
739 }
740
741 // ===================================================================== 2023-06-15
742 // We don't want to be bothered by other themes/plugins' admin notices.
743
744 add_action('admin_head', 'code_profiler_hide_admin_notices');
745
746 function code_profiler_hide_admin_notices() {
747 if ( isset( $_GET['page'] ) && $_GET['page'] == 'code-profiler') {
748 remove_all_actions('admin_notices');
749 remove_all_actions('all_admin_notices');
750 add_filter('admin_footer_text', 'code_profiler_admin_footer');
751 }
752 }
753
754 function code_profiler_admin_footer () {
755 echo '<span id="footer-thankyou">'.
756 sprintf(
757 /* Translators: %s are the '<a href="">' and '</a>' anchors */
758 esc_html(
759 'Thank you for using %sCode Profiler%s.',
760 'code-profiler'
761 ),
762 '<a href="https://nintechnet.com/codeprofiler/" target="_blank">',
763 '</a>'
764 ).
765 '</span>';
766 }
767
768
769 // =====================================================================
770 // Hide/display an element depending on some value.
771
772 function code_profiler_showhide( $var, $val ) {
773
774 if ( $var == $val ) {
775 echo " style='display:block'";
776 } else {
777 echo " style='display:none'";
778 }
779 }
780
781 // =====================================================================
782 // Retrieve all themes.
783
784 function code_profiler_get_themes() {
785
786 $list = [];
787
788 // Make sure the function is loaded
789 if (! function_exists('wp_get_themes') ) {
790 require_once ABSPATH . 'wp-includes/theme.php';
791 }
792 $themes = wp_get_themes();
793 foreach( $themes as $k => $v ) {
794 if ( $v->Name ) {
795 $list[ $k ] = [
796 'n' => $v->Name,
797
798 't' => $v->Template
799 ];
800 } else {
801 $list[ $k ] = [
802 'n' => $k,
803 't' => $v->Template
804 ];
805 }
806 }
807 return $list;
808 }
809
810 // =====================================================================
811 // Retrieve all available cron events.
812
813 function code_profiler_get_crons() {
814
815 $list = [];
816
817 $wp_crons = _get_cron_array();
818
819 if ( empty( $wp_crons ) ) {
820 return $list;
821 }
822
823 foreach ( $wp_crons as $timestamp => $cronhooks ) {
824 foreach ( $cronhooks as $hook => $keys ) {
825 $list[] = $hook;
826 }
827 }
828 sort( $list );
829 return $list;
830 }
831
832 // =====================================================================
833 // Create a file with the ABSPATH.
834
835 function code_profiler_create_tmpfile() {
836
837 $tmp_folder = dirname( __DIR__ ) .'/tmp';
838 $tmp_file = "$tmp_folder/profiler.inc.php";
839
840 if (! is_dir( $tmp_folder ) ) {
841 $res = @ mkdir( $tmp_folder );
842 if ( false === $res ) {
843 return sprintf(
844 __('Cannot create temporary folder: [%s]. Any attempt to profile a cron event will likely fail.', 'code-profiler'),
845 $tmp_folder
846 );
847 }
848 }
849
850 if (! is_file( "$tmp_folder/index.html" ) ) {
851 touch( "$tmp_folder/index.html" );
852 }
853
854 $res = @ file_put_contents(
855 $tmp_file,
856 "<?php\nconst ABSPATH = '". ABSPATH ."';\n"
857 );
858 if ( false === $res ) {
859 return sprintf(
860 __('Cannot create temporary file: [%s]. Any attempt to profile a cron event will likely fail.', 'code-profiler'),
861 $tmp_file
862 );
863 }
864 }
865
866 // =====================================================================
867 // EOF
868