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.
148 lines
6.4 KiB
PHP
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;
|
|
}
|
|
};
|