system-status.php

I’ll build this as a practical inc/admin/system-status.php module for your RX theme: admin page, server/PHP/WordPress/theme checks, security/performance checks, copy/export tools, and safe recommendations.

Below is an advanced inc/admin/system-status.php file for your RX Theme. It creates a full System Status admin page inside WordPress with many useful checks: WordPress, PHP, server, database, theme, plugins, security, performance, REST API, uploads, memory, filesystem, debug status, caching, SSL, and export/copy report tools.

Create this file:

rx-theme/inc/admin/system-status.php

Then paste this code:

<?php
/**
 * RX Theme - Advanced System Status
 *
 * File: inc/admin/system-status.php
 *
 * @package RX_Theme
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( ! class_exists( 'RX_Theme_System_Status' ) ) :

final class RX_Theme_System_Status {

	/**
	 * Menu slug.
	 *
	 * @var string
	 */
	const MENU_SLUG = 'rx-theme-system-status';

	/**
	 * Minimum recommended PHP version.
	 *
	 * @var string
	 */
	const RECOMMENDED_PHP = '8.1';

	/**
	 * Minimum recommended WordPress version.
	 *
	 * @var string
	 */
	const RECOMMENDED_WP = '6.4';

	/**
	 * Recommended memory limit.
	 *
	 * @var int
	 */
	const RECOMMENDED_MEMORY_MB = 256;

	/**
	 * Init hooks.
	 */
	public static function init() {
		add_action( 'admin_menu', array( __CLASS__, 'register_menu' ) );
		add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_assets' ) );
		add_action( 'admin_post_rx_system_status_download', array( __CLASS__, 'download_report' ) );
	}

	/**
	 * Register admin menu page.
	 */
	public static function register_menu() {
		add_theme_page(
			esc_html__( 'RX System Status', 'rx-theme' ),
			esc_html__( 'RX System Status', 'rx-theme' ),
			'manage_options',
			self::MENU_SLUG,
			array( __CLASS__, 'render_page' )
		);
	}

	/**
	 * Admin CSS/JS.
	 *
	 * @param string $hook Current admin hook.
	 */
	public static function admin_assets( $hook ) {
		if ( false === strpos( $hook, self::MENU_SLUG ) ) {
			return;
		}

		wp_register_style( 'rx-system-status', false, array(), '1.0.0' );
		wp_enqueue_style( 'rx-system-status' );

		$css = '
			.rx-status-wrap{max-width:1280px}
			.rx-status-hero{background:#fff;border:1px solid #dcdcde;border-radius:12px;padding:22px;margin:18px 0;box-shadow:0 1px 2px rgba(0,0,0,.04)}
			.rx-status-hero h1{margin:0 0 8px;font-size:26px}
			.rx-status-hero p{font-size:14px;color:#50575e;margin:0}
			.rx-status-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px;margin:18px 0}
			.rx-status-card{background:#fff;border:1px solid #dcdcde;border-radius:12px;padding:16px;min-height:92px}
			.rx-status-card strong{display:block;font-size:24px;margin-bottom:6px}
			.rx-status-card span{color:#646970}
			.rx-status-section{background:#fff;border:1px solid #dcdcde;border-radius:12px;margin:18px 0;overflow:hidden}
			.rx-status-section h2{margin:0;padding:14px 16px;border-bottom:1px solid #dcdcde;background:#f6f7f7;font-size:16px}
			.rx-status-table{width:100%;border-collapse:collapse}
			.rx-status-table th,.rx-status-table td{padding:12px 16px;border-bottom:1px solid #f0f0f1;text-align:left;vertical-align:top}
			.rx-status-table th{width:280px;color:#1d2327;font-weight:600}
			.rx-status-table tr:last-child th,.rx-status-table tr:last-child td{border-bottom:none}
			.rx-badge{display:inline-flex;align-items:center;gap:5px;padding:4px 9px;border-radius:999px;font-size:12px;font-weight:600;line-height:1}
			.rx-ok{background:#e7f7ed;color:#0a6b2b}
			.rx-warn{background:#fff4d6;color:#8a5700}
			.rx-bad{background:#ffe9e9;color:#b42318}
			.rx-info{background:#eaf3ff;color:#135e96}
			.rx-muted{color:#646970}
			.rx-code{background:#f6f7f7;border:1px solid #dcdcde;border-radius:6px;padding:2px 6px;font-family:Consolas,Monaco,monospace}
			.rx-actions{display:flex;gap:10px;flex-wrap:wrap;margin:18px 0}
			#rx-status-report{width:100%;min-height:340px;font-family:Consolas,Monaco,monospace;font-size:12px;white-space:pre}
			@media(max-width:1100px){.rx-status-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
			@media(max-width:700px){.rx-status-grid{grid-template-columns:1fr}.rx-status-table th,.rx-status-table td{display:block;width:auto}.rx-status-table th{padding-bottom:4px}.rx-status-table td{padding-top:4px}}
		';

		wp_add_inline_style( 'rx-system-status', $css );

		wp_register_script( 'rx-system-status', false, array(), '1.0.0', true );
		wp_enqueue_script( 'rx-system-status' );

		$js = '
			document.addEventListener("DOMContentLoaded", function(){
				var btn = document.getElementById("rx-copy-status");
				var area = document.getElementById("rx-status-report");
				if(btn && area){
					btn.addEventListener("click", function(e){
						e.preventDefault();
						area.select();
						area.setSelectionRange(0, 999999);
						try {
							document.execCommand("copy");
							btn.textContent = "Copied!";
							setTimeout(function(){ btn.textContent = "Copy Report"; }, 1600);
						} catch(err) {
							alert("Copy failed. Please select and copy manually.");
						}
					});
				}
			});
		';

		wp_add_inline_script( 'rx-system-status', $js );
	}

	/**
	 * Render page.
	 */
	public static function render_page() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'You do not have permission to view this page.', 'rx-theme' ) );
		}

		$status = self::get_status_data();
		$score  = self::calculate_score( $status );
		$report = self::plain_text_report( $status );

		?>
		<div class="wrap rx-status-wrap">
			<div class="rx-status-hero">
				<h1><?php esc_html_e( 'RX Theme System Status', 'rx-theme' ); ?></h1>
				<p>
					<?php esc_html_e( 'A complete health report for WordPress, server, PHP, database, theme, security, performance, uploads, REST API, and filesystem.', 'rx-theme' ); ?>
				</p>
			</div>

			<div class="rx-status-grid">
				<?php self::summary_card( esc_html__( 'Health Score', 'rx-theme' ), $score . '%', self::score_badge( $score ) ); ?>
				<?php self::summary_card( esc_html__( 'WordPress', 'rx-theme' ), $status['wordpress']['version']['value'], $status['wordpress']['version']['badge'] ); ?>
				<?php self::summary_card( esc_html__( 'PHP', 'rx-theme' ), $status['server']['php_version']['value'], $status['server']['php_version']['badge'] ); ?>
				<?php self::summary_card( esc_html__( 'Memory Limit', 'rx-theme' ), $status['server']['wp_memory_limit']['value'], $status['server']['wp_memory_limit']['badge'] ); ?>
			</div>

			<div class="rx-actions">
				<a href="<?php echo esc_url( self::download_url() ); ?>" class="button button-primary">
					<?php esc_html_e( 'Download Report', 'rx-theme' ); ?>
				</a>
				<button class="button" id="rx-copy-status">
					<?php esc_html_e( 'Copy Report', 'rx-theme' ); ?>
				</button>
			</div>

			<?php
			self::render_section( esc_html__( 'WordPress Environment', 'rx-theme' ), $status['wordpress'] );
			self::render_section( esc_html__( 'Server Environment', 'rx-theme' ), $status['server'] );
			self::render_section( esc_html__( 'Database Status', 'rx-theme' ), $status['database'] );
			self::render_section( esc_html__( 'Theme Status', 'rx-theme' ), $status['theme'] );
			self::render_section( esc_html__( 'Plugin Status', 'rx-theme' ), $status['plugins'] );
			self::render_section( esc_html__( 'Security Status', 'rx-theme' ), $status['security'] );
			self::render_section( esc_html__( 'Performance Status', 'rx-theme' ), $status['performance'] );
			self::render_section( esc_html__( 'Filesystem and Uploads', 'rx-theme' ), $status['filesystem'] );
			self::render_section( esc_html__( 'WordPress REST API and Cron', 'rx-theme' ), $status['api_cron'] );
			self::render_section( esc_html__( 'Recommended PHP Extensions', 'rx-theme' ), $status['extensions'] );
			?>

			<div class="rx-status-section">
				<h2><?php esc_html_e( 'Plain Text Report', 'rx-theme' ); ?></h2>
				<div style="padding:16px;">
					<textarea id="rx-status-report" readonly><?php echo esc_textarea( $report ); ?></textarea>
				</div>
			</div>
		</div>
		<?php
	}

	/**
	 * Summary card.
	 *
	 * @param string $title Title.
	 * @param string $value Value.
	 * @param string $badge Badge HTML.
	 */
	private static function summary_card( $title, $value, $badge ) {
		?>
		<div class="rx-status-card">
			<strong><?php echo esc_html( $value ); ?></strong>
			<span><?php echo esc_html( $title ); ?></span>
			<div style="margin-top:10px;"><?php echo wp_kses_post( $badge ); ?></div>
		</div>
		<?php
	}

	/**
	 * Render status section.
	 *
	 * @param string $title Section title.
	 * @param array  $items Items.
	 */
	private static function render_section( $title, $items ) {
		?>
		<div class="rx-status-section">
			<h2><?php echo esc_html( $title ); ?></h2>
			<table class="rx-status-table">
				<tbody>
				<?php foreach ( $items as $label => $item ) : ?>
					<tr>
						<th><?php echo esc_html( $item['label'] ); ?></th>
						<td>
							<?php echo wp_kses_post( $item['badge'] ); ?>
							<span style="margin-left:8px;"><?php echo wp_kses_post( $item['value'] ); ?></span>
							<?php if ( ! empty( $item['note'] ) ) : ?>
								<div class="rx-muted" style="margin-top:6px;"><?php echo wp_kses_post( $item['note'] ); ?></div>
							<?php endif; ?>
						</td>
					</tr>
				<?php endforeach; ?>
				</tbody>
			</table>
		</div>
		<?php
	}

	/**
	 * Get all system status data.
	 *
	 * @return array
	 */
	private static function get_status_data() {
		global $wpdb;

		$theme       = wp_get_theme();
		$parent      = $theme->parent();
		$uploads     = wp_upload_dir();
		$wp_memory   = self::size_to_mb( WP_MEMORY_LIMIT );
		$php_memory  = self::size_to_mb( ini_get( 'memory_limit' ) );
		$max_upload  = size_format( wp_max_upload_size() );
		$db_version  = ! empty( $wpdb->db_version() ) ? $wpdb->db_version() : 'Unknown';

		$data = array();

		$data['wordpress'] = array(
			'home_url' => self::item(
				__( 'Home URL', 'rx-theme' ),
				esc_url( home_url() ),
				'info'
			),
			'site_url' => self::item(
				__( 'Site URL', 'rx-theme' ),
				esc_url( site_url() ),
				'info'
			),
			'version' => self::item(
				__( 'WordPress Version', 'rx-theme' ),
				get_bloginfo( 'version' ),
				version_compare( get_bloginfo( 'version' ), self::RECOMMENDED_WP, '>=' ) ? 'ok' : 'warn',
				sprintf(
					esc_html__( 'Recommended: WordPress %s or higher.', 'rx-theme' ),
					self::RECOMMENDED_WP
				)
			),
			'multisite' => self::item(
				__( 'Multisite', 'rx-theme' ),
				is_multisite() ? __( 'Enabled', 'rx-theme' ) : __( 'Disabled', 'rx-theme' ),
				is_multisite() ? 'info' : 'ok'
			),
			'language' => self::item(
				__( 'Site Language', 'rx-theme' ),
				get_locale(),
				'info'
			),
			'timezone' => self::item(
				__( 'Timezone', 'rx-theme' ),
				wp_timezone_string() ? wp_timezone_string() : 'UTC',
				'info'
			),
			'debug' => self::item(
				__( 'WP_DEBUG', 'rx-theme' ),
				defined( 'WP_DEBUG' ) && WP_DEBUG ? __( 'Enabled', 'rx-theme' ) : __( 'Disabled', 'rx-theme' ),
				defined( 'WP_DEBUG' ) && WP_DEBUG ? 'warn' : 'ok',
				defined( 'WP_DEBUG' ) && WP_DEBUG ? __( 'Disable debug mode on production websites.', 'rx-theme' ) : ''
			),
			'debug_log' => self::item(
				__( 'WP_DEBUG_LOG', 'rx-theme' ),
				defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ? __( 'Enabled', 'rx-theme' ) : __( 'Disabled', 'rx-theme' ),
				defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ? 'warn' : 'ok'
			),
			'script_debug' => self::item(
				__( 'SCRIPT_DEBUG', 'rx-theme' ),
				defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? __( 'Enabled', 'rx-theme' ) : __( 'Disabled', 'rx-theme' ),
				defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? 'warn' : 'ok'
			),
		);

		$data['server'] = array(
			'php_version' => self::item(
				__( 'PHP Version', 'rx-theme' ),
				PHP_VERSION,
				version_compare( PHP_VERSION, self::RECOMMENDED_PHP, '>=' ) ? 'ok' : 'warn',
				sprintf(
					esc_html__( 'Recommended: PHP %s or higher.', 'rx-theme' ),
					self::RECOMMENDED_PHP
				)
			),
			'server_software' => self::item(
				__( 'Server Software', 'rx-theme' ),
				isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : 'Unknown',
				'info'
			),
			'wp_memory_limit' => self::item(
				__( 'WordPress Memory Limit', 'rx-theme' ),
				WP_MEMORY_LIMIT,
				$wp_memory >= self::RECOMMENDED_MEMORY_MB ? 'ok' : 'warn',
				sprintf(
					esc_html__( 'Recommended: %dMB or higher.', 'rx-theme' ),
					self::RECOMMENDED_MEMORY_MB
				)
			),
			'php_memory_limit' => self::item(
				__( 'PHP Memory Limit', 'rx-theme' ),
				ini_get( 'memory_limit' ),
				$php_memory >= self::RECOMMENDED_MEMORY_MB || -1 === (int) ini_get( 'memory_limit' ) ? 'ok' : 'warn'
			),
			'max_execution_time' => self::item(
				__( 'Max Execution Time', 'rx-theme' ),
				ini_get( 'max_execution_time' ) . ' seconds',
				(int) ini_get( 'max_execution_time' ) >= 60 || 0 === (int) ini_get( 'max_execution_time' ) ? 'ok' : 'warn',
				__( 'Recommended: 60 seconds or more for large imports and updates.', 'rx-theme' )
			),
			'max_input_vars' => self::item(
				__( 'Max Input Vars', 'rx-theme' ),
				ini_get( 'max_input_vars' ),
				(int) ini_get( 'max_input_vars' ) >= 3000 ? 'ok' : 'warn',
				__( 'Recommended: 3000 or higher for large option panels.', 'rx-theme' )
			),
			'post_max_size' => self::item(
				__( 'Post Max Size', 'rx-theme' ),
				ini_get( 'post_max_size' ),
				self::size_to_mb( ini_get( 'post_max_size' ) ) >= 64 ? 'ok' : 'warn'
			),
			'upload_max_filesize' => self::item(
				__( 'Upload Max Filesize', 'rx-theme' ),
				ini_get( 'upload_max_filesize' ),
				self::size_to_mb( ini_get( 'upload_max_filesize' ) ) >= 64 ? 'ok' : 'warn'
			),
			'wp_max_upload_size' => self::item(
				__( 'WordPress Max Upload Size', 'rx-theme' ),
				$max_upload,
				'ok'
			),
			'curl_version' => self::item(
				__( 'cURL Version', 'rx-theme' ),
				function_exists( 'curl_version' ) ? curl_version()['version'] : __( 'Not available', 'rx-theme' ),
				function_exists( 'curl_version' ) ? 'ok' : 'bad'
			),
		);

		$data['database'] = array(
			'db_extension' => self::item(
				__( 'Database Extension', 'rx-theme' ),
				$wpdb->use_mysqli ? 'mysqli' : 'mysql',
				$wpdb->use_mysqli ? 'ok' : 'warn'
			),
			'db_server' => self::item(
				__( 'Database Server Version', 'rx-theme' ),
				$db_version,
				'info'
			),
			'db_charset' => self::item(
				__( 'Database Charset', 'rx-theme' ),
				DB_CHARSET,
				'ok'
			),
			'db_collate' => self::item(
				__( 'Database Collate', 'rx-theme' ),
				defined( 'DB_COLLATE' ) && DB_COLLATE ? DB_COLLATE : __( 'Default', 'rx-theme' ),
				'info'
			),
			'table_prefix' => self::item(
				__( 'Table Prefix', 'rx-theme' ),
				$wpdb->prefix,
				'wp_' === $wpdb->prefix ? 'warn' : 'ok',
				'wp_' === $wpdb->prefix ? __( 'For security, a custom table prefix is better than the default wp_.', 'rx-theme' ) : ''
			),
		);

		$data['theme'] = array(
			'name' => self::item(
				__( 'Active Theme', 'rx-theme' ),
				$theme->get( 'Name' ),
				'ok'
			),
			'version' => self::item(
				__( 'Theme Version', 'rx-theme' ),
				$theme->get( 'Version' ) ? $theme->get( 'Version' ) : __( 'No version set', 'rx-theme' ),
				$theme->get( 'Version' ) ? 'ok' : 'warn'
			),
			'author' => self::item(
				__( 'Theme Author', 'rx-theme' ),
				$theme->get( 'Author' ) ? wp_kses_post( $theme->get( 'Author' ) ) : __( 'Unknown', 'rx-theme' ),
				'info'
			),
			'template' => self::item(
				__( 'Template Directory', 'rx-theme' ),
				get_template_directory(),
				is_readable( get_template_directory() ) ? 'ok' : 'bad'
			),
			'stylesheet' => self::item(
				__( 'Stylesheet Directory', 'rx-theme' ),
				get_stylesheet_directory(),
				is_readable( get_stylesheet_directory() ) ? 'ok' : 'bad'
			),
			'child_theme' => self::item(
				__( 'Child Theme', 'rx-theme' ),
				is_child_theme() ? __( 'Active', 'rx-theme' ) : __( 'Not active', 'rx-theme' ),
				is_child_theme() ? 'ok' : 'info'
			),
			'parent_theme' => self::item(
				__( 'Parent Theme', 'rx-theme' ),
				$parent ? $parent->get( 'Name' ) . ' ' . $parent->get( 'Version' ) : __( 'None', 'rx-theme' ),
				$parent ? 'ok' : 'info'
			),
		);

		$active_plugins = (array) get_option( 'active_plugins', array() );
		$all_plugins    = self::safe_get_plugins();

		$data['plugins'] = array(
			'total_plugins' => self::item(
				__( 'Total Plugins', 'rx-theme' ),
				count( $all_plugins ),
				'info'
			),
			'active_plugins' => self::item(
				__( 'Active Plugins', 'rx-theme' ),
				count( $active_plugins ),
				count( $active_plugins ) <= 35 ? 'ok' : 'warn',
				count( $active_plugins ) > 35 ? __( 'Too many active plugins may reduce speed and increase conflict risk.', 'rx-theme' ) : ''
			),
			'must_use_plugins' => self::item(
				__( 'Must-Use Plugins', 'rx-theme' ),
				count( self::safe_get_mu_plugins() ),
				'info'
			),
			'dropins' => self::item(
				__( 'Drop-ins', 'rx-theme' ),
				count( self::safe_get_dropins() ),
				'info'
			),
		);

		$data['security'] = array(
			'ssl' => self::item(
				__( 'HTTPS / SSL', 'rx-theme' ),
				is_ssl() ? __( 'Enabled', 'rx-theme' ) : __( 'Not detected', 'rx-theme' ),
				is_ssl() ? 'ok' : 'bad',
				is_ssl() ? '' : __( 'Use HTTPS for better security and SEO.', 'rx-theme' )
			),
			'file_edit' => self::item(
				__( 'Theme/Plugin File Editor', 'rx-theme' ),
				defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT ? __( 'Disabled', 'rx-theme' ) : __( 'Enabled', 'rx-theme' ),
				defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT ? 'ok' : 'warn',
				defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT ? '' : __( 'Recommended: define DISALLOW_FILE_EDIT as true in wp-config.php.', 'rx-theme' )
			),
			'xmlrpc' => self::item(
				__( 'XML-RPC', 'rx-theme' ),
				apply_filters( 'xmlrpc_enabled', true ) ? __( 'Enabled', 'rx-theme' ) : __( 'Disabled', 'rx-theme' ),
				apply_filters( 'xmlrpc_enabled', true ) ? 'warn' : 'ok',
				apply_filters( 'xmlrpc_enabled', true ) ? __( 'If you do not use XML-RPC, consider disabling it for security.', 'rx-theme' ) : ''
			),
			'wp_version_visible' => self::item(
				__( 'WordPress Generator Tag', 'rx-theme' ),
				has_action( 'wp_head', 'wp_generator' ) ? __( 'Visible', 'rx-theme' ) : __( 'Hidden', 'rx-theme' ),
				has_action( 'wp_head', 'wp_generator' ) ? 'warn' : 'ok'
			),
			'debug_display' => self::item(
				__( 'Display Errors', 'rx-theme' ),
				ini_get( 'display_errors' ) ? __( 'Enabled', 'rx-theme' ) : __( 'Disabled', 'rx-theme' ),
				ini_get( 'display_errors' ) ? 'warn' : 'ok'
			),
		);

		$data['performance'] = array(
			'object_cache' => self::item(
				__( 'Object Cache', 'rx-theme' ),
				wp_using_ext_object_cache() ? __( 'External object cache active', 'rx-theme' ) : __( 'Default WordPress object cache', 'rx-theme' ),
				wp_using_ext_object_cache() ? 'ok' : 'info'
			),
			'page_cache_plugin' => self::item(
				__( 'Advanced Cache Drop-in', 'rx-theme' ),
				defined( 'WP_CACHE' ) && WP_CACHE ? __( 'WP_CACHE enabled', 'rx-theme' ) : __( 'WP_CACHE not enabled', 'rx-theme' ),
				defined( 'WP_CACHE' ) && WP_CACHE ? 'ok' : 'warn',
				defined( 'WP_CACHE' ) && WP_CACHE ? '' : __( 'For production websites, use page caching or server-level caching.', 'rx-theme' )
			),
			'cron_disabled' => self::item(
				__( 'DISABLE_WP_CRON', 'rx-theme' ),
				defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ? __( 'Enabled', 'rx-theme' ) : __( 'Disabled', 'rx-theme' ),
				defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ? 'info' : 'ok'
			),
			'concatenate_scripts' => self::item(
				__( 'CONCATENATE_SCRIPTS', 'rx-theme' ),
				defined( 'CONCATENATE_SCRIPTS' ) ? ( CONCATENATE_SCRIPTS ? 'true' : 'false' ) : __( 'Default', 'rx-theme' ),
				'info'
			),
			'gzip' => self::item(
				__( 'zlib.output_compression', 'rx-theme' ),
				ini_get( 'zlib.output_compression' ) ? __( 'Enabled', 'rx-theme' ) : __( 'Disabled or server-managed', 'rx-theme' ),
				'info'
			),
		);

		$data['filesystem'] = array(
			'filesystem_method' => self::item(
				__( 'Filesystem Method', 'rx-theme' ),
				self::filesystem_method(),
				'ok'
			),
			'uploads_dir' => self::item(
				__( 'Uploads Directory', 'rx-theme' ),
				$uploads['basedir'],
				wp_is_writable( $uploads['basedir'] ) ? 'ok' : 'bad',
				wp_is_writable( $uploads['basedir'] ) ? '' : __( 'Uploads directory must be writable.', 'rx-theme' )
			),
			'uploads_url' => self::item(
				__( 'Uploads URL', 'rx-theme' ),
				esc_url( $uploads['baseurl'] ),
				'info'
			),
			'theme_writable' => self::item(
				__( 'Theme Directory Writable', 'rx-theme' ),
				wp_is_writable( get_stylesheet_directory() ) ? __( 'Writable', 'rx-theme' ) : __( 'Not writable', 'rx-theme' ),
				wp_is_writable( get_stylesheet_directory() ) ? 'warn' : 'ok',
				wp_is_writable( get_stylesheet_directory() ) ? __( 'For security, theme folders usually should not be writable by PHP unless needed.', 'rx-theme' ) : ''
			),
			'log_file' => self::item(
				__( 'Debug Log File', 'rx-theme' ),
				file_exists( WP_CONTENT_DIR . '/debug.log' ) ? __( 'Exists', 'rx-theme' ) : __( 'Not found', 'rx-theme' ),
				file_exists( WP_CONTENT_DIR . '/debug.log' ) ? 'warn' : 'ok'
			),
		);

		$rest_status = self::check_rest_api();

		$data['api_cron'] = array(
			'rest_api' => self::item(
				__( 'REST API', 'rx-theme' ),
				$rest_status['message'],
				$rest_status['status']
			),
			'loopback' => self::item(
				__( 'Loopback Requests', 'rx-theme' ),
				self::check_loopback(),
				'info'
			),
			'cron_event_count' => self::item(
				__( 'Scheduled Cron Events', 'rx-theme' ),
				self::count_cron_events(),
				'ok'
			),
			'alternate_cron' => self::item(
				__( 'ALTERNATE_WP_CRON', 'rx-theme' ),
				defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ? __( 'Enabled', 'rx-theme' ) : __( 'Disabled', 'rx-theme' ),
				'info'
			),
		);

		$data['extensions'] = self::php_extensions_status();

		return $data;
	}

	/**
	 * Make a status item.
	 *
	 * @param string $label Label.
	 * @param string $value Value.
	 * @param string $status ok|warn|bad|info.
	 * @param string $note Note.
	 *
	 * @return array
	 */
	private static function item( $label, $value, $status = 'info', $note = '' ) {
		return array(
			'label'  => $label,
			'value'  => is_scalar( $value ) ? (string) $value : wp_json_encode( $value ),
			'status' => $status,
			'badge'  => self::badge( $status ),
			'note'   => $note,
		);
	}

	/**
	 * Badge HTML.
	 *
	 * @param string $status Status.
	 *
	 * @return string
	 */
	private static function badge( $status ) {
		$map = array(
			'ok'   => array( 'class' => 'rx-ok', 'text' => __( 'Good', 'rx-theme' ) ),
			'warn' => array( 'class' => 'rx-warn', 'text' => __( 'Warning', 'rx-theme' ) ),
			'bad'  => array( 'class' => 'rx-bad', 'text' => __( 'Critical', 'rx-theme' ) ),
			'info' => array( 'class' => 'rx-info', 'text' => __( 'Info', 'rx-theme' ) ),
		);

		$item = isset( $map[ $status ] ) ? $map[ $status ] : $map['info'];

		return sprintf(
			'<span class="rx-badge %1$s">%2$s</span>',
			esc_attr( $item['class'] ),
			esc_html( $item['text'] )
		);
	}

	/**
	 * Score badge.
	 *
	 * @param int $score Score.
	 *
	 * @return string
	 */
	private static function score_badge( $score ) {
		if ( $score >= 85 ) {
			return self::badge( 'ok' );
		}

		if ( $score >= 65 ) {
			return self::badge( 'warn' );
		}

		return self::badge( 'bad' );
	}

	/**
	 * Calculate system score.
	 *
	 * @param array $status Status data.
	 *
	 * @return int
	 */
	private static function calculate_score( $status ) {
		$total = 0;
		$good  = 0;

		foreach ( $status as $section ) {
			foreach ( $section as $item ) {
				$total++;

				if ( 'ok' === $item['status'] ) {
					$good++;
				} elseif ( 'info' === $item['status'] ) {
					$good += 0.8;
				} elseif ( 'warn' === $item['status'] ) {
					$good += 0.45;
				}
			}
		}

		if ( 0 === $total ) {
			return 0;
		}

		return (int) round( ( $good / $total ) * 100 );
	}

	/**
	 * Convert memory size to MB.
	 *
	 * @param string $size Size.
	 *
	 * @return int
	 */
	private static function size_to_mb( $size ) {
		$size = trim( (string) $size );

		if ( '-1' === $size ) {
			return -1;
		}

		$unit  = strtolower( substr( $size, -1 ) );
		$value = (float) $size;

		switch ( $unit ) {
			case 'g':
				$value *= 1024;
				break;
			case 'k':
				$value /= 1024;
				break;
			case 'm':
			default:
				break;
		}

		return (int) round( $value );
	}

	/**
	 * Safe get plugins.
	 *
	 * @return array
	 */
	private static function safe_get_plugins() {
		if ( ! function_exists( 'get_plugins' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		return function_exists( 'get_plugins' ) ? get_plugins() : array();
	}

	/**
	 * Safe get MU plugins.
	 *
	 * @return array
	 */
	private static function safe_get_mu_plugins() {
		if ( ! function_exists( 'get_mu_plugins' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		return function_exists( 'get_mu_plugins' ) ? get_mu_plugins() : array();
	}

	/**
	 * Safe get dropins.
	 *
	 * @return array
	 */
	private static function safe_get_dropins() {
		if ( ! function_exists( 'get_dropins' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		return function_exists( 'get_dropins' ) ? get_dropins() : array();
	}

	/**
	 * Filesystem method.
	 *
	 * @return string
	 */
	private static function filesystem_method() {
		if ( ! function_exists( 'get_filesystem_method' ) ) {
			require_once ABSPATH . 'wp-admin/includes/file.php';
		}

		return get_filesystem_method();
	}

	/**
	 * Check REST API.
	 *
	 * @return array
	 */
	private static function check_rest_api() {
		$url      = rest_url();
		$response = wp_remote_get(
			$url,
			array(
				'timeout'   => 8,
				'sslverify' => false,
			)
		);

		if ( is_wp_error( $response ) ) {
			return array(
				'status'  => 'bad',
				'message' => $response->get_error_message(),
			);
		}

		$code = wp_remote_retrieve_response_code( $response );

		if ( $code >= 200 && $code < 300 ) {
			return array(
				'status'  => 'ok',
				'message' => sprintf( 'Working. HTTP %d', $code ),
			);
		}

		return array(
			'status'  => 'warn',
			'message' => sprintf( 'Unexpected response. HTTP %d', $code ),
		);
	}

	/**
	 * Check loopback request.
	 *
	 * @return string
	 */
	private static function check_loopback() {
		$response = wp_remote_get(
			home_url( '/' ),
			array(
				'timeout'   => 8,
				'sslverify' => false,
			)
		);

		if ( is_wp_error( $response ) ) {
			return $response->get_error_message();
		}

		return 'HTTP ' . wp_remote_retrieve_response_code( $response );
	}

	/**
	 * Count cron events.
	 *
	 * @return int
	 */
	private static function count_cron_events() {
		$cron  = _get_cron_array();
		$count = 0;

		if ( empty( $cron ) || ! is_array( $cron ) ) {
			return 0;
		}

		foreach ( $cron as $timestamp => $hooks ) {
			foreach ( $hooks as $hook => $events ) {
				$count += count( $events );
			}
		}

		return $count;
	}

	/**
	 * PHP extension status.
	 *
	 * @return array
	 */
	private static function php_extensions_status() {
		$extensions = array(
			'curl'      => __( 'cURL', 'rx-theme' ),
			'dom'       => __( 'DOM', 'rx-theme' ),
			'exif'      => __( 'EXIF', 'rx-theme' ),
			'fileinfo'  => __( 'Fileinfo', 'rx-theme' ),
			'gd'        => __( 'GD Image Library', 'rx-theme' ),
			'imagick'   => __( 'Imagick', 'rx-theme' ),
			'intl'      => __( 'Intl', 'rx-theme' ),
			'json'      => __( 'JSON', 'rx-theme' ),
			'mbstring'  => __( 'Mbstring', 'rx-theme' ),
			'mysqli'    => __( 'MySQLi', 'rx-theme' ),
			'openssl'   => __( 'OpenSSL', 'rx-theme' ),
			'zip'       => __( 'Zip', 'rx-theme' ),
		);

		$items = array();

		foreach ( $extensions as $ext => $label ) {
			$loaded = extension_loaded( $ext );

			$items[ $ext ] = self::item(
				$label,
				$loaded ? __( 'Loaded', 'rx-theme' ) : __( 'Missing', 'rx-theme' ),
				$loaded ? 'ok' : ( 'imagick' === $ext ? 'warn' : 'bad' ),
				! $loaded ? __( 'Ask your hosting provider to enable this PHP extension if your site needs it.', 'rx-theme' ) : ''
			);
		}

		return $items;
	}

	/**
	 * Download URL.
	 *
	 * @return string
	 */
	private static function download_url() {
		return wp_nonce_url(
			admin_url( 'admin-post.php?action=rx_system_status_download' ),
			'rx_system_status_download'
		);
	}

	/**
	 * Download report.
	 */
	public static function download_report() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Permission denied.', 'rx-theme' ) );
		}

		check_admin_referer( 'rx_system_status_download' );

		$status = self::get_status_data();
		$report = self::plain_text_report( $status );

		nocache_headers();

		header( 'Content-Type: text/plain; charset=utf-8' );
		header( 'Content-Disposition: attachment; filename=rx-theme-system-status-' . gmdate( 'Y-m-d-H-i-s' ) . '.txt' );

		echo $report; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		exit;
	}

	/**
	 * Plain text report.
	 *
	 * @param array $status Status data.
	 *
	 * @return string
	 */
	private static function plain_text_report( $status ) {
		$lines = array();

		$lines[] = 'RX THEME SYSTEM STATUS REPORT';
		$lines[] = 'Generated: ' . gmdate( 'Y-m-d H:i:s' ) . ' UTC';
		$lines[] = 'Site: ' . home_url();
		$lines[] = str_repeat( '=', 70 );

		foreach ( $status as $section_key => $section ) {
			$lines[] = '';
			$lines[] = strtoupper( str_replace( '_', ' ', $section_key ) );
			$lines[] = str_repeat( '-', 70 );

			foreach ( $section as $item ) {
				$value = wp_strip_all_tags( $item['value'] );
				$note  = ! empty( $item['note'] ) ? ' | Note: ' . wp_strip_all_tags( $item['note'] ) : '';

				$lines[] = sprintf(
					'%s: %s [%s]%s',
					$item['label'],
					$value,
					strtoupper( $item['status'] ),
					$note
				);
			}
		}

		return implode( "\n", $lines );
	}
}

RX_Theme_System_Status::init();

endif;

Now load it from your theme functions.php:

/**
 * RX Theme Admin System Status.
 */
if ( is_admin() ) {
	$rx_system_status_file = get_template_directory() . '/inc/admin/system-status.php';

	if ( file_exists( $rx_system_status_file ) ) {
		require_once $rx_system_status_file;
	}
}

After adding it, go to:

WordPress Dashboard → Appearance → RX System Status

This page will show a complete health report for your RX theme and hosting environment.

Leave a Reply

Your email address will not be published. Required fields are marked *