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

704 lines 19.2 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 // Update version in the DB
158 update_option('code-profiler', $cp_options );
159 }
160
161 }
162 add_action('admin_init', 'code_profiler_init_update');
163
164 // =====================================================================
165 // Verify or create our storage folder in the uploads directory.
166 // Call during activation and access to the plugin.
167
168 function code_profiler_check_uploadsdir() {
169
170 if (! file_exists( CODE_PROFILER_UPLOAD_DIR ) ) {
171 mkdir( CODE_PROFILER_UPLOAD_DIR, 0755 );
172 }
173 if (! is_writable( CODE_PROFILER_UPLOAD_DIR ) ) {
174 // PHP running as an Apache module?
175 chmod( CODE_PROFILER_UPLOAD_DIR, 0777 );
176 }
177 if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/index.html') ) {
178 touch( CODE_PROFILER_UPLOAD_DIR .'/index.html');
179 }
180 // For Apache & Litespeed
181 if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/.htaccess') ) {
182 file_put_contents(
183 CODE_PROFILER_UPLOAD_DIR .'/.htaccess',
184 'Require all denied'
185 );
186 }
187 // Make sure there's a MU plugin directory
188 if (! is_dir( WPMU_PLUGIN_DIR ) ) {
189 wp_mkdir_p( WPMU_PLUGIN_DIR );
190 }
191
192
193 if (! file_exists( WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ) ) {
194
195 if (! is_writable( WPMU_PLUGIN_DIR ) ) {
196 wp_die(
197 sprintf(
198 __('Error: The MU folder is read only, please make it writable: %s', 'code-profiler'),
199 esc_html( WPMU_PLUGIN_DIR ) .'/'
200 )
201 );
202 }
203
204 if (! file_exists( __DIR__ .'/mu.plugin') ) {
205 code_profiler_log_error( sprintf(
206 esc_html__('Cannot find the MU plugin: %s', 'code-profiler'),
207 __DIR__ .'/mu.plugin'
208 ));
209 } else {
210 $res = copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN );
211 if ( $res === false ) {
212 code_profiler_log_error( sprintf(
213 esc_html__('Cannot copy the MU plugin to %s', 'code-profiler'),
214 WPMU_PLUGIN_DIR
215 ));
216 }
217 }
218 } else {
219 // Update MU plugin it if needed
220 if ( md5_file( WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ) !== md5_file( __DIR__ .'/mu.plugin') ) {
221 copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN );
222 }
223 }
224
225 // Log file
226 if (! file_exists( CODE_PROFILER_LOG ) ) {
227 file_put_contents( CODE_PROFILER_LOG, "<?php exit; ?>\n" );
228 }
229
230 }
231
232 // =====================================================================
233 // Check the PHP memory limit and return a suggested size
234 // for the profiler's buffer.
235
236 function code_profiler_suggested_memory() {
237
238 $memory_limit = ini_get('memory_limit');
239
240 if ('-1' == $memory_limit ) {
241 // Return max size
242 return 10;
243 }
244
245 if ( preg_match('/^(\d+)([PTGMK])$/i', $memory_limit, $match ) ) {
246 $bytes = (int) $match[1];
247 switch ( strtoupper( $match[2] ) ) {
248 case 'P':
249 $bytes *= 1024;
250 case 'T':
251 $bytes *= 1024;
252 case 'G':
253 $bytes *= 1024;
254 case 'M':
255 $bytes *= 1024;
256 case 'K':
257 $bytes *= 1024;
258 }
259 // 256 MB
260 if ( $bytes >= 268435456 ) {
261 return 10;
262 // 128 MB
263 } elseif ( $bytes >= 134217728 ) {
264 return 7;
265 // 64 MB
266 } elseif ( $bytes >= 67108864 ) {
267 return 4;
268 // <64 MB
269 } else {
270 return 1;
271 }
272 }
273 // Don't know :/
274 return 5;
275 }
276
277 // =====================================================================
278 // Create the default options.
279
280 function code_profiler_default_options() {
281
282 $buffer = code_profiler_suggested_memory();
283
284 $cp_options = [
285 'show_paths' => 'relative',
286 'display_name' => 'full',
287 'truncate_name' => 30,
288 'chart_type' => 'x',
289 'chart_max_plugins' => 25,
290 'hide_empty_value' => 1,
291 'table_max_rows' => 30,
292 'warn_composer' => 1,
293 'enable_wpcli' => 1,
294 'disable_wpcron' => 1,
295 'http_response' => '^(?:3|4|5)\d{2}$',
296 'accuracy' => 1,
297 'buffer' => (int) $buffer
298 ];
299
300 update_option('code-profiler', $cp_options );
301
302 }
303
304 // ===================================================================== 2023-06-14
305 // Create a profile name.
306
307 function code_profiler_profile_name() {
308
309 return date('Y-m-d_') . substr( time(), 4 );
310 }
311
312 // ===================================================================== 2023-06-14
313 // Disable PHP display_errors so that notice, warning and error messages
314 // don't show up in the AJAX response.
315
316 function code_profiler_hide_errors() {
317
318 ini_set('display_errors', 0 );
319 }
320
321 // =====================================================================
322 // Disable opcode cache.
323
324 function code_profiler_disable_opcode() {
325
326 try {
327 if ( extension_loaded('Zend OPcache') ) {
328 ini_set('opcache.enable', 0);
329 } elseif ( extension_loaded('wincache') ) {
330 ini_set('wincache.fcenabled', 0);
331 }
332 set_time_limit(0);
333 ini_set('memory_limit', -1);
334
335 } catch ( Exception $e ) { }
336
337 $cp_options = get_option('code-profiler');
338 if (! empty( $cp_options['disable_wpcron'] ) ) {
339 if (! defined('DISABLE_WP_CRON') ) {
340 define('DISABLE_WP_CRON', true );
341 }
342 }
343 }
344
345 // =====================================================================
346 // Verify the security key when running the profiler.
347
348 function code_profiler_verify_key() {
349
350 $response = [
351 'status' => 'error',
352 'message' => __('Security keys do not match. Reload the page and try again (%s)',
353 'code-profiler')
354 ];
355
356 if ( empty( $_REQUEST['profiler_key'] ) ) {
357 $response['message'] = sprintf( $response['message'], '001');
358
359 } else {
360 $file = CODE_PROFILER_UPLOAD_DIR .'/key_'. sha1( $_REQUEST['profiler_key'] ) .'.tmp';
361 if ( file_exists( $file ) ) {
362 // Delete it and accept the request
363 unlink( $file );
364 return;
365 }
366 $response['message'] = sprintf( $response['message'], '002');
367 }
368 wp_send_json( $response );
369
370 }
371
372 // =====================================================================
373 // Write message to the log.
374 // Log level can be a combination of INFO (1), WARN (2), ERROR (4)
375 // and DEBUG (8).
376
377 function code_profiler_write2log( $message, $level, $create ) {
378
379 if ( empty( $create ) ) {
380 file_put_contents(
381 CODE_PROFILER_LOG,
382 time() ."~~$level~~$message\n",
383 FILE_APPEND
384 );
385 } else {
386 file_put_contents(
387 CODE_PROFILER_LOG,
388 time() ."~~$level~~$message\n"
389 );
390 }
391 }
392
393 function code_profiler_log_info( $string, $create = 0 ) {
394 code_profiler_write2log( $string, 1, $create );
395 }
396 function code_profiler_log_warn( $string, $create = 0 ) {
397 code_profiler_write2log( $string, 2, $create );
398 }
399 function code_profiler_log_error( $string, $create = 0 ) {
400 code_profiler_write2log( $string, 4, $create );
401 }
402 function code_profiler_log_debug( $string, $create = 0 ) {
403 code_profiler_write2log( $string, 8, $create );
404 }
405
406 // =====================================================================
407 // Retrieve and return matching files from a directory.
408
409 function code_profiler_glob( $directory, $regex, $pathname = false ) {
410
411 $list = [];
412
413 foreach ( new DirectoryIterator( $directory ) as $finfo ) {
414 if (! $finfo->isDot() && preg_match("`$regex`", $finfo->getFilename() ) ) {
415 if ( $pathname ) {
416 $list[] = $finfo->getPathname();
417 } else {
418 $list[] = $finfo->getFilename();
419 }
420 }
421 }
422 return $list;
423 }
424
425 // =====================================================================
426 // Verify if a profile exists and return its full path.
427
428 function code_profiler_get_profile_path( $id, $type = 'slugs') {
429
430 if (! empty( $id ) && preg_match('/^\d{10}\.\d+$/', $id ) ) {
431
432 $glob = code_profiler_glob(CODE_PROFILER_UPLOAD_DIR, "$id\..+\.$type\.profile$", true);
433
434 if ( is_array( $glob ) && ! empty( $glob[0] ) ) {
435 if ( preg_match( "`$id\.(.+?).$type.profile$`", $glob[0], $match ) ) {
436
437 return CODE_PROFILER_UPLOAD_DIR. "/$id.{$match[1]}";
438 }
439 }
440 }
441 return false;
442 }
443
444 // =====================================================================
445 // Open, parse and return the content of a profile file.
446
447 function code_profiler_get_profile_data( $file, $type = 'slugs') {
448
449 $buffer = [];
450
451 $fh = fopen( "$file.$type.profile", 'rb');
452
453 if ( $fh === false ) {
454 $err = sprintf(
455 CODE_PROFILER_ERROR_NOTICE,
456 sprintf(
457 esc_html__('Unable to open profile file: %s.', 'code-profiler'),
458 esc_html( "$file.$type.profile" )
459 )
460 );
461 $buffer['error'] = $err;
462 return $buffer;
463 }
464
465 /**
466 * We don't use fgetcsv() as it requires the $escape parameter since PHP 8.4
467 */
468 while (! feof( $fh ) ) {
469 $tmp = trim( fgets( $fh, 1000 ) );
470 if (! $tmp ) {
471 continue;
472 }
473 $buffer[] = explode( "\t", $tmp );
474 }
475
476 fclose( $fh );
477
478 if ( empty( $buffer ) ) {
479 $err = sprintf(
480 CODE_PROFILER_ERROR_NOTICE,
481 sprintf(
482 esc_html__('Profile file is empty or corrupted: %s.', 'code-profiler'),
483 esc_html( "$file.$type.profile" )
484 )
485 );
486 $buffer['error'] = $err;
487 return $buffer;
488 }
489
490 // Get rid of the empty field created by fgetcsv
491 return array_filter( $buffer );
492 }
493
494 // =====================================================================
495 // Exit with a json-encoded error message except if we're running
496 // from WP CLI
497
498 function code_profiler_wp_send_json( $response ) {
499
500 if ( defined('WP_CLI') && WP_CLI ) {
501 WP_CLI::error( $response['message'] );
502 }
503 wp_send_json( $response );
504 }
505
506 // =====================================================================
507 // Retrieve the summary stats for a given profile.
508
509 function code_profiler_getsummarystats( $profile_path, $type = 'html') {
510
511 if ( $type == 'html') {
512 $open = '<ul><li>&#10148; ';
513 $div = '</li><li>&#10148; ';
514 $close = '</li></ul>';
515 } else {
516 // WP-CLI
517 $open = " \u{27A4} ";
518 $div = "\n \u{27A4} ";
519 $close = "\n\n";
520 }
521
522 if (! file_exists( "$profile_path.summary.profile" ) ) {
523 return false;
524 }
525 $decode = json_decode( file_get_contents( "$profile_path.summary.profile" ) );
526
527 $string = $open;
528
529 if ( empty( $decode->items ) ) {
530 $tmp = __('N/A', 'code-profiler');
531 } else {
532 $tmp = (int) --$decode->items;
533 }
534 $string .= sprintf(
535 __('%s plugins and 1 theme', 'code-profiler'),
536 $tmp
537 );
538
539 $string .= $div;
540
541 if ( empty( $decode->time ) ) {
542 $tmp = __('N/A', 'code-profiler');
543 } else {
544 $tmp = number_format( $decode->time, 4 );
545 }
546 $string .= sprintf(
547 __('Execution time: %ss', 'code-profiler'),
548 $tmp
549 );
550
551 $string .= $div;
552
553 if ( empty( $decode->memory ) ) {
554 $tmp = __('N/A', 'code-profiler');
555 } else {
556 $tmp = number_format( $decode->memory, 2);
557 }
558 $string .= sprintf(
559 __('Peak memory: %s MB', 'code-profiler'),
560 $tmp
561 );
562
563 $string .= $div;
564
565 if ( empty( $decode->io ) ) {
566 $tmp = __('N/A', 'code-profiler');
567 } else {
568 $tmp = number_format( (int) $decode->io );
569 }
570 $string .= sprintf(
571 __('File I/O operations: %s', 'code-profiler'),
572 $tmp
573 );
574
575 $string .= $div;
576
577 if ( empty( $decode->queries ) ) {
578 $tmp = __('N/A', 'code-profiler');
579 } else {
580 $tmp = number_format( (int) $decode->queries );
581 }
582 $string .= sprintf(
583 __('SQL queries: %s', 'code-profiler'),
584 $tmp
585 );
586
587 $string .= $div;
588
589 if ( empty( CODE_PROFILER_ACCURACY[ $decode->precision ] ) ) {
590 $tmp = __('N/A', 'code-profiler');
591 } else {
592 $tmp = CODE_PROFILER_ACCURACY[ $decode->precision ];
593 }
594 $string .= sprintf(
595 __('Accuracy: %s', 'code-profiler'),
596 $tmp
597 );
598
599 $string .= $close;
600
601 return $string;
602 }
603
604 // =====================================================================
605 // Remove *tmp files left in the profiles folder.
606
607 function code_profiler_cleantmpfiles() {
608
609 $glob = code_profiler_glob( CODE_PROFILER_UPLOAD_DIR, '\.tmp$', true );
610
611 if ( is_array( $glob ) ) {
612 foreach( $glob as $file ) {
613 unlink( $file );
614 }
615 }
616 }
617
618 // =====================================================================
619 // Remove non-ASCII characters from a string.
620
621 function code_profiler_ASCII_filter( $string ) {
622
623 return preg_replace('/[\x00-\x1f\x7f-\xff]/', '', $string );
624 }
625
626 // =====================================================================
627 // Clear the log if it's bigger than 100KB.
628
629 function code_profiler_clearlog() {
630
631 if ( file_exists( CODE_PROFILER_LOG ) && filesize( CODE_PROFILER_LOG ) > 100000 ) {
632 file_put_contents( CODE_PROFILER_LOG, "<?php exit; ?>\n" );
633 }
634 }
635
636 // ===================================================================== 2023-06-15
637 // We don't want to be bothered by other themes/plugins' admin notices.
638
639 add_action('admin_head', 'code_profiler_hide_admin_notices');
640
641 function code_profiler_hide_admin_notices() {
642 if ( isset( $_GET['page'] ) && $_GET['page'] == 'code-profiler') {
643 remove_all_actions('admin_notices');
644 remove_all_actions('all_admin_notices');
645 add_filter('admin_footer_text', 'code_profiler_admin_footer');
646 }
647 }
648
649 function code_profiler_admin_footer () {
650 echo '<span id="footer-thankyou">'.
651 sprintf(
652 /* Translators: %s are the '<a href="">' and '</a>' anchors */
653 esc_html(
654 'Thank you for using %sCode Profiler%s.',
655 'code-profiler'
656 ),
657 '<a href="https://nintechnet.com/codeprofiler/" target="_blank">',
658 '</a>'
659 ).
660 '</span>';
661 }
662
663 // =====================================================================
664 // Display/hide an element depending on some value
665
666 function code_profiler_hide( $var, $val ) {
667
668 if ( $var == $val ) {
669 echo " style='display:none'";
670 }
671 }
672
673 // =====================================================================
674 // Retrieve all themes.
675
676 function code_profiler_get_themes() {
677
678 $list = [];
679
680 // Make sure the function is loaded
681 if (! function_exists('wp_get_themes') ) {
682 require_once ABSPATH . 'wp-includes/theme.php';
683 }
684 $themes = wp_get_themes();
685 foreach( $themes as $k => $v ) {
686 if ( $v->Name ) {
687 $list[ $k ] = [
688 'n' => $v->Name,
689
690 't' => $v->Template
691 ];
692 } else {
693 $list[ $k ] = [
694 'n' => $k,
695 't' => $v->Template
696 ];
697 }
698 }
699 return $list;
700 }
701
702 // =====================================================================
703 // EOF
704