Files
wp-healthcheck/includes/steps/20-environment.php
Steve Hanlon 6dd050ea1d Rename to ATT Site Healthcheck (private prefix)
Wholesale rename to avoid clashes with generic 'site healthcheck'
plugin names on a target site:

  - Plugin Name:        'Site Healthcheck' → 'ATT Site Healthcheck'
  - Main file:          site-healthcheck.php → att-site-healthcheck.php
  - Plugin folder:      site-healthcheck → att-site-healthcheck
  - Admin menu slug:    site-healthcheck → att-site-healthcheck
  - Settings slug:      site-healthcheck-settings → att-site-healthcheck-settings
  - PHP class prefix:   WPH_ → ATT_HC_
  - Function prefix:    wph_ → att_hc_
  - Option / transient: wph_* → att_hc_*
  - Action/filter:      wph_* → att_hc_*
  - CSS class prefix:   wph- → att-hc-
  - Constants:          WPH_GITEA_* → ATT_HC_GITEA_*
  - Class file names:   class-wph-*.php → class-att-hc-*.php
  - Dev folder:         ~/dev/wp-healthcheck → ~/dev/att-site-healthcheck

Existing in-progress sessions on installs that had the old wph_session
option will not migrate — they were intended for dev use only and the
user has confirmed this is OK for the rename window.

Smoke-tested on testsite: classes load, 14 steps discovered, save/load
round-trip works, admin page renders with new att-hc- CSS classes.

Recovery plugin detection unchanged — that lives in wp-site-recovery
and continues to be detected by Name + Author header.
2026-06-12 11:11:42 +01:00

148 lines
6.4 KiB
PHP

<?php
if (!defined('ABSPATH')) exit;
return new class extends ATT_HC_Step {
public function id(): string { return 'environment'; }
public function title(): string { return 'Step 2 — Environment Check'; }
public function blurb(): string {
return 'Before touching updates, review the hosting environment.';
}
public function sub_items(): array {
return [
'PHP version — check against WordPress requirements and plugin compatibility. Flag if below 8.1. Note if EOL.',
'Disk usage — flag if over 80% used',
'Error logs — check the server error log and WordPress debug log if enabled. Note any recurring errors, 500s, or deprecated function warnings',
'wp-config.php — confirm WP_DEBUG is off on production',
'File permissions — spot check wp-config.php (should be 640 or 600), wp-content (755), uploads (755)',
];
}
public function autocheck(array $session_state): array {
$f = [];
// PHP version + EOL
// EOL dates from php.net (Y-m-d). Conservative — bump as new versions ship.
$eol = [
'7.4' => '2022-11-28',
'8.0' => '2023-11-26',
'8.1' => '2025-12-31',
'8.2' => '2026-12-31',
'8.3' => '2027-12-31',
'8.4' => '2028-12-31',
];
$php = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION;
if (isset($eol[$php])) {
$is_eol = strtotime($eol[$php]) < time();
$f[] = $this->finding(
'php_version',
$is_eol ? 'bad' : (version_compare($php, '8.1', '<') ? 'warn' : 'ok'),
'PHP version',
PHP_VERSION,
$is_eol ? "EOL since {$eol[$php]} — upgrade urgently." : "Supported until {$eol[$php]}."
);
} else {
$f[] = $this->finding('php_version', 'info', 'PHP version', PHP_VERSION, 'EOL date unknown for this branch.');
}
// WP version vs latest
global $wp_version;
if (!function_exists('get_core_updates')) require_once ABSPATH . 'wp-admin/includes/update.php';
$updates = function_exists('get_core_updates') ? get_core_updates(['dismissed' => true]) : [];
$latest = (!empty($updates) && !empty($updates[0]->current)) ? $updates[0]->current : $wp_version;
$behind = version_compare($wp_version, $latest, '<');
$f[] = $this->finding(
'wp_version',
$behind ? 'warn' : 'ok',
'WordPress version',
$wp_version,
$behind ? "Latest is {$latest} — update available." : 'Up to date.'
);
// Disk usage on ABSPATH
$free = @disk_free_space(ABSPATH);
$total = @disk_total_space(ABSPATH);
if ($free !== false && $total !== false && $total > 0) {
$used_pct = round(($total - $free) / $total * 100, 1);
$level = $used_pct > 90 ? 'bad' : ($used_pct > 80 ? 'warn' : 'ok');
$f[] = $this->finding(
'disk_usage',
$level,
'Disk usage',
$used_pct . '% used',
size_format($total - $free) . ' of ' . size_format($total) . ' (free: ' . size_format($free) . ')'
);
}
// wp-config flags
$flags = [
'WP_DEBUG' => false,
'WP_DEBUG_DISPLAY' => true, // default true; we want false on production
'WP_DEBUG_LOG' => false,
'DISALLOW_FILE_EDIT' => false, // we want this true
'WP_ENVIRONMENT_TYPE'=> 'production',
];
foreach ($flags as $const => $expected_for_prod) {
if (!defined($const)) {
if ($const === 'WP_ENVIRONMENT_TYPE') continue; // optional
$f[] = $this->finding('flag_' . strtolower($const), 'info', $const, 'not defined', 'Default applies.');
continue;
}
$val = constant($const);
if ($const === 'WP_ENVIRONMENT_TYPE') {
$f[] = $this->finding('flag_env_type', 'info', 'WP_ENVIRONMENT_TYPE', (string) $val, '');
continue;
}
$bool = (bool) $val;
$ok = ($const === 'DISALLOW_FILE_EDIT') ? ($bool === true) : ($bool === false);
$level = $ok ? 'ok' : ($const === 'WP_DEBUG_DISPLAY' ? 'bad' : 'warn');
$f[] = $this->finding(
'flag_' . strtolower($const),
$level,
$const,
$bool ? 'true' : 'false',
$ok ? '' : ($const === 'WP_DEBUG_DISPLAY' ? 'Errors are being shown to visitors — turn this off on production.' : 'Recommended: ' . ($expected_for_prod ? 'true' : 'false') . ' on production.')
);
}
// Permissions on key paths
foreach ([
'wp-config.php' => ABSPATH . 'wp-config.php',
'wp-content/' => WP_CONTENT_DIR,
'uploads/' => wp_get_upload_dir()['basedir'] ?? WP_CONTENT_DIR . '/uploads',
] as $label => $path) {
if (!file_exists($path)) continue;
$perms = substr(sprintf('%o', fileperms($path)), -4);
// wp-config: 600 or 640; dirs: 755 (loose check)
$is_cfg = $label === 'wp-config.php';
$ok = $is_cfg ? in_array($perms, ['0600', '0640'], true)
: in_array($perms, ['0755', '0750'], true);
$f[] = $this->finding(
'perm_' . sanitize_key($label),
$ok ? 'ok' : 'warn',
'Permissions: ' . $label,
$perms,
$ok ? '' : ($is_cfg ? 'Recommend 600 or 640.' : 'Recommend 755 or 750.')
);
}
// Error log size (PHP error_log + WP debug.log)
foreach ([
'PHP error_log' => ini_get('error_log'),
'WP debug.log' => WP_CONTENT_DIR . '/debug.log',
] as $label => $path) {
if (!$path || !file_exists($path) || !is_readable($path)) continue;
$size = filesize($path);
$level = $size > 10 * MB_IN_BYTES ? 'warn' : 'info';
$f[] = $this->finding(
'log_' . sanitize_key($label),
$level,
$label,
size_format($size),
$path . ($level === 'warn' ? ' — large file, consider rotating + reviewing tail.' : '')
);
}
return $f;
}
};