%s

'); define('CODE_PROFILER_ERROR_NOTICE', '

%s

'); define('CODE_PROFILER_WARNING_NOTICE', '

%s

'); if (! defined('CODE_PROFILER_MUPLUGIN') ) { // MU plugin's name can be defined in the wp-config.php define('CODE_PROFILER_MUPLUGIN', '0----code-profiler.php'); } /** * Since WP 6.7, translation loading must not be triggered too early. */ add_action('init', 'code_profiler_i18n_constants'); function code_profiler_i18n_constants() { global $wp_version; if (! defined('CODE_PROFILER_UA') ) { // UA signatures can be user-defined in the wp-config.php define ('CODE_PROFILER_UA', [ esc_html__('Desktop', 'code-profiler') => [ 'Firefox' => 'Mozilla/5.0 (Linux x86_64; rv:149.0) Gecko/20100101 Firefox/149.0', 'Chrome' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML,'. ' like Gecko) Chrome/147.0.0.0 Safari/537.36', 'Edge' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,'. ' like Gecko) Chrome/147.0.0.0 Safari/537.36 Edg/Chrome/147.0.7727.50' ], esc_html__('Mobile', 'code-profiler') => [ 'Android Phone' => 'Mozilla/5.0 (Android 16; Mobile; rv:68.0) Gecko/68.0 Firefox/149.0', 'Android Tablet' => 'Mozilla/5.0 (Linux; Android 16.0; SAMSUNG-SM-T377A Build/NMF26X)'. ' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.7727.50 Mobile Safari/537.36', 'iPhone' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7_7 like Mac OS X) AppleWebKit/605.1.15'. ' (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1', 'iPad' => 'Mozilla/5.0 (iPad; CPU OS 18_7_7 like Mac OS X) AppleWebKit/605.1.15'. ' (KHTML, like Gecko) GSA/213.0.449417121 Mobile/15E148 Safari/605.1.15' ], esc_html__('Bot', 'code-profiler') => [ 'Google Bot' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)', 'WordPress' => 'Mozilla/5.0 (compatible; CodeProfiler for WordPress/'. $wp_version . '; https://nintechnet.com/codeprofiler/)' ] ] ); } define('CODE_PROFILER_ACCURACY', [ 1 => __('Highest', 'code-profiler'), 5 => __('High', 'code-profiler'), 10 => __('Moderate', 'code-profiler'), 15 => __('Low', 'code-profiler'), 20 => __('Lowest', 'code-profiler') ]); } // ===================================================================== // Find the main site upload dir on a multisite installation. // Used when running the profiler via WP CLI. // This code is based on the WP private _wp_upload_dir function. function code_profiler_upload_dir() { $upload_path = trim( get_option('upload_path') ); if ( empty( $upload_path ) || 'wp-content/uploads' === $upload_path ) { $basedir = WP_CONTENT_DIR . '/uploads'; } elseif ( strpos( $upload_path, ABSPATH ) !== 0 ) { // $basedir is absolute, $upload_path is (maybe) relative to ABSPATH. $basedir = path_join( ABSPATH, $upload_path ); } else { $basedir = $upload_path; } if ( defined('UPLOADS') && ! ( is_multisite() && get_site_option('ms_files_rewriting') ) ) { $basedir = ABSPATH . UPLOADS; } return ['basedir' => $basedir]; } // ===================================================================== // Prevent cURL timeout if a plugin changes its timeout options. function code_profiler_curl_timeout( $handle, $parsed_args, $url ) { if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_start_profiler') { curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, 300 ); curl_setopt( $handle, CURLOPT_TIMEOUT, 300 ); } } add_action('http_api_curl', 'code_profiler_curl_timeout', 1000, 3 ); // ===================================================================== // Update version in the DB and check if options must be updated. function code_profiler_init_update() { if ( ( $cp_options = get_option('code-profiler') ) == false ) { /** * "Automatic conversion of false to array is deprecated" since PHP 8.1 */ $cp_options = []; } if ( empty( $cp_options['version'] ) ) { $cp_options['version'] = CODE_PROFILER_VERSION; update_option('code-profiler', $cp_options ); } elseif ( version_compare( $cp_options['version'], CODE_PROFILER_VERSION, '<') ) { // Version 1.1 if ( version_compare( $cp_options['version'], '1.1', '<' ) ) { if (! isset( $cp_options['enable_wpcli'] ) ) { $cp_options['enable_wpcli'] = 1; } } // Version 1.2 if ( version_compare( $cp_options['version'], '1.2', '<' ) ) { if (! isset( $cp_options['disable_wpcron'] ) ) { $cp_options['disable_wpcron'] = 1; } } // Version 1.3.1 if ( version_compare( $cp_options['version'], '1.3.1', '<' ) ) { if (! isset( $cp_options['http_response'] ) ) { $cp_options['http_response'] = '^(?:3|4|5)\d{2}$'; } } // Version 1.5 if ( version_compare( $cp_options['version'], '1.5', '<' ) ) { if (! isset( $cp_options['accuracy'] ) ) { $cp_options['accuracy'] = 1; } } // Version 1.7.5 if ( version_compare( $cp_options['version'], '1.7.5', '<' ) ) { if (! isset( $cp_options['php_error'] ) ) { $cp_options['php_error'] = 1; } } // Version 1.8 if ( version_compare( $cp_options['version'], '1.8', '<' ) ) { if ( isset( $cp_options['mem_where'] ) ) { $cp_options['mem']['x_end'] = $cp_options['mem_where']; unset( $cp_options['mem_where'] ); } if ( isset( $cp_options['mem_post'] ) ) { $cp_options['mem']['post'] = $cp_options['mem_post']; unset( $cp_options['mem_post'] ); } if ( isset( $cp_options['mem_user'] ) ) { $cp_options['mem']['x_auth'] = $cp_options['mem_user']; unset( $cp_options['mem_user'] ); } if ( isset( $cp_options['mem_username'] ) ) { $cp_options['mem']['username'] = $cp_options['mem_username']; unset( $cp_options['mem_username'] ); } if ( isset( $cp_options['mem_method'] ) ) { $cp_options['mem']['method'] = $cp_options['mem_method']; unset( $cp_options['mem_method'] ); } if ( isset( $cp_options['mem_theme'] ) ) { $cp_options['mem']['theme'] = $cp_options['mem_theme']; unset( $cp_options['mem_theme'] ); } if ( isset( $cp_options['ua'] ) ) { $cp_options['mem']['user_agent'] = $cp_options['ua']; unset( $cp_options['ua'] ); } if ( isset( $cp_options['cookies'] ) ) { $cp_options['mem']['cookies'] = $cp_options['cookies']; unset( $cp_options['cookies'] ); } if ( isset( $cp_options['mem_content_type'] ) ) { $cp_options['mem']['content_type'] = $cp_options['mem_content_type']; unset( $cp_options['mem_content_type'] ); } if ( isset( $cp_options['payload'] ) ) { $cp_options['mem']['payload'] = $cp_options['payload']; unset( $cp_options['payload'] ); } if ( isset( $cp_options['custom_headers'] ) ) { $cp_options['mem']['custom_headers'] = $cp_options['custom_headers']; unset( $cp_options['custom_headers'] ); } if ( isset( $cp_options['exclusions'] ) ) { $cp_options['mem']['exclusions'] = $cp_options['exclusions']; unset( $cp_options['exclusions'] ); } } // Version 1.8.1 if ( version_compare( $cp_options['version'], '1.8.1', '<' ) ) { CodeProfiler_WPCron::install(); } /** * Version 1.9.2 */ if ( version_compare( $cp_options['version'], '1.9.2', '<' ) ) { unset( $cp_options['warn_composer'] ); } // Adjust current version $cp_options['version'] = CODE_PROFILER_VERSION; // Update version in the DB update_option('code-profiler', $cp_options ); } } add_action('admin_init', 'code_profiler_init_update'); // ===================================================================== // Verify or create our storage folder in the uploads directory. // Call during activation and access to the plugin. function code_profiler_check_uploadsdir() { if (! file_exists( CODE_PROFILER_UPLOAD_DIR ) ) { wp_mkdir_p( CODE_PROFILER_UPLOAD_DIR ); } if (! is_writable( CODE_PROFILER_UPLOAD_DIR ) || ! is_dir( CODE_PROFILER_UPLOAD_DIR ) ) { wp_die( sprintf( __('Error: The %s folder cannot be created or is read only', 'code-profiler'), esc_html( CODE_PROFILER_UPLOAD_DIR ) .'/' ) ); } if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/index.html') ) { touch( CODE_PROFILER_UPLOAD_DIR .'/index.html'); } // For Apache & Litespeed if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/.htaccess') ) { file_put_contents( CODE_PROFILER_UPLOAD_DIR .'/.htaccess', 'Require all denied' ); } // Make sure there's a MU plugin directory if (! is_dir( WPMU_PLUGIN_DIR ) ) { wp_mkdir_p( WPMU_PLUGIN_DIR ); } if (! file_exists( WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ) ) { if (! is_writable( WPMU_PLUGIN_DIR ) ) { wp_die( sprintf( __('Error: The MU folder is read only, please make it writable: %s', 'code-profiler'), esc_html( WPMU_PLUGIN_DIR ) .'/' ) ); } if (! file_exists( __DIR__ .'/mu.plugin') ) { code_profiler_log_error( sprintf( esc_html__('Cannot find the MU plugin: %s', 'code-profiler'), __DIR__ .'/mu.plugin' )); } else { $res = copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ); if ( $res === false ) { code_profiler_log_error( sprintf( esc_html__('Cannot copy the MU plugin to %s', 'code-profiler'), WPMU_PLUGIN_DIR )); } } } else { // Update MU plugin it if needed if ( md5_file( WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ) !== md5_file( __DIR__ .'/mu.plugin') ) { copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ); } } // Log file if (! file_exists( CODE_PROFILER_LOG ) ) { file_put_contents( CODE_PROFILER_LOG, "\n" ); } } // ===================================================================== // Check the PHP memory limit and return a suggested size // for the profiler's buffer. function code_profiler_suggested_memory() { $memory_limit = ini_get('memory_limit'); if ('-1' == $memory_limit ) { // Return max size return 10; } if ( preg_match('/^(\d+)([PTGMK])$/i', $memory_limit, $match ) ) { $bytes = (int) $match[1]; switch ( strtoupper( $match[2] ) ) { case 'P': $bytes *= 1024; case 'T': $bytes *= 1024; case 'G': $bytes *= 1024; case 'M': $bytes *= 1024; case 'K': $bytes *= 1024; } // 256 MB if ( $bytes >= 268435456 ) { return 10; // 128 MB } elseif ( $bytes >= 134217728 ) { return 7; // 64 MB } elseif ( $bytes >= 67108864 ) { return 4; // <64 MB } else { return 1; } } // Don't know :/ return 5; } // ===================================================================== // Create the default options. function code_profiler_default_options() { $buffer = code_profiler_suggested_memory(); $cp_options = [ 'show_paths' => 'relative', 'display_name' => 'full', 'truncate_name' => 30, 'chart_type' => 'x', 'chart_max_plugins' => 25, 'hide_empty_value' => 1, 'table_max_rows' => 30, 'enable_wpcli' => 1, 'disable_wpcron' => 1, 'http_response' => '^(?:3|4|5)\d{2}$', 'accuracy' => 1, 'buffer' => (int) $buffer ]; update_option('code-profiler', $cp_options ); } // ===================================================================== 2023-06-14 // Create a profile name. function code_profiler_profile_name() { return date('Y-m-d_') . substr( time(), 4 ); } // ===================================================================== 2023-06-14 // Disable PHP display_errors so that notice, warning and error messages // don't show up in the AJAX response. function code_profiler_hide_errors() { ini_set('display_errors', 0 ); } // ===================================================================== // Disable opcode cache. function code_profiler_disable_opcode() { try { if ( extension_loaded('Zend OPcache') ) { ini_set('opcache.enable', 0); } elseif ( extension_loaded('wincache') ) { ini_set('wincache.fcenabled', 0); } set_time_limit(0); ini_set('memory_limit', -1); } catch ( Exception $e ) { } $cp_options = get_option('code-profiler'); /** * Disable WP-CRON. */ if (! empty( $cp_options['disable_wpcron'] ) ) { if (! defined('DISABLE_WP_CRON') ) { define('DISABLE_WP_CRON', true ); } } /** * Enable PHP error logging. */ if (! empty( $cp_options['php_error'] ) ) { ini_set('log_errors', 1 ); $phplog = ini_get('error_log'); if ( empty( $phplog ) || $phplog === false ) { ini_set('error_log', WP_CONTENT_DIR .'/debug.log'); } else { ini_set('error_log', $phplog ); } } } // ===================================================================== // Verify the security key when running the profiler. function code_profiler_verify_key() { $response = [ 'status' => 'error', // We cannot load translation here. 'message' => 'Security keys do not match. Reload the page and try again (%s)' ]; if ( empty( $_REQUEST['profiler_key'] ) ) { $response['message'] = sprintf( $response['message'], '001'); } else { $file = CODE_PROFILER_UPLOAD_DIR .'/key_'. sha1( $_REQUEST['profiler_key'] ) .'.tmp'; if ( file_exists( $file ) ) { /** * We limit the TTL to max $expire seconds to match code_profiler_cleantmpfiles(). */ $now = time(); $expire = 20; // seconds if ( filemtime( $file ) + $expire < $now ) { $response['message'] = sprintf( $response['message'], '003'); wp_send_json( $response ); } // Delete it and accept the request unlink( $file ); return; } $response['message'] = sprintf( $response['message'], '002'); } wp_send_json( $response ); } // ===================================================================== // Write message to the log. // Log level can be a combination of INFO (1), WARN (2), ERROR (4) // and DEBUG (8). function code_profiler_write2log( $message, $level, $create ) { if ( empty( $create ) ) { file_put_contents( CODE_PROFILER_LOG, time() ."~~$level~~$message\n", FILE_APPEND ); } else { file_put_contents( CODE_PROFILER_LOG, time() ."~~$level~~$message\n" ); } } function code_profiler_log_info( $string, $create = 0 ) { code_profiler_write2log( $string, 1, $create ); } function code_profiler_log_warn( $string, $create = 0 ) { code_profiler_write2log( $string, 2, $create ); } function code_profiler_log_error( $string, $create = 0 ) { code_profiler_write2log( $string, 4, $create ); } function code_profiler_log_debug( $string, $create = 0 ) { code_profiler_write2log( $string, 8, $create ); } // ===================================================================== // Retrieve and return matching files from a directory. function code_profiler_glob( $directory, $regex, $pathname = false ) { $list = []; foreach ( new DirectoryIterator( $directory ) as $finfo ) { if (! $finfo->isDot() && preg_match("`$regex`", $finfo->getFilename() ) ) { if ( $pathname ) { $list[] = $finfo->getPathname(); } else { $list[] = $finfo->getFilename(); } } } return $list; } // ===================================================================== // Verify if a profile exists and return its full path. function code_profiler_get_profile_path( $id, $type = 'slugs') { if (! empty( $id ) && preg_match('/^\d{10}\.\d+$/', $id ) ) { $glob = code_profiler_glob(CODE_PROFILER_UPLOAD_DIR, "$id\..+\.$type\.profile$", true); if ( is_array( $glob ) && ! empty( $glob[0] ) ) { if ( preg_match( "`$id\.(.+?).$type.profile$`", $glob[0], $match ) ) { return CODE_PROFILER_UPLOAD_DIR. "/$id.{$match[1]}"; } } } return false; } // ===================================================================== // Open, parse and return the content of a profile file. function code_profiler_get_profile_data( $file, $type = 'slugs') { $buffer = []; $fh = fopen( "$file.$type.profile", 'rb'); if ( $fh === false ) { $err = sprintf( CODE_PROFILER_ERROR_NOTICE, sprintf( esc_html__('Unable to open profile file: %s.', 'code-profiler'), esc_html( "$file.$type.profile" ) ) ); $buffer['error'] = $err; return $buffer; } /** * We don't use fgetcsv() as it requires the $escape parameter since PHP 8.4 */ while (! feof( $fh ) ) { $tmp = trim( fgets( $fh, 1000 ) ); if (! $tmp ) { continue; } $buffer[] = explode( "\t", $tmp ); } fclose( $fh ); if ( empty( $buffer ) ) { $err = sprintf( CODE_PROFILER_ERROR_NOTICE, sprintf( esc_html__('Profile file is empty or corrupted: %s.', 'code-profiler'), esc_html( "$file.$type.profile" ) ) ); $buffer['error'] = $err; return $buffer; } // Get rid of the empty field created by fgetcsv return array_filter( $buffer ); } // ===================================================================== // Exit with a json-encoded error message except if we're running // from WP CLI function code_profiler_wp_send_json( $response ) { if ( defined('WP_CLI') && WP_CLI ) { WP_CLI::error( $response['message'] ); } wp_send_json( $response ); } // ===================================================================== // Retrieve the summary stats for a given profile. function code_profiler_getsummarystats( $profile_path, $type = 'html') { if ( $type == 'html') { $open = ''; } else { // WP-CLI $open = " \u{27A4} "; $div = "\n \u{27A4} "; $close = "\n\n"; } if (! file_exists( "$profile_path.summary.profile" ) ) { return false; } $decode = json_decode( file_get_contents( "$profile_path.summary.profile" ) ); $string = $open; if ( empty( $decode->items ) ) { $tmp = __('N/A', 'code-profiler'); } else { $tmp = (int) --$decode->items; } $string .= sprintf( __('%s plugins and 1 theme', 'code-profiler'), $tmp ); $string .= $div; if ( empty( $decode->time ) ) { $tmp = __('N/A', 'code-profiler'); } else { $tmp = number_format( $decode->time, 4 ); } $string .= sprintf( __('Execution time: %ss', 'code-profiler'), $tmp ); $string .= $div; if ( empty( $decode->memory ) ) { $tmp = __('N/A', 'code-profiler'); } else { $tmp = number_format( $decode->memory, 2); } $string .= sprintf( __('Peak memory: %s MB', 'code-profiler'), $tmp ); $string .= $div; if ( empty( $decode->io ) ) { $tmp = __('N/A', 'code-profiler'); } else { $tmp = number_format( (int) $decode->io ); } $string .= sprintf( __('File I/O operations: %s', 'code-profiler'), $tmp ); $string .= $div; if ( empty( $decode->queries ) ) { $tmp = __('N/A', 'code-profiler'); } else { $tmp = number_format( (int) $decode->queries ); } $string .= sprintf( __('SQL queries: %s', 'code-profiler'), $tmp ); $string .= $div; if ( empty( CODE_PROFILER_ACCURACY[ $decode->precision ] ) ) { $tmp = __('N/A', 'code-profiler'); } else { $tmp = CODE_PROFILER_ACCURACY[ $decode->precision ]; } $string .= sprintf( __('Accuracy: %s', 'code-profiler'), $tmp ); $string .= $close; return $string; } // ===================================================================== // Remove *tmp files left in the profiles folder. function code_profiler_cleantmpfiles() { $glob = code_profiler_glob( CODE_PROFILER_UPLOAD_DIR, '\.tmp$', true ); if ( is_array( $glob ) ) { $now = time(); foreach( $glob as $file ) { /** * Temporary files deletion: * - +20 seconds for temp sessions (to match the TTL checked at runtime). * - +10 minutes for unmatched temp profiles. */ if ( strpos( $file, CODE_PROFILER_UPLOAD_DIR .'/key_') === 0 ) { if ( filemtime( $file ) + 20 > $now ) { continue; } } else { if ( filemtime( $file ) + 60 * 10 > $now ) { continue; } } /** * Delete all temp files. */ unlink( $file ); } } } // ===================================================================== // Remove non-ASCII characters from a string. function code_profiler_ASCII_filter( $string ) { return preg_replace('/[\x00-\x1f\x7f-\xff]/', '', $string ); } // ===================================================================== // Clear the log if it's bigger than 100KB. function code_profiler_clearlog() { if ( file_exists( CODE_PROFILER_LOG ) && filesize( CODE_PROFILER_LOG ) > 100000 ) { file_put_contents( CODE_PROFILER_LOG, "\n" ); } } // ===================================================================== 2023-06-15 // We don't want to be bothered by other themes/plugins' admin notices. add_action('admin_head', 'code_profiler_hide_admin_notices'); function code_profiler_hide_admin_notices() { if ( isset( $_GET['page'] ) && $_GET['page'] == 'code-profiler') { remove_all_actions('admin_notices'); remove_all_actions('all_admin_notices'); add_filter('admin_footer_text', 'code_profiler_admin_footer'); } } function code_profiler_admin_footer () { echo ''. sprintf( /* Translators: %s are the '' and '' anchors */ esc_html( 'Thank you for using %sCode Profiler%s.', 'code-profiler' ), '', '' ). ''; } // ===================================================================== // Hide/display an element depending on some value. function code_profiler_showhide( $var, $val ) { if ( $var == $val ) { echo " style='display:block'"; } else { echo " style='display:none'"; } } // ===================================================================== // Retrieve all themes. function code_profiler_get_themes() { $list = []; // Make sure the function is loaded if (! function_exists('wp_get_themes') ) { require_once ABSPATH . 'wp-includes/theme.php'; } $themes = wp_get_themes(); foreach( $themes as $k => $v ) { if ( $v->Name ) { $list[ $k ] = [ 'n' => $v->Name, 't' => $v->Template ]; } else { $list[ $k ] = [ 'n' => $k, 't' => $v->Template ]; } } return $list; } // ===================================================================== // Retrieve all available cron events. function code_profiler_get_crons() { $list = []; $wp_crons = _get_cron_array(); if ( empty( $wp_crons ) ) { return $list; } foreach ( $wp_crons as $timestamp => $cronhooks ) { foreach ( $cronhooks as $hook => $keys ) { $list[] = $hook; } } sort( $list ); return $list; } // ===================================================================== // Create a file with the ABSPATH. function code_profiler_create_tmpfile() { $tmp_folder = dirname( __DIR__ ) .'/tmp'; $tmp_file = "$tmp_folder/profiler.inc.php"; if (! is_dir( $tmp_folder ) ) { $res = @ mkdir( $tmp_folder ); if ( false === $res ) { return sprintf( __('Cannot create temporary folder: [%s]. Any attempt to profile a cron event will likely fail.', 'code-profiler'), $tmp_folder ); } } if (! is_file( "$tmp_folder/index.html" ) ) { touch( "$tmp_folder/index.html" ); } $res = @ file_put_contents( $tmp_file, "