PluginProbe
Version Info – Server Health Monitor, PHP & MySQL Version Display, Environment Indicators / trunk
Version Info – Server Health Monitor, PHP & MySQL Version Display, Environment Indicators vtrunk
2.1.0 2.0.3 2.0.2 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.2.0 1.2.1 1.3.0 1.3.1 1.3.2 1.3.3 2.0.0 2.0.1
version-info / includes / SystemInfo.php

SystemInfo.php in Version Info – Server Health Monitor, PHP & MySQL Version Display, Environment Indicators trunk, at includes/SystemInfo.php

217 lines 7.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:disable WordPress.Files.FileName -- PSR-4 class file naming; renaming would break the autoloader.
2
3
4 namespace GauchoPlugins\VersionInfo;
5
6 defined( 'ABSPATH' ) || exit; // Exit if accessed directly.
7
8 /**
9 * Free-tier system data helpers.
10 *
11 * Single source of truth for the PHP end-of-life table — the PRO Health
12 * Advisor reads it through php_eol_dates(), so free and PRO EOL data can
13 * never drift apart.
14 *
15 * Everything here must stay portable and dependency-free: no /proc reads
16 * (system RAM stays PRO territory in Pro\MemoryProvider), no Pro\*
17 * references (includes/pro/ is stripped from the wp.org free build), and
18 * no PHP 7+ syntax — the plugin still installs on PHP 5.6 / WP 4.7.
19 */
20 class SystemInfo {
21
22 /**
23 * PHP minor versions and their end-of-life (security support end) dates.
24 *
25 * `const` (rather than `private const`) — class constant visibility
26 * modifiers require PHP 7.1+. Dates verified against endoflife.date on
27 * 2026-08-11. PHP 8.6 is unreleased and deliberately absent.
28 *
29 * @var array<string, string>
30 */
31 const PHP_EOL_DATES = array(
32 '5.6' => '2018-12-31',
33 '7.0' => '2019-01-10',
34 '7.1' => '2019-12-01',
35 '7.2' => '2020-11-30',
36 '7.3' => '2021-12-06',
37 '7.4' => '2022-11-28',
38 '8.0' => '2023-11-26',
39 '8.1' => '2025-12-31',
40 '8.2' => '2026-12-31',
41 '8.3' => '2027-12-31',
42 '8.4' => '2028-12-31',
43 '8.5' => '2029-12-31',
44 );
45
46 /**
47 * Returns the PHP minor-version EOL date table.
48 *
49 * @return array<string, string>
50 */
51 public static function php_eol_dates() {
52 return self::PHP_EOL_DATES;
53 }
54
55 /**
56 * EOL status of the running PHP version.
57 *
58 * @return array{date: string|null, days_left: int|null, is_eol: bool}
59 */
60 public static function php_eol_info() {
61 $parts = explode( '.', phpversion() );
62 $minor = ( isset( $parts[0] ) ? $parts[0] : '' ) . '.' . ( isset( $parts[1] ) ? $parts[1] : '0' );
63 if ( ! array_key_exists( $minor, self::PHP_EOL_DATES ) ) {
64 return array(
65 'date' => null,
66 'days_left' => null,
67 'is_eol' => false,
68 );
69 }
70 $eol_ts = strtotime( self::PHP_EOL_DATES[ $minor ] );
71 $is_eol = time() > $eol_ts;
72 return array(
73 'date' => self::PHP_EOL_DATES[ $minor ],
74 'days_left' => $is_eol ? null : (int) round( ( $eol_ts - time() ) / DAY_IN_SECONDS ),
75 'is_eol' => $is_eol,
76 );
77 }
78
79 /**
80 * PHP memory limit and current usage. PHP-process memory only — system
81 * RAM (/proc/meminfo, WMI) is the PRO MemoryProvider's job.
82 *
83 * @return array{usage: int|null, limit: int, percent: float|null}
84 */
85 public static function memory_usage() {
86 $usage = function_exists( 'memory_get_usage' ) ? (int) memory_get_usage( true ) : null;
87 $raw = function_exists( 'ini_get' ) ? ini_get( 'memory_limit' ) : false;
88 // ini_get() returns string|false — false is the only non-string case to normalize.
89 $limit = self::parse_memory_limit( false === $raw ? '' : (string) $raw );
90 return array(
91 'usage' => $usage,
92 'limit' => $limit,
93 'percent' => self::memory_percent( $usage, $limit ),
94 );
95 }
96
97 /**
98 * Pure helper: shorthand ini value to bytes. 0 means unknown/unlimited
99 * ("-1" = unlimited; treated as "no limit", Pro\MemoryProvider precedent).
100 *
101 * @param string $raw Raw ini value, e.g. "256M", "1G", "-1".
102 * @return int
103 */
104 public static function parse_memory_limit( $raw ) {
105 $raw = trim( (string) $raw );
106 if ( '' === $raw || '-1' === $raw ) {
107 return 0;
108 }
109 $bytes = wp_convert_hr_to_bytes( $raw );
110 return $bytes > 0 ? (int) $bytes : 0;
111 }
112
113 /**
114 * Pure helper: usage percentage of the limit; null when either is unknown.
115 *
116 * @param int|null $usage Bytes currently used, or null when unavailable.
117 * @param int $limit Limit in bytes; 0 = unknown/unlimited.
118 * @return float|null
119 */
120 public static function memory_percent( $usage, $limit ) {
121 if ( null === $usage || $limit <= 0 ) {
122 return null;
123 }
124 return round( ( $usage / $limit ) * 100, 1 );
125 }
126
127 /**
128 * Shared human-readable memory string for the three display surfaces.
129 *
130 * @return string "24.6 MB of 256 MB (9.6%)", "24.6 MB (no limit)", or ''
131 * when usage is unavailable (callers omit the row/segment).
132 */
133 public static function memory_text() {
134 $memory = self::memory_usage();
135 if ( null === $memory['usage'] ) {
136 return '';
137 }
138 if ( null === $memory['percent'] ) {
139 /* translators: %s: current PHP memory usage */
140 return sprintf( __( '%s (no limit)', 'version-info' ), size_format( $memory['usage'] ) );
141 }
142 return sprintf(
143 /* translators: %1$s: current PHP memory usage, %2$s: PHP memory limit, %3$s: percentage of the limit used */
144 __( '%1$s of %2$s (%3$s%%)', 'version-info' ),
145 size_format( $memory['usage'] ),
146 size_format( $memory['limit'] ),
147 number_format_i18n( $memory['percent'], 1 )
148 );
149 }
150
151 /**
152 * Shared EOL alert string for the footer and admin bar. Empty unless the
153 * running PHP is past EOL or within 365 days of it — the countdown is an
154 * alert, not permanent string growth for healthy stacks.
155 *
156 * @return string
157 */
158 public static function php_eol_alert_text() {
159 $eol = self::php_eol_info();
160 if ( $eol['is_eol'] ) {
161 return __( 'PHP past EOL', 'version-info' );
162 }
163 if ( null !== $eol['days_left'] && $eol['days_left'] <= 365 ) {
164 /* translators: %s: number of days until the running PHP version reaches end of life */
165 return sprintf( __( 'PHP EOL in %s days', 'version-info' ), number_format_i18n( $eol['days_left'] ) );
166 }
167 return '';
168 }
169
170 /**
171 * Server IP, best effort. Mirrors Pro\LocationProvider::server_ip() —
172 * duplicated by design: free code must never reference Pro\* classes
173 * because includes/pro/ is stripped from the wp.org build.
174 *
175 * @return string Empty string when unavailable (callers omit the row).
176 */
177 public static function server_ip() {
178 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- server-set values (not client input), sanitized by the character whitelist below.
179 $ip = isset( $_SERVER['SERVER_ADDR'] ) ? $_SERVER['SERVER_ADDR'] : ( isset( $_SERVER['LOCAL_ADDR'] ) ? $_SERVER['LOCAL_ADDR'] : '' );
180 return (string) preg_replace( '/[^0-9a-fA-F:\.]/', '', (string) $ip );
181 }
182
183 /**
184 * Compact Markdown table of the core stack, built server-side so the
185 * clipboard JS stays dumb. Deliberately excludes the plugin/theme list —
186 * that is the PRO System Export feature.
187 *
188 * @return string
189 */
190 public static function markdown_table() {
191 global $wpdb;
192 $memory = self::memory_usage();
193 $server = sanitize_text_field( isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : __( 'Unknown', 'version-info' ) );
194
195 $rows = array(
196 array( __( 'WordPress', 'version-info' ), apply_filters( 'version_info_wp_version', get_bloginfo( 'version' ) ) ),
197 array( __( 'PHP', 'version-info' ), apply_filters( 'version_info_php_version', phpversion() ) ),
198 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- reading the MySQL server version; no WP API exists and the value is not site data.
199 array( __( 'MySQL', 'version-info' ), apply_filters( 'version_info_mysql_version', (string) $wpdb->get_var( 'SELECT VERSION()' ) ) ),
200 array( __( 'Web Server', 'version-info' ), apply_filters( 'version_info_server_software', $server ) ),
201 array( __( 'PHP Memory Limit', 'version-info' ), $memory['limit'] > 0 ? size_format( $memory['limit'] ) : __( 'Unlimited', 'version-info' ) ),
202 array( __( 'WP Memory Limit', 'version-info' ), defined( 'WP_MEMORY_LIMIT' ) ? WP_MEMORY_LIMIT : __( 'Unknown', 'version-info' ) ),
203 array( __( 'Locale', 'version-info' ), get_locale() ),
204 array( __( 'Multisite', 'version-info' ), is_multisite() ? __( 'Yes', 'version-info' ) : __( 'No', 'version-info' ) ),
205 );
206
207 $lines = array(
208 '| ' . __( 'Item', 'version-info' ) . ' | ' . __( 'Value', 'version-info' ) . ' |',
209 '| --- | --- |',
210 );
211 foreach ( $rows as $row ) {
212 $lines[] = '| ' . str_replace( '|', '\|', (string) $row[0] ) . ' | ' . str_replace( '|', '\|', (string) $row[1] ) . ' |';
213 }
214 return implode( "\n", $lines );
215 }
216 }
217