%s

' ); define('CODE_PROFILER_ERROR_NOTICE', '

%s

' ); global $wp_version; define ( 'CODE_PROFILER_UA', [ esc_html__('Desktop', 'code-profiler') => [ 'FireFox' => 'Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0', 'Chrome' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36' ], esc_html__('Mobile', 'code-profiler') => [ 'Android Phone' => 'Mozilla/5.0 (Android 12.0; Mobile; rv:97.0) Gecko/97.0'. ' Firefox/97.0', 'Android Tablet' => 'Mozilla/5.0 (Linux; Android 12.0; SAMSUNG-SM-T377A Build/NMF26X)'. ' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.3538.102 Mobile Safari/537.36', 'iPhone' => 'Mozilla/5.0 (iPhone; CPU OS 10_14 like Mac OS X) AppleWebKit/605.1'. '.15 (KHTML, like Gecko) Version/11.1.1 Mobile/14E304 Safari/605.1.15', 'iPad' => 'Mozilla/5.0 (iPad; CPU OS 10_14 like Mac OS X) AppleWebKit/605.1.15'. ' (KHTML, like Gecko) Version/11.1.1 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://code-profiler.com/)' ] ] ); // ===================================================================== // Update version in the DB and check if options must be updated. function code_profiler_init_update() { $cp_options = get_option( 'code-profiler' ); if ( empty( $cp_options['version'] ) || version_compare( $cp_options['version'], CODE_PROFILER_VERSION, '<' ) ) { $cp_options['version'] = CODE_PROFILER_VERSION; // Version 1.1 if (! isset( $cp_options['enable_wpcli'] ) ) { $cp_options['enable_wpcli'] = 1; } // Version 1.2 if (! isset( $cp_options['disable_wpcron'] ) ) { $cp_options['disable_wpcron'] = 1; } // Version 1.3.1 if (! isset( $cp_options['http_response'] ) ) { $cp_options['http_response'] = '^(?:3|4|5)\d{2}$'; } // 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 ) ) { mkdir( CODE_PROFILER_UPLOAD_DIR, 0755 ); } 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 .'/0----code-profiler.php' ) ) { 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 .'/0----code-profiler.php' ); 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 .'/0----code-profiler.php' ) !== md5_file( __DIR__ .'/mu.plugin' ) ) { copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/0----code-profiler.php' ); } } // Log file if (! file_exists( CODE_PROFILER_LOG ) ) { file_put_contents( CODE_PROFILER_LOG, "\n" ); } } // ===================================================================== // Create the default options. function code_profiler_default_options() { $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, 'warn_composer' => 1, 'enable_wpcli' => 1, 'disable_wpcron' => 1, 'http_response' => '^(?:3|4|5)\d{2}$' ]; update_option( 'code-profiler', $cp_options ); } // ===================================================================== // Create a profile name. function code_profiler_profile_name() { return date('Y-m-d_') . substr( time(), 4 ); } // ===================================================================== // 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); } catch ( Exception $e ) { } $cp_options = get_option( 'code-profiler' ); if (! empty( $cp_options['disable_wpcron'] ) ) { if (! defined( 'DISABLE_WP_CRON' ) ) { define('DISABLE_WP_CRON', true ); } } } // ===================================================================== // Verify the security key when running the profile. function code_profiler_verify_key() { $response = [ 'status' => 'error', 'message' => __('Security keys do not match. Reload the page and try again (%s)', 'code-profiler' ) ]; $cp_options = get_option( 'code-profiler' ); if ( empty( $cp_options['hash'] ) ) { $response['message'] = sprintf( $response['message'], '#1' ); } else { if ( empty( $_REQUEST['profiler_key'] ) ) { $response['message'] = sprintf( $response['message'], '#2' ); } else { if ( $cp_options['hash'] == sha1( $_REQUEST['profiler_key'] ) ) { return; } else { $response['message'] = sprintf( $response['message'], '#3' ); } } } wp_send_json( $response ); } // ===================================================================== // Write message to the log. // Log level can be a combination of INFO (1), WARN (2) and ERROR (4). function code_profiler_write2log( $message, $level = 1 ) { file_put_contents( CODE_PROFILER_LOG, time() ."~~$level~~$message\n", FILE_APPEND ); } function code_profiler_log_info( $string ) { code_profiler_write2log( $string, 1 ); } function code_profiler_log_warn( $string ) { code_profiler_write2log( $string, 2 ); } function code_profiler_log_error( $string ) { code_profiler_write2log( $string, 4 ); } // ===================================================================== // Verify if a profile exists and return its full path. function code_profiler_get_profile_path( $id, $type = 'slugs' ) { $return = false; if (! empty( $id ) && preg_match('/^\d{10}\.\d+$/', $id ) ) { $glob = glob( CODE_PROFILER_UPLOAD_DIR ."/$id.*.$type.profile" ); 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; } while (! feof( $fh ) ) { $buffer[] = fgetcsv( $fh, 1000, "\t" ); } 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 ) ) { return false; } $decode->items--; $string .= sprintf( __('%s plugins and 1 theme', 'code-profiler'), (int) $decode->items ); $string .= $div; if ( empty( $decode->time ) ) { return false; } $string .= sprintf( __('Execution time: %ss', 'code-profiler'), number_format( $decode->time, 4 ) ); $string .= $div; if ( empty( $decode->memory ) ) { return false; } $string .= sprintf( __('Peak memory: %s MB', 'code-profiler'), number_format( $decode->memory, 2) ); $string .= $div; if ( empty( $decode->io ) ) { return false; } $string .= sprintf( __('File I/O operations: %s', 'code-profiler'), number_format( $decode->io ) ); $string .= $div; if ( empty( $decode->queries ) ) { return false; } $string .= sprintf( __('SQL queries: %s', 'code-profiler'), number_format( $decode->queries ) ); $string .= $close; return $string; } // ===================================================================== // Remove *tmp files left after an HTTP error (4xx or 5xx). function code_profiler_cleantmpfiles() { $glob = glob( CODE_PROFILER_UPLOAD_DIR .'/*.tmp' ); if ( is_array( $glob ) ) { $count = 0; foreach( $glob as $file ) { $count++; unlink( $file ); } if ( $count ) { code_profiler_log_info( sprintf( __('Deleting %s temporary files found in the profiles folder', 'code-profiler' ), (int) $count ) ); } } } // ===================================================================== // 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'); } } // ===================================================================== // EOF