454 lines
13 KiB
PHP
454 lines
13 KiB
PHP
<?php
|
|
/**
|
|
* System Information Collector
|
|
*
|
|
* Collects detailed system information for diagnosing performance bottlenecks.
|
|
*
|
|
* @package MaplePress
|
|
*/
|
|
|
|
class MaplePress_System_Info {
|
|
|
|
/**
|
|
* Get all system information.
|
|
*
|
|
* @return array System information array.
|
|
*/
|
|
public static function get_all_info() {
|
|
return array(
|
|
'php' => self::get_php_info(),
|
|
'php_fpm' => self::get_php_fpm_info(),
|
|
'server' => self::get_server_info(),
|
|
'wordpress' => self::get_wordpress_info(),
|
|
'database' => self::get_database_info(),
|
|
'performance' => self::get_performance_info(),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get PHP configuration information.
|
|
*
|
|
* @return array PHP configuration.
|
|
*/
|
|
private static function get_php_info() {
|
|
return array(
|
|
'version' => PHP_VERSION,
|
|
'sapi' => php_sapi_name(),
|
|
'memory_limit' => ini_get( 'memory_limit' ),
|
|
'max_execution_time' => ini_get( 'max_execution_time' ),
|
|
'max_input_time' => ini_get( 'max_input_time' ),
|
|
'post_max_size' => ini_get( 'post_max_size' ),
|
|
'upload_max_filesize' => ini_get( 'upload_max_filesize' ),
|
|
'max_input_vars' => ini_get( 'max_input_vars' ),
|
|
'display_errors' => ini_get( 'display_errors' ),
|
|
'error_reporting' => ini_get( 'error_reporting' ),
|
|
'opcache_enabled' => function_exists( 'opcache_get_status' ) && opcache_get_status() !== false,
|
|
'opcache_status' => function_exists( 'opcache_get_status' ) ? opcache_get_status() : null,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get PHP-FPM configuration information.
|
|
*
|
|
* @return array PHP-FPM configuration.
|
|
*/
|
|
private static function get_php_fpm_info() {
|
|
$info = array(
|
|
'is_fpm' => false,
|
|
'pool_name' => 'N/A',
|
|
'pm_type' => 'N/A',
|
|
'max_children' => 'N/A',
|
|
'start_servers' => 'N/A',
|
|
'min_spare_servers' => 'N/A',
|
|
'max_spare_servers' => 'N/A',
|
|
'max_requests' => 'N/A',
|
|
'status_url' => 'N/A',
|
|
'active_processes' => 'N/A',
|
|
'total_processes' => 'N/A',
|
|
'idle_processes' => 'N/A',
|
|
'listen_queue' => 'N/A',
|
|
'listen_queue_len' => 'N/A',
|
|
'config_file' => 'N/A',
|
|
);
|
|
|
|
// Check if running under PHP-FPM
|
|
if ( php_sapi_name() === 'fpm-fcgi' || php_sapi_name() === 'cgi-fcgi' ) {
|
|
$info['is_fpm'] = true;
|
|
|
|
// Try to find PHP-FPM config file
|
|
$possible_config_paths = array(
|
|
'/etc/php/' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '/fpm/pool.d/www.conf',
|
|
'/usr/local/etc/php-fpm.d/www.conf',
|
|
'/etc/php-fpm.d/www.conf',
|
|
'/etc/php/fpm/pool.d/www.conf',
|
|
);
|
|
|
|
foreach ( $possible_config_paths as $path ) {
|
|
if ( file_exists( $path ) && is_readable( $path ) ) {
|
|
$info['config_file'] = $path;
|
|
$config = self::parse_php_fpm_config( $path );
|
|
if ( $config ) {
|
|
$info = array_merge( $info, $config );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Try to get PHP-FPM status via FastCGI
|
|
$status = self::get_php_fpm_status();
|
|
if ( $status ) {
|
|
$info = array_merge( $info, $status );
|
|
}
|
|
}
|
|
|
|
return $info;
|
|
}
|
|
|
|
/**
|
|
* Parse PHP-FPM configuration file.
|
|
*
|
|
* @param string $config_file Path to config file.
|
|
* @return array|null Parsed configuration or null.
|
|
*/
|
|
private static function parse_php_fpm_config( $config_file ) {
|
|
$config = array();
|
|
|
|
$content = file_get_contents( $config_file );
|
|
if ( ! $content ) {
|
|
return null;
|
|
}
|
|
|
|
// Parse key configuration values (ignore comments after values)
|
|
$patterns = array(
|
|
'pool_name' => '/^\[([^\]]+)\]/m',
|
|
'pm_type' => '/^pm\s*=\s*(\w+)/m',
|
|
'max_children' => '/^pm\.max_children\s*=\s*(\d+)/m',
|
|
'start_servers' => '/^pm\.start_servers\s*=\s*(\d+)/m',
|
|
'min_spare_servers' => '/^pm\.min_spare_servers\s*=\s*(\d+)/m',
|
|
'max_spare_servers' => '/^pm\.max_spare_servers\s*=\s*(\d+)/m',
|
|
'max_requests' => '/^pm\.max_requests\s*=\s*(\d+)/m',
|
|
);
|
|
|
|
foreach ( $patterns as $key => $pattern ) {
|
|
if ( preg_match( $pattern, $content, $matches ) ) {
|
|
$config[ $key ] = trim( $matches[1] );
|
|
}
|
|
}
|
|
|
|
return $config;
|
|
}
|
|
|
|
/**
|
|
* Get PHP-FPM status from status endpoint.
|
|
*
|
|
* @return array|null Status information or null.
|
|
*/
|
|
private static function get_php_fpm_status() {
|
|
// Try common PHP-FPM status URLs
|
|
$status_urls = array(
|
|
'http://localhost/fpm-status',
|
|
'http://127.0.0.1/fpm-status',
|
|
'http://localhost/status',
|
|
);
|
|
|
|
foreach ( $status_urls as $url ) {
|
|
$response = wp_remote_get( $url . '?json' );
|
|
if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 ) {
|
|
$body = wp_remote_retrieve_body( $response );
|
|
$data = json_decode( $body, true );
|
|
if ( $data ) {
|
|
return array(
|
|
'pool_name' => $data['pool'] ?? 'N/A',
|
|
'total_processes' => $data['total processes'] ?? 'N/A',
|
|
'active_processes' => $data['active processes'] ?? 'N/A',
|
|
'idle_processes' => $data['idle processes'] ?? 'N/A',
|
|
'listen_queue' => $data['listen queue'] ?? 'N/A',
|
|
'listen_queue_len' => $data['listen queue len'] ?? 'N/A',
|
|
'max_children' => $data['max children reached'] ?? 'N/A',
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get server information.
|
|
*
|
|
* @return array Server information.
|
|
*/
|
|
private static function get_server_info() {
|
|
return array(
|
|
'software' => isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : 'Unknown',
|
|
'os' => PHP_OS,
|
|
'hostname' => gethostname(),
|
|
'server_addr' => isset( $_SERVER['SERVER_ADDR'] ) ? $_SERVER['SERVER_ADDR'] : 'Unknown',
|
|
'document_root' => isset( $_SERVER['DOCUMENT_ROOT'] ) ? $_SERVER['DOCUMENT_ROOT'] : 'Unknown',
|
|
'cpu_count' => self::get_cpu_count(),
|
|
'memory_total' => self::get_memory_total(),
|
|
'memory_available' => self::get_memory_available(),
|
|
'load_average' => self::get_load_average(),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get CPU core count.
|
|
*
|
|
* @return int|string CPU count or 'Unknown'.
|
|
*/
|
|
private static function get_cpu_count() {
|
|
if ( function_exists( 'shell_exec' ) ) {
|
|
// Linux
|
|
$output = shell_exec( 'nproc 2>/dev/null' );
|
|
if ( $output ) {
|
|
return (int) trim( $output );
|
|
}
|
|
|
|
// macOS
|
|
$output = shell_exec( 'sysctl -n hw.ncpu 2>/dev/null' );
|
|
if ( $output ) {
|
|
return (int) trim( $output );
|
|
}
|
|
}
|
|
|
|
return 'Unknown';
|
|
}
|
|
|
|
/**
|
|
* Get total system memory in MB.
|
|
*
|
|
* @return string Memory in MB or 'Unknown'.
|
|
*/
|
|
private static function get_memory_total() {
|
|
if ( function_exists( 'shell_exec' ) ) {
|
|
// Linux
|
|
$output = shell_exec( "free -m | grep Mem | awk '{print $2}'" );
|
|
if ( $output ) {
|
|
return trim( $output ) . ' MB';
|
|
}
|
|
|
|
// macOS
|
|
$output = shell_exec( 'sysctl -n hw.memsize 2>/dev/null' );
|
|
if ( $output ) {
|
|
return round( (int) trim( $output ) / 1024 / 1024 ) . ' MB';
|
|
}
|
|
}
|
|
|
|
return 'Unknown';
|
|
}
|
|
|
|
/**
|
|
* Get available system memory in MB.
|
|
*
|
|
* @return string Available memory or 'Unknown'.
|
|
*/
|
|
private static function get_memory_available() {
|
|
if ( function_exists( 'shell_exec' ) ) {
|
|
// Linux
|
|
$output = shell_exec( "free -m | grep Mem | awk '{print $7}'" );
|
|
if ( $output ) {
|
|
return trim( $output ) . ' MB';
|
|
}
|
|
}
|
|
|
|
return 'Unknown';
|
|
}
|
|
|
|
/**
|
|
* Get system load average.
|
|
*
|
|
* @return string Load average or 'Unknown'.
|
|
*/
|
|
private static function get_load_average() {
|
|
if ( function_exists( 'sys_getloadavg' ) ) {
|
|
$load = sys_getloadavg();
|
|
return sprintf( '%.2f, %.2f, %.2f', $load[0], $load[1], $load[2] );
|
|
}
|
|
|
|
return 'Unknown';
|
|
}
|
|
|
|
/**
|
|
* Get WordPress information.
|
|
*
|
|
* @return array WordPress information.
|
|
*/
|
|
private static function get_wordpress_info() {
|
|
global $wp_version;
|
|
|
|
return array(
|
|
'version' => $wp_version,
|
|
'multisite' => is_multisite(),
|
|
'site_url' => get_site_url(),
|
|
'home_url' => get_home_url(),
|
|
'debug_mode' => defined( 'WP_DEBUG' ) && WP_DEBUG,
|
|
'memory_limit' => WP_MEMORY_LIMIT,
|
|
'max_memory_limit' => WP_MAX_MEMORY_LIMIT,
|
|
'theme' => wp_get_theme()->get( 'Name' ),
|
|
'active_plugins' => count( get_option( 'active_plugins', array() ) ),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get database information.
|
|
*
|
|
* @return array Database information.
|
|
*/
|
|
private static function get_database_info() {
|
|
global $wpdb;
|
|
|
|
$info = array(
|
|
'extension' => $wpdb->db_version(),
|
|
'server' => $wpdb->get_var( 'SELECT VERSION()' ),
|
|
'client_version' => $wpdb->db_version(),
|
|
'database_name' => DB_NAME,
|
|
'database_host' => DB_HOST,
|
|
'database_charset' => DB_CHARSET,
|
|
'table_prefix' => $wpdb->prefix,
|
|
);
|
|
|
|
// Get MySQL configuration
|
|
$variables = array(
|
|
'max_connections',
|
|
'max_allowed_packet',
|
|
'query_cache_size',
|
|
'innodb_buffer_pool_size',
|
|
'tmp_table_size',
|
|
'max_heap_table_size',
|
|
);
|
|
|
|
foreach ( $variables as $var ) {
|
|
$value = $wpdb->get_var( "SHOW VARIABLES LIKE '$var'" );
|
|
if ( $value ) {
|
|
$info[ $var ] = $wpdb->get_var( "SHOW VARIABLES LIKE '$var'", 1 );
|
|
}
|
|
}
|
|
|
|
// Get current connections
|
|
$info['current_connections'] = $wpdb->get_var( "SHOW STATUS LIKE 'Threads_connected'" )
|
|
? $wpdb->get_var( "SHOW STATUS LIKE 'Threads_connected'", 1 )
|
|
: 'Unknown';
|
|
|
|
return $info;
|
|
}
|
|
|
|
/**
|
|
* Get performance-related information.
|
|
*
|
|
* @return array Performance information.
|
|
*/
|
|
private static function get_performance_info() {
|
|
return array(
|
|
'object_cache' => wp_using_ext_object_cache() ? 'External' : 'Built-in',
|
|
'curl_available' => function_exists( 'curl_version' ),
|
|
'curl_version' => function_exists( 'curl_version' ) ? curl_version()['version'] : 'N/A',
|
|
'allow_url_fopen' => ini_get( 'allow_url_fopen' ) ? 'Yes' : 'No',
|
|
'compression' => extension_loaded( 'zlib' ) ? 'Available' : 'Not available',
|
|
'https_support' => is_ssl() ? 'Yes' : 'No',
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Format system info as HTML.
|
|
*
|
|
* @param array $info System information array.
|
|
* @return string HTML output.
|
|
*/
|
|
public static function format_as_html( $info ) {
|
|
ob_start();
|
|
?>
|
|
<div class="maplepress-system-info">
|
|
<?php foreach ( $info as $section => $data ) : ?>
|
|
<div class="system-info-section">
|
|
<h3 style="margin-top: 0; padding: 10px; background: #f0f0f1; border-left: 4px solid #2271b1;">
|
|
<?php echo esc_html( ucwords( str_replace( '_', ' ', $section ) ) ); ?>
|
|
</h3>
|
|
<table class="widefat" style="margin-top: 0;">
|
|
<tbody>
|
|
<?php foreach ( $data as $key => $value ) : ?>
|
|
<?php
|
|
// Check if this is the opcache_status field (which contains a large array)
|
|
$is_opcache_status = ( $key === 'opcache_status' && is_array( $value ) );
|
|
$field_id = 'maplepress-field-' . sanitize_title( $section . '-' . $key );
|
|
?>
|
|
<tr>
|
|
<td style="width: 250px; font-weight: bold; vertical-align: top;">
|
|
<?php echo esc_html( ucwords( str_replace( '_', ' ', $key ) ) ); ?>
|
|
<?php if ( $is_opcache_status ) : ?>
|
|
<br>
|
|
<button type="button"
|
|
class="button button-secondary button-small maplepress-toggle-field"
|
|
data-target="<?php echo esc_attr( $field_id ); ?>"
|
|
style="margin-top: 5px;">
|
|
<span class="dashicons dashicons-visibility" style="vertical-align: middle; font-size: 14px;"></span>
|
|
<span class="toggle-text">Show</span>
|
|
</button>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td>
|
|
<?php if ( $is_opcache_status ) : ?>
|
|
<div id="<?php echo esc_attr( $field_id ); ?>" style="display: none;">
|
|
<pre><?php echo esc_html( print_r( $value, true ) ); ?></pre>
|
|
</div>
|
|
<span class="opcache-hidden-notice" style="color: #666; font-style: italic;">
|
|
Click "Show" to view opcache details
|
|
</span>
|
|
<?php elseif ( is_array( $value ) ) : ?>
|
|
<pre><?php echo esc_html( print_r( $value, true ) ); ?></pre>
|
|
<?php elseif ( is_bool( $value ) ) : ?>
|
|
<?php echo $value ? '<span style="color: green;">✓ Yes</span>' : '<span style="color: red;">✗ No</span>'; ?>
|
|
<?php else : ?>
|
|
<?php echo esc_html( $value ); ?>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<style>
|
|
.maplepress-system-info .system-info-section {
|
|
margin-bottom: 30px;
|
|
}
|
|
.maplepress-system-info pre {
|
|
margin: 0;
|
|
padding: 10px;
|
|
background: #f9f9f9;
|
|
border-left: 3px solid #ccc;
|
|
overflow-x: auto;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
jQuery(document).ready(function($) {
|
|
// Toggle field visibility for opcache_status
|
|
$('.maplepress-toggle-field').on('click', function() {
|
|
var button = $(this);
|
|
var targetId = button.data('target');
|
|
var content = $('#' + targetId);
|
|
var notice = button.closest('tr').find('.opcache-hidden-notice');
|
|
var icon = button.find('.dashicons');
|
|
var text = button.find('.toggle-text');
|
|
|
|
if (content.is(':visible')) {
|
|
content.slideUp(300);
|
|
notice.show();
|
|
icon.removeClass('dashicons-hidden').addClass('dashicons-visibility');
|
|
text.text('Show');
|
|
} else {
|
|
content.slideDown(300);
|
|
notice.hide();
|
|
icon.removeClass('dashicons-visibility').addClass('dashicons-hidden');
|
|
text.text('Hide');
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
<?php
|
|
return ob_get_clean();
|
|
}
|
|
}
|