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

727 lines 19.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_CALLS_LOG', 'calls.tmp');
37 define('CODE_PROFILER_TMP_DISKIO_LOG', 'diskio.tmp');
38 define('CODE_PROFILER_TMP_CONNECTIONS_LOG', 'connections.tmp');
39 define('CODE_PROFILER_UPDATE_NOTICE', '<div class="updated notice is-dismissible"><p>%s</p></div>');
40 define('CODE_PROFILER_ERROR_NOTICE', '<div class="error notice is-dismissible"><p>%s</p></div>');
41 if (! defined('CODE_PROFILER_MUPLUGIN') ) {
42 // MU plugin's name can be defined in the wp-config.php
43 define('CODE_PROFILER_MUPLUGIN', '0----code-profiler.php');
44 }
45
46 /**
47 * Since WP 6.7, translation loading must not be triggered too early.
48 */
49 add_action('init', 'code_profiler_i18n_constants');
50 function code_profiler_i18n_constants() {
51
52 global $wp_version;
53 if (! defined('CODE_PROFILER_UA') ) { // UA signatures can be user-defined in the wp-config.php
54 define ('CODE_PROFILER_UA', [
55 esc_html__('Desktop', 'code-profiler') => [
56 'Firefox' => 'Mozilla/5.0 (Linux x86_64; rv:135.0) Gecko/20100101 Firefox/135.0',
57 'Chrome' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML,'.
58 ' like Gecko) Chrome/133.0.0.0 Safari/537.36',
59 'Edge' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,'.
60 ' like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/131.0.2903.86'
61 ],
62 esc_html__('Mobile', 'code-profiler') => [
63 'Android Phone' => 'Mozilla/5.0 (Android 15; Mobile; rv:68.0) Gecko/68.0 Firefox/135.0',
64 'Android Tablet' => 'Mozilla/5.0 (Linux; Android 15.0; SAMSUNG-SM-T377A Build/NMF26X)'.
65 ' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Mobile Safari/537.36',
66 'iPhone' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_7_2 like Mac OS X) AppleWebKit/605.1.15'.
67 ' (KHTML, like Gecko) Version/17_7_2 Mobile/15E148 Safari/604.1',
68 'iPad' => 'Mozilla/5.0 (iPad; CPU OS 17_7_2 like Mac OS X) AppleWebKit/605.1.15'.
69 ' (KHTML, like Gecko) GSA/213.0.449417121 Mobile/15E148 Safari/605.1.15'
70 ],
71 esc_html__('Bot', 'code-profiler') => [
72 'Google Bot' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
73 'WordPress' => 'Mozilla/5.0 (compatible; CodeProfiler for WordPress/'. $wp_version .
74 '; https://nintechnet.com/codeprofiler/)'
75 ]
76
77 ] );
78 }
79 define('CODE_PROFILER_ACCURACY', [
80 1 => __('Highest', 'code-profiler'),
81 5 => __('High', 'code-profiler'),
82 10 => __('Moderate', 'code-profiler'),
83 15 => __('Low', 'code-profiler'),
84 20 => __('Lowest', 'code-profiler')
85 ]);
86 }
87
88 // =====================================================================
89 // Find the main site upload dir on a multisite installation.
90 // Used when running the profiler via WP CLI.
91 // This code is based on the WP private _wp_upload_dir function.
92
93 function code_profiler_upload_dir() {
94
95 $upload_path = trim( get_option('upload_path') );
96 if ( empty( $upload_path ) || 'wp-content/uploads' === $upload_path ) {
97 $basedir = WP_CONTENT_DIR . '/uploads';
98 } elseif ( strpos( $upload_path, ABSPATH ) !== 0 ) {
99 // $basedir is absolute, $upload_path is (maybe) relative to ABSPATH.
100 $basedir = path_join( ABSPATH, $upload_path );
101 } else {
102 $basedir = $upload_path;
103 }
104
105 if ( defined('UPLOADS') && ! ( is_multisite() && get_site_option('ms_files_rewriting') ) ) {
106 $basedir = ABSPATH . UPLOADS;
107 }
108
109 return ['basedir' => $basedir];
110 }
111
112 // =====================================================================
113 // Prevent cURL timeout if a plugin changes its timeout options.
114
115 function code_profiler_curl_timeout( $handle, $parsed_args, $url ) {
116
117 if ( isset( $_REQUEST['action'] ) &&
118 $_REQUEST['action'] == 'codeprofiler_start_profiler') {
119
120 curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, 300 );
121 curl_setopt( $handle, CURLOPT_TIMEOUT, 300 );
122 }
123 }
124 add_action('http_api_curl', 'code_profiler_curl_timeout', 1000, 3 );
125
126 // =====================================================================
127 // Update version in the DB and check if options must be updated.
128
129 function code_profiler_init_update() {
130
131 $cp_options = get_option('code-profiler');
132 if ( empty( $cp_options['version'] ) ||
133 version_compare( $cp_options['version'], CODE_PROFILER_VERSION, '<') ) {
134
135 $cp_options['version'] = CODE_PROFILER_VERSION;
136
137 // Version 1.1
138 if (! isset( $cp_options['enable_wpcli'] ) ) {
139 $cp_options['enable_wpcli'] = 1;
140 }
141
142 // Version 1.2
143 if (! isset( $cp_options['disable_wpcron'] ) ) {
144 $cp_options['disable_wpcron'] = 1;
145 }
146
147 // Version 1.3.1
148 if (! isset( $cp_options['http_response'] ) ) {
149 $cp_options['http_response'] = '^(?:3|4|5)\d{2}$';
150 }
151
152 // Version 1.5
153 if (! isset( $cp_options['accuracy'] ) ) {
154 $cp_options['accuracy'] = 1;
155 }
156
157 // Version 1.7.5
158 if (! isset( $cp_options['php_error'] ) ) {
159 $cp_options['php_error'] = 1;
160 }
161
162 // Update version in the DB
163 update_option('code-profiler', $cp_options );
164 }
165
166 }
167 add_action('admin_init', 'code_profiler_init_update');
168
169 // =====================================================================
170 // Verify or create our storage folder in the uploads directory.
171 // Call during activation and access to the plugin.
172
173 function code_profiler_check_uploadsdir() {
174
175 if (! file_exists( CODE_PROFILER_UPLOAD_DIR ) ) {
176 mkdir( CODE_PROFILER_UPLOAD_DIR, 0755 );
177 }
178 if (! is_writable( CODE_PROFILER_UPLOAD_DIR ) ) {
179 // PHP running as an Apache module?
180 chmod( CODE_PROFILER_UPLOAD_DIR, 0777 );
181 }
182 if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/index.html') ) {
183 touch( CODE_PROFILER_UPLOAD_DIR .'/index.html');
184 }
185 // For Apache & Litespeed
186 if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/.htaccess') ) {
187 file_put_contents(
188 CODE_PROFILER_UPLOAD_DIR .'/.htaccess',
189 'Require all denied'
190 );
191 }
192 // Make sure there's a MU plugin directory
193 if (! is_dir( WPMU_PLUGIN_DIR ) ) {
194 wp_mkdir_p( WPMU_PLUGIN_DIR );
195 }
196
197
198 if (! file_exists( WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ) ) {
199
200 if (! is_writable( WPMU_PLUGIN_DIR ) ) {
201 wp_die(
202 sprintf(
203 __('Error: The MU folder is read only, please make it writable: %s', 'code-profiler'),
204 esc_html( WPMU_PLUGIN_DIR ) .'/'
205 )
206 );
207 }
208
209 if (! file_exists( __DIR__ .'/mu.plugin') ) {
210 code_profiler_log_error( sprintf(
211 esc_html__('Cannot find the MU plugin: %s', 'code-profiler'),
212 __DIR__ .'/mu.plugin'
213 ));
214 } else {
215 $res = copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN );
216 if ( $res === false ) {
217 code_profiler_log_error( sprintf(
218 esc_html__('Cannot copy the MU plugin to %s', 'code-profiler'),
219 WPMU_PLUGIN_DIR
220 ));
221 }
222 }
223 } else {
224 // Update MU plugin it if needed
225 if ( md5_file( WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ) !== md5_file( __DIR__ .'/mu.plugin') ) {
226 copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN );
227 }
228 }
229
230 // Log file
231 if (! file_exists( CODE_PROFILER_LOG ) ) {
232 file_put_contents( CODE_PROFILER_LOG, "<?php exit; ?>\n" );
233 }
234
235 }
236
237 // =====================================================================
238 // Check the PHP memory limit and return a suggested size
239 // for the profiler's buffer.
240
241 function code_profiler_suggested_memory() {
242
243 $memory_limit = ini_get('memory_limit');
244
245 if ('-1' == $memory_limit ) {
246 // Return max size
247 return 10;
248 }
249
250 if ( preg_match('/^(\d+)([PTGMK])$/i', $memory_limit, $match ) ) {
251 $bytes = (int) $match[1];
252 switch ( strtoupper( $match[2] ) ) {
253 case 'P':
254 $bytes *= 1024;
255 case 'T':
256 $bytes *= 1024;
257 case 'G':
258 $bytes *= 1024;
259 case 'M':
260 $bytes *= 1024;
261 case 'K':
262 $bytes *= 1024;
263 }
264 // 256 MB
265 if ( $bytes >= 268435456 ) {
266 return 10;
267 // 128 MB
268 } elseif ( $bytes >= 134217728 ) {
269 return 7;
270 // 64 MB
271 } elseif ( $bytes >= 67108864 ) {
272 return 4;
273 // <64 MB
274 } else {
275 return 1;
276 }
277 }
278 // Don't know :/
279 return 5;
280 }
281
282 // =====================================================================
283 // Create the default options.
284
285 function code_profiler_default_options() {
286
287 $buffer = code_profiler_suggested_memory();
288
289 $cp_options = [
290 'show_paths' => 'relative',
291 'display_name' => 'full',
292 'truncate_name' => 30,
293 'chart_type' => 'x',
294 'chart_max_plugins' => 25,
295 'hide_empty_value' => 1,
296 'table_max_rows' => 30,
297 'warn_composer' => 1,
298 'enable_wpcli' => 1,
299 'disable_wpcron' => 1,
300 'http_response' => '^(?:3|4|5)\d{2}$',
301 'accuracy' => 1,
302 'buffer' => (int) $buffer
303 ];
304
305 update_option('code-profiler', $cp_options );
306
307 }
308
309 // ===================================================================== 2023-06-14
310 // Create a profile name.
311
312 function code_profiler_profile_name() {
313
314 return date('Y-m-d_') . substr( time(), 4 );
315 }
316
317 // ===================================================================== 2023-06-14
318 // Disable PHP display_errors so that notice, warning and error messages
319 // don't show up in the AJAX response.
320
321 function code_profiler_hide_errors() {
322
323 ini_set('display_errors', 0 );
324 }
325
326 // =====================================================================
327 // Disable opcode cache.
328
329 function code_profiler_disable_opcode() {
330
331 try {
332 if ( extension_loaded('Zend OPcache') ) {
333 ini_set('opcache.enable', 0);
334 } elseif ( extension_loaded('wincache') ) {
335 ini_set('wincache.fcenabled', 0);
336 }
337 set_time_limit(0);
338 ini_set('memory_limit', -1);
339
340 } catch ( Exception $e ) { }
341
342 $cp_options = get_option('code-profiler');
343 /**
344 * Disable WP-CRON.
345 */
346 if (! empty( $cp_options['disable_wpcron'] ) ) {
347 if (! defined('DISABLE_WP_CRON') ) {
348 define('DISABLE_WP_CRON', true );
349 }
350 }
351 /**
352 * Enable PHP error logging.
353 */
354 if (! empty( $cp_options['php_error'] ) ) {
355 ini_set('log_errors', 1 );
356 $phplog = ini_get('error_log');
357 if ( empty( $phplog ) || $phplog === false ) {
358 ini_set('error_log', WP_CONTENT_DIR .'/debug.log');
359 } else {
360 ini_set('error_log', $phplog );
361 }
362 }
363 }
364
365 // =====================================================================
366 // Verify the security key when running the profiler.
367
368 function code_profiler_verify_key() {
369
370 $response = [
371 'status' => 'error',
372 // We cannot load translation here.
373 'message' => 'Security keys do not match. Reload the page and try again (%s)'
374 ];
375
376 if ( empty( $_REQUEST['profiler_key'] ) ) {
377 $response['message'] = sprintf( $response['message'], '001');
378
379 } else {
380 $file = CODE_PROFILER_UPLOAD_DIR .'/key_'. sha1( $_REQUEST['profiler_key'] ) .'.tmp';
381 if ( file_exists( $file ) ) {
382 // Delete it and accept the request
383 unlink( $file );
384 return;
385 }
386 $response['message'] = sprintf( $response['message'], '002');
387 }
388 wp_send_json( $response );
389
390 }
391
392 // =====================================================================
393 // Write message to the log.
394 // Log level can be a combination of INFO (1), WARN (2), ERROR (4)
395 // and DEBUG (8).
396
397 function code_profiler_write2log( $message, $level, $create ) {
398
399 if ( empty( $create ) ) {
400 file_put_contents(
401 CODE_PROFILER_LOG,
402 time() ."~~$level~~$message\n",
403 FILE_APPEND
404 );
405 } else {
406 file_put_contents(
407 CODE_PROFILER_LOG,
408 time() ."~~$level~~$message\n"
409 );
410 }
411 }
412
413 function code_profiler_log_info( $string, $create = 0 ) {
414 code_profiler_write2log( $string, 1, $create );
415 }
416 function code_profiler_log_warn( $string, $create = 0 ) {
417 code_profiler_write2log( $string, 2, $create );
418 }
419 function code_profiler_log_error( $string, $create = 0 ) {
420 code_profiler_write2log( $string, 4, $create );
421 }
422 function code_profiler_log_debug( $string, $create = 0 ) {
423 code_profiler_write2log( $string, 8, $create );
424 }
425
426 // =====================================================================
427 // Retrieve and return matching files from a directory.
428
429 function code_profiler_glob( $directory, $regex, $pathname = false ) {
430
431 $list = [];
432
433 foreach ( new DirectoryIterator( $directory ) as $finfo ) {
434 if (! $finfo->isDot() && preg_match("`$regex`", $finfo->getFilename() ) ) {
435 if ( $pathname ) {
436 $list[] = $finfo->getPathname();
437 } else {
438 $list[] = $finfo->getFilename();
439 }
440 }
441 }
442 return $list;
443 }
444
445 // =====================================================================
446 // Verify if a profile exists and return its full path.
447
448 function code_profiler_get_profile_path( $id, $type = 'slugs') {
449
450 if (! empty( $id ) && preg_match('/^\d{10}\.\d+$/', $id ) ) {
451
452 $glob = code_profiler_glob(CODE_PROFILER_UPLOAD_DIR, "$id\..+\.$type\.profile$", true);
453
454 if ( is_array( $glob ) && ! empty( $glob[0] ) ) {
455 if ( preg_match( "`$id\.(.+?).$type.profile$`", $glob[0], $match ) ) {
456
457 return CODE_PROFILER_UPLOAD_DIR. "/$id.{$match[1]}";
458 }
459 }
460 }
461 return false;
462 }
463
464 // =====================================================================
465 // Open, parse and return the content of a profile file.
466
467 function code_profiler_get_profile_data( $file, $type = 'slugs') {
468
469 $buffer = [];
470
471 $fh = fopen( "$file.$type.profile", 'rb');
472
473 if ( $fh === false ) {
474 $err = sprintf(
475 CODE_PROFILER_ERROR_NOTICE,
476 sprintf(
477 esc_html__('Unable to open profile file: %s.', 'code-profiler'),
478 esc_html( "$file.$type.profile" )
479 )
480 );
481 $buffer['error'] = $err;
482 return $buffer;
483 }
484
485 /**
486 * We don't use fgetcsv() as it requires the $escape parameter since PHP 8.4
487 */
488 while (! feof( $fh ) ) {
489 $tmp = trim( fgets( $fh, 1000 ) );
490 if (! $tmp ) {
491 continue;
492 }
493 $buffer[] = explode( "\t", $tmp );
494 }
495
496 fclose( $fh );
497
498 if ( empty( $buffer ) ) {
499 $err = sprintf(
500 CODE_PROFILER_ERROR_NOTICE,
501 sprintf(
502 esc_html__('Profile file is empty or corrupted: %s.', 'code-profiler'),
503 esc_html( "$file.$type.profile" )
504 )
505 );
506 $buffer['error'] = $err;
507 return $buffer;
508 }
509
510 // Get rid of the empty field created by fgetcsv
511 return array_filter( $buffer );
512 }
513
514 // =====================================================================
515 // Exit with a json-encoded error message except if we're running
516 // from WP CLI
517
518 function code_profiler_wp_send_json( $response ) {
519
520 if ( defined('WP_CLI') && WP_CLI ) {
521 WP_CLI::error( $response['message'] );
522 }
523 wp_send_json( $response );
524 }
525
526 // =====================================================================
527 // Retrieve the summary stats for a given profile.
528
529 function code_profiler_getsummarystats( $profile_path, $type = 'html') {
530
531 if ( $type == 'html') {
532 $open = '<ul><li>&#10148; ';
533 $div = '</li><li>&#10148; ';
534 $close = '</li></ul>';
535 } else {
536 // WP-CLI
537 $open = " \u{27A4} ";
538 $div = "\n \u{27A4} ";
539 $close = "\n\n";
540 }
541
542 if (! file_exists( "$profile_path.summary.profile" ) ) {
543 return false;
544 }
545 $decode = json_decode( file_get_contents( "$profile_path.summary.profile" ) );
546
547 $string = $open;
548
549 if ( empty( $decode->items ) ) {
550 $tmp = __('N/A', 'code-profiler');
551 } else {
552 $tmp = (int) --$decode->items;
553 }
554 $string .= sprintf(
555 __('%s plugins and 1 theme', 'code-profiler'),
556 $tmp
557 );
558
559 $string .= $div;
560
561 if ( empty( $decode->time ) ) {
562 $tmp = __('N/A', 'code-profiler');
563 } else {
564 $tmp = number_format( $decode->time, 4 );
565 }
566 $string .= sprintf(
567 __('Execution time: %ss', 'code-profiler'),
568 $tmp
569 );
570
571 $string .= $div;
572
573 if ( empty( $decode->memory ) ) {
574 $tmp = __('N/A', 'code-profiler');
575 } else {
576 $tmp = number_format( $decode->memory, 2);
577 }
578 $string .= sprintf(
579 __('Peak memory: %s MB', 'code-profiler'),
580 $tmp
581 );
582
583 $string .= $div;
584
585 if ( empty( $decode->io ) ) {
586 $tmp = __('N/A', 'code-profiler');
587 } else {
588 $tmp = number_format( (int) $decode->io );
589 }
590 $string .= sprintf(
591 __('File I/O operations: %s', 'code-profiler'),
592 $tmp
593 );
594
595 $string .= $div;
596
597 if ( empty( $decode->queries ) ) {
598 $tmp = __('N/A', 'code-profiler');
599 } else {
600 $tmp = number_format( (int) $decode->queries );
601 }
602 $string .= sprintf(
603 __('SQL queries: %s', 'code-profiler'),
604 $tmp
605 );
606
607 $string .= $div;
608
609 if ( empty( CODE_PROFILER_ACCURACY[ $decode->precision ] ) ) {
610 $tmp = __('N/A', 'code-profiler');
611 } else {
612 $tmp = CODE_PROFILER_ACCURACY[ $decode->precision ];
613 }
614 $string .= sprintf(
615 __('Accuracy: %s', 'code-profiler'),
616 $tmp
617 );
618
619 $string .= $close;
620
621 return $string;
622 }
623
624 // =====================================================================
625 // Remove *tmp files left in the profiles folder.
626
627 function code_profiler_cleantmpfiles() {
628
629 $glob = code_profiler_glob( CODE_PROFILER_UPLOAD_DIR, '\.tmp$', true );
630
631 if ( is_array( $glob ) ) {
632 foreach( $glob as $file ) {
633 unlink( $file );
634 }
635 }
636 }
637
638 // =====================================================================
639 // Remove non-ASCII characters from a string.
640
641 function code_profiler_ASCII_filter( $string ) {
642
643 return preg_replace('/[\x00-\x1f\x7f-\xff]/', '', $string );
644 }
645
646 // =====================================================================
647 // Clear the log if it's bigger than 100KB.
648
649 function code_profiler_clearlog() {
650
651 if ( file_exists( CODE_PROFILER_LOG ) && filesize( CODE_PROFILER_LOG ) > 100000 ) {
652 file_put_contents( CODE_PROFILER_LOG, "<?php exit; ?>\n" );
653 }
654 }
655
656 // ===================================================================== 2023-06-15
657 // We don't want to be bothered by other themes/plugins' admin notices.
658
659 add_action('admin_head', 'code_profiler_hide_admin_notices');
660
661 function code_profiler_hide_admin_notices() {
662 if ( isset( $_GET['page'] ) && $_GET['page'] == 'code-profiler') {
663 remove_all_actions('admin_notices');
664 remove_all_actions('all_admin_notices');
665 add_filter('admin_footer_text', 'code_profiler_admin_footer');
666 }
667 }
668
669 function code_profiler_admin_footer () {
670 echo '<span id="footer-thankyou">'.
671 sprintf(
672 /* Translators: %s are the '<a href="">' and '</a>' anchors */
673 esc_html(
674 'Thank you for using %sCode Profiler%s.',
675 'code-profiler'
676 ),
677 '<a href="https://nintechnet.com/codeprofiler/" target="_blank">',
678 '</a>'
679 ).
680 '</span>';
681 }
682
683
684 // =====================================================================
685 // Hide/display an element depending on some value.
686
687 function code_profiler_showhide( $var, $val ) {
688
689 if ( $var == $val ) {
690 echo " style='display:block'";
691 } else {
692 echo " style='display:none'";
693 }
694 }
695
696 // =====================================================================
697 // Retrieve all themes.
698
699 function code_profiler_get_themes() {
700
701 $list = [];
702
703 // Make sure the function is loaded
704 if (! function_exists('wp_get_themes') ) {
705 require_once ABSPATH . 'wp-includes/theme.php';
706 }
707 $themes = wp_get_themes();
708 foreach( $themes as $k => $v ) {
709 if ( $v->Name ) {
710 $list[ $k ] = [
711 'n' => $v->Name,
712
713 't' => $v->Template
714 ];
715 } else {
716 $list[ $k ] = [
717 'n' => $k,
718 't' => $v->Template
719 ];
720 }
721 }
722 return $list;
723 }
724
725 // =====================================================================
726 // EOF
727