array( $this->get_site_info_section(), $this->get_requirements_section(), $this->get_environment_section(), ), 'generated_at' => time(), ); } /** * Basic site and framework info. * * @return array */ private function get_site_info_section(): array { return array( 'key' => 'site_info', 'title' => __( 'Site Info', 'ips-order-cart' ), 'rows' => array( $this->row( __( 'Site URL', 'ips-order-cart' ), site_url() ), $this->row( __( 'Home URL', 'ips-order-cart' ), home_url() ), $this->row( __( 'WordPress Version', 'ips-order-cart' ), get_bloginfo( 'version' ) ), $this->row( __( 'PHP Version', 'ips-order-cart' ), PHP_VERSION ), $this->row( __( 'IPS CoreKit Version', 'ips-order-cart' ), $this->get_corekit_version() ), ), ); } /** * Requirements / compatibility checks. * * @return array */ private function get_requirements_section(): array { $wp_memory_limit = $this->memory_size_to_num( WP_MEMORY_LIMIT ); if ( function_exists( 'ini_get' ) ) { $wp_memory_limit = max( $wp_memory_limit, $this->memory_size_to_num( @ini_get( 'memory_limit' ) ) ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged } $wc_version = $this->get_wc_version(); return array( 'key' => 'requirements', 'title' => __( 'Plugins Requirements', 'ips-order-cart' ), 'rows' => array( $this->version_row( __( 'WordPress Version', 'ips-order-cart' ), get_bloginfo( 'version' ), self::REQUIRED_WP ), $this->version_row( __( 'WooCommerce Version', 'ips-order-cart' ), $wc_version, self::REQUIRED_WC, true ), $this->memory_row( __( 'Available Memory', 'ips-order-cart' ), $wp_memory_limit, self::REQUIRED_MEMORY ), $this->version_row( __( 'PHP Version', 'ips-order-cart' ), PHP_VERSION, self::REQUIRED_PHP ), $this->version_row( __( 'TLS Version', 'ips-order-cart' ), $this->get_tls_version(), self::REQUIRED_TLS ), $this->version_row( __( 'ImageMagick Version', 'ips-order-cart' ), $this->get_imagick_version(), '0.0.0', false, true ), $this->boolean_row( __( 'WordPress Cron', 'ips-order-cart' ), $this->is_wp_cron_enabled() ), $this->boolean_row( __( 'MultiByte String', 'ips-order-cart' ), extension_loaded( 'mbstring' ) ), $this->boolean_row( __( 'SimpleXML', 'ips-order-cart' ), extension_loaded( 'simplexml' ) ), $this->boolean_row( __( 'GD Library', 'ips-order-cart' ), extension_loaded( 'gd' ) && function_exists( 'gd_info' ) ), $this->boolean_row( __( 'Iconv Module', 'ips-order-cart' ), extension_loaded( 'iconv' ) ), $this->boolean_row( __( 'OPCache Save Comments', 'ips-order-cart' ), (bool) ini_get( 'opcache.save_comments' ) ), $this->boolean_row( __( 'URL FOpen', 'ips-order-cart' ), (bool) ini_get( 'allow_url_fopen' ) ), ), ); } /** * Environment toggles and debug flags. * * @return array */ private function get_environment_section(): array { return array( 'key' => 'environment', 'title' => __( 'Environment', 'ips-order-cart' ), 'rows' => array( $this->boolean_row( __( 'WP_DEBUG', 'ips-order-cart' ), defined( 'WP_DEBUG' ) && WP_DEBUG ), $this->boolean_row( __( 'WP_CACHE', 'ips-order-cart' ), defined( 'WP_CACHE' ) && WP_CACHE ), $this->boolean_row( __( 'External Object Cache', 'ips-order-cart' ), (bool) wp_using_ext_object_cache() ), ), ); } /** * Safe subset of PHP configuration info. * * @return array */ public function get_php_info(): array { $ini = static function( string $key ): string { $value = ini_get( $key ); return false === $value ? '' : (string) $value; }; $extensions = get_loaded_extensions(); sort( $extensions ); $ext_with_version = array(); foreach ( $extensions as $ext ) { $ext_with_version[] = array( 'name' => $ext, 'version' => phpversion( $ext ) ?: '', ); } return array( 'php_version' => PHP_VERSION, 'php_sapi' => PHP_SAPI, 'memory_limit' => $ini( 'memory_limit' ), 'upload_max_filesize' => $ini( 'upload_max_filesize' ), 'post_max_size' => $ini( 'post_max_size' ), 'max_execution_time' => $ini( 'max_execution_time' ), 'display_errors' => $ini( 'display_errors' ), 'error_reporting' => (string) ini_get( 'error_reporting' ), 'loaded_ini' => php_ini_loaded_file() ?: '', 'scan_additional' => function_exists( 'php_ini_scanned_files' ) ? ( php_ini_scanned_files() ?: '' ) : '', 'extensions' => $ext_with_version, 'safe_full_phpinfo_enabled' => (bool) apply_filters( 'ipscmi_corekit_system_status_full_phpinfo', false ), ); } /** * Log files info (safe metadata only). * * @return array */ public function get_logs(): array { $max_size = (int) apply_filters( 'ipscmi_corekit_system_status_log_max_size', 8 * MB_IN_BYTES ); // default 8 MB. $log_paths = array( 'debug.log' => WP_CONTENT_DIR . '/debug.log', ); // Allow external providers to add more logs. $log_paths = apply_filters( 'ipscmi_corekit_system_status_log_paths', $log_paths ); $files = array(); foreach ( $log_paths as $key => $path ) { if ( ! $path || ! file_exists( $path ) || ! is_readable( $path ) ) { continue; } $size = (int) filesize( $path ); $mtime = (int) filemtime( $path ); $files[] = array( 'id' => sanitize_title( is_string( $key ) ? $key : basename( (string) $path ) ), 'name' => basename( (string) $path ), 'path' => $path, 'size' => $size, 'modified' => $mtime, 'too_large' => $size > $max_size, 'downloadable' => $size <= $max_size, 'content' => $size <= $max_size ? $this->safe_read_file( $path, $max_size ) : '', ); } return array( 'max_size_bytes' => $max_size, 'files' => $files, ); } /** * Database info (lightweight; optional table stats limited by filter). * * @global \wpdb $wpdb * @return array */ public function get_db_info(): array { global $wpdb; $limit_tables = absint( apply_filters( 'ipscmi_corekit_system_status_dbinfo_tables_limit', 20 ) ); $include_tables = apply_filters( 'ipscmi_corekit_system_status_dbinfo_include_tables', true ); $server_version = method_exists( $wpdb, 'db_version' ) ? (string) $wpdb->db_version() : ''; $client_version = ''; $summary = array( 'total_data_bytes' => 0, 'total_index_bytes' => 0, 'total_free_bytes' => 0, 'total_tables' => 0, 'tables' => array(), ); if ( $include_tables ) { $tables = $this->load_table_status( $limit_tables ); $summary['tables'] = $tables['tables']; $summary['total_tables'] = $tables['total_tables']; $summary['total_data_bytes'] = $tables['total_data_bytes']; $summary['total_index_bytes'] = $tables['total_index_bytes']; $summary['total_free_bytes'] = $tables['total_free_bytes']; } return array( 'server_version' => $server_version, 'client_version' => $client_version, 'charset' => $wpdb->charset ?? '', 'collate' => $wpdb->collate ?? '', 'engine_innodb' => $this->has_innodb(), 'summary' => $summary, ); } /** * Fetch table status (limited). * * @param int $limit Number of tables to include (order by data+index desc). * @return array */ private function load_table_status( int $limit ): array { global $wpdb; $total_tables = 0; $total_data_bytes = 0; $total_index_bytes = 0; $total_free_bytes = 0; $result_tables = array(); // Some hosts restrict SHOW TABLE STATUS; catch failures. $tables = $wpdb->get_results( 'SHOW TABLE STATUS', ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery if ( empty( $tables ) || ! is_array( $tables ) ) { return array( 'total_tables' => 0, 'total_data_bytes' => 0, 'total_index_bytes' => 0, 'total_free_bytes' => 0, 'tables' => array(), ); } // Sort by total size desc. usort( $tables, static function ( $a, $b ) { $sizeA = ( (float) ( $a['Data_length'] ?? 0 ) ) + ( (float) ( $a['Index_length'] ?? 0 ) ); $sizeB = ( (float) ( $b['Data_length'] ?? 0 ) ) + ( (float) ( $b['Index_length'] ?? 0 ) ); return $sizeB <=> $sizeA; } ); foreach ( $tables as $row ) { $total_tables++; $data = (float) ( $row['Data_length'] ?? 0 ); $index = (float) ( $row['Index_length'] ?? 0 ); $free = (float) ( $row['Data_free'] ?? 0 ); $total_data_bytes += $data; $total_index_bytes += $index; $total_free_bytes += $free; if ( $limit > 0 && count( $result_tables ) >= $limit ) { continue; } $result_tables[] = array( 'name' => (string) ( $row['Name'] ?? '' ), 'engine' => (string) ( $row['Engine'] ?? '' ), 'rows' => (int) ( $row['Rows'] ?? 0 ), 'data' => $data, 'index' => $index, 'free' => $free, 'collate' => (string) ( $row['Collation'] ?? '' ), ); } return array( 'total_tables' => $total_tables, 'total_data_bytes' => $total_data_bytes, 'total_index_bytes' => $total_index_bytes, 'total_free_bytes' => $total_free_bytes, 'tables' => $result_tables, ); } private function has_innodb(): bool { global $wpdb; $engines = $wpdb->get_results( 'SHOW ENGINES', ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery if ( empty( $engines ) ) { return false; } foreach ( $engines as $engine ) { if ( isset( $engine['Engine'] ) && strtolower( $engine['Engine'] ) === 'innodb' && isset( $engine['Support'] ) ) { $support = strtolower( (string) $engine['Support'] ); return in_array( $support, array( 'default', 'yes', 'enabled', 'available' ), true ); } } return false; } /** * Build a generic row. */ private function row( string $label, string $value, string $status = 'info', ?string $required = null ): array { return array( 'label' => $label, 'value' => $value, 'status' => $status, 'required' => $required, ); } /** * Read file content safely with a max size cap. * * @param string $path File path. * @param int $max_size Max bytes to read. * * @return string */ private function safe_read_file( string $path, int $max_size ): string { if ( ! file_exists( $path ) || ! is_readable( $path ) ) { return ''; } $size = (int) filesize( $path ); if ( $size > $max_size ) { return ''; } $content = file_get_contents( $path ); if ( false === $content ) { return ''; } // Normalize to UTF-8 where possible. if ( function_exists( 'mb_detect_encoding' ) && function_exists( 'mb_convert_encoding' ) ) { $encoding = mb_detect_encoding( $content, 'UTF-8, ISO-8859-1, ASCII', true ); if ( $encoding && 'UTF-8' !== $encoding ) { $content = mb_convert_encoding( $content, 'UTF-8', $encoding ); } } return $content; } /** * Version comparison row. */ private function version_row( string $label, string $value, string $required, bool $allow_empty = false, bool $optional = false ): array { $status = 'ok'; if ( $optional && 'n/a' === strtolower( $value ) ) { $status = 'info'; } elseif ( $allow_empty && empty( $value ) ) { $status = 'warning'; } elseif ( ! $allow_empty && ( empty( $value ) || ! version_compare( $value, $required, '>=' ) ) ) { $status = 'error'; } return $this->row( $label, $value ?: __( 'N/A', 'ips-order-cart' ), $status, $required ); } /** * Boolean row. */ private function boolean_row( string $label, bool $value ): array { return $this->row( $label, $value ? __( 'Enabled', 'ips-order-cart' ) : __( 'Disabled', 'ips-order-cart' ), $value ? 'ok' : 'error' ); } /** * Memory row. */ private function memory_row( string $label, int $bytes, string $required ): array { $status = $bytes >= $this->memory_size_to_num( $required ) ? 'ok' : 'warning'; return $this->row( $label, size_format( $bytes ), $status, $required ); } /** * Derive TLS version from OpenSSL constant when available. */ private function get_tls_version(): string { if ( defined( 'OPENSSL_VERSION_TEXT' ) ) { if ( preg_match( '/OpenSSL\\s+(\\d+\\.\\d+\\.\\d+)/i', OPENSSL_VERSION_TEXT, $matches ) ) { return $matches[1]; } } return '1.0.0'; } private function get_imagick_version(): string { if ( class_exists( 'Imagick' ) && is_callable( array( 'Imagick', 'getVersion' ) ) ) { $info = \Imagick::getVersion(); if ( isset( $info['versionString'] ) && preg_match( '/([0-9]+\\.[0-9]+\\.[0-9]+)/', $info['versionString'], $match ) ) { return $match[1]; } } return 'N/A'; } private function is_wp_cron_enabled(): bool { return ! ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) || (bool) apply_filters( 'ipscmi_corekit_system_status_server_cron', false ); } private function memory_size_to_num( string $memory_size ): int { $memory_size = strtoupper( trim( $memory_size ) ); $unit = substr( $memory_size, -1 ); $size = (int) substr( $memory_size, 0, -1 ); $multiplier = array( 'P' => 5, 'T' => 4, 'G' => 3, 'M' => 2, 'K' => 1, ); if ( isset( $multiplier[ $unit ] ) ) { for ( $i = 1; $i <= $multiplier[ $unit ]; $i++ ) { $size *= 1024; } } return $size; } private function get_wc_version(): string { if ( function_exists( 'WC' ) && WC() ) { return WC()->version ?? ''; } if ( defined( 'WC_VERSION' ) ) { return WC_VERSION; } return ''; } private function get_corekit_version(): string { return ipscmi_order_cart_corekit_plugin_fw_get_version(); } }