Files
wp-healthcheck/includes/report.php
Steve Hanlon 0d51fc3b59 Phase 3 v1: autocheck framework + six step automations
Infrastructure:
- WPH_Step::autocheck($session_state) returns an array of findings
  shaped {id, level: ok/warn/bad/info, label, value, detail}.
- WPH_Session stores results keyed by step id (persisted in the option-
  backed session).
- WPH_Step::has_autocheck() reflection check so the UI only renders the
  panel for steps that implement automation.
- 'Run checks' / 'Refresh' button per step, admin-post handler runs
  autocheck() and stashes the result on the session.
- Findings rendered as a coloured table on the step card; included
  verbatim in the Markdown report with status icons.

Step automations implemented:
- Step 1 (Backup): detection of 11 known backup plugins by slug;
  active/inactive state; UpdraftPlus last-backup timestamp.
- Step 2 (Environment): PHP version + EOL, WP version vs latest, disk
  usage, wp-config flags, file perms on wp-config/wp-content/uploads,
  error-log sizes.
- Step 4 (Plugins): WP.org API enrichment with 24h transient cache —
  last_updated, active_installs, abandonment flag, removed-from-repo
  flag, update-available count. Summary line at the top.
- Step 8 (Security): SSL cert expiry via stream_socket_client +
  openssl_x509_parse, administrator audit, xmlrpc reachability, login
  URL hardening detection.
- Step 9 (Database): spam comments, post revisions, autoload size (WP
  6.6+ value handling), top 3 largest tables.
- Step 11 (Small fixes): deactivated-but-installed plugin list,
  homepage alt-text scan.

Smoke-tested on testsite — all six steps return findings with
correctly-classified levels. Report regenerated with automated findings
section.
2026-06-11 16:02:34 +01:00

106 lines
4.1 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
if (!defined('ABSPATH')) exit;
/**
* Build a Markdown report for a healthcheck session.
* Per bead hc-5ix.7: pure Markdown, downloadable. No DB-side report storage
* (plugin may be uninstalled at the end of the engagement).
*/
function wph_build_markdown_report(WPH_Session $session): string {
$tech = get_userdata($session->technician_id());
$tech_name = $tech ? $tech->display_name : '#' . $session->technician_id();
$lines = [];
$lines[] = '# Site Healthcheck — ' . $session->site_url();
$lines[] = '';
$lines[] = '- **Started:** ' . date('Y-m-d H:i', $session->started_at());
if ($session->is_finished()) {
$lines[] = '- **Finished:** ' . date('Y-m-d H:i', (int) $session->finished_at());
$minutes = max(1, (int) round(((int) $session->finished_at() - $session->started_at()) / 60));
$lines[] = '- **Duration:** ~' . $minutes . ' minute(s)';
}
$lines[] = '- **Technician:** ' . $tech_name;
$lines[] = '- **Site:** ' . $session->site_url();
$lines[] = '- **WordPress:** ' . $session->wp_version();
$lines[] = '- **PHP:** ' . $session->php_version();
$lines[] = '- **Session ID:** ' . $session->id();
$lines[] = '';
// Summary table
$lines[] = '## Summary';
$lines[] = '';
$lines[] = '| Step | Status |';
$lines[] = '|---|---|';
foreach (WPH_Steps::instance()->all() as $step) {
$state = $session->step_state($step->id());
$lines[] = '| ' . $step->title() . ' | ' . wph_status_label($state['status']) . ' |';
}
$lines[] = '';
// Per-step detail
$lines[] = '## Detail';
$lines[] = '';
foreach (WPH_Steps::instance()->all() as $step) {
$state = $session->step_state($step->id());
$lines[] = '### ' . $step->title();
$lines[] = '';
$lines[] = '_Status: ' . wph_status_label($state['status']) . '_';
if ($state['updated_at']) {
$lines[] = '_Saved: ' . date('Y-m-d H:i', $state['updated_at']) . '_';
}
if ($blurb = $step->blurb()) {
$lines[] = '';
$lines[] = $blurb;
}
if ($items = $step->sub_items()) {
$lines[] = '';
foreach ($items as $item) {
$lines[] = '- [ ] ' . $item;
}
}
if (!empty($state['notes'])) {
$lines[] = '';
$lines[] = '**Notes:**';
$lines[] = '';
foreach (preg_split('/\R/', (string) $state['notes']) as $nl) {
$lines[] = '> ' . $nl;
}
}
$auto = $session->get_autocheck($step->id());
if ($auto && !empty($auto['findings'])) {
$lines[] = '';
$lines[] = '**Automated checks** (run ' . date('Y-m-d H:i', (int) ($auto['checked_at'] ?? 0)) . '):';
$lines[] = '';
foreach ($auto['findings'] as $finding) {
$icon = ['ok' => '✅', 'warn' => '⚠️', 'bad' => '❌', 'info' => ''][$finding['level']] ?? '·';
$bits = [$icon, '**' . $finding['label'] . '**'];
if (!empty($finding['value'])) $bits[] = $finding['value'];
if (!empty($finding['detail'])) $bits[] = '— ' . $finding['detail'];
$lines[] = '- ' . implode(' ', $bits);
}
}
if ($state['status'] === WPH_Session::STATUS_BLOCKED && ($esc = $step->escalation())) {
$lines[] = '';
$lines[] = '> ⚠ **Escalation:** ' . $esc;
}
$lines[] = '';
}
$lines[] = '---';
$lines[] = '_Generated by Site Healthcheck plugin v' . WPH_VERSION . '_';
return implode("\n", $lines) . "\n";
}
function wph_status_label(string $status): string {
switch ($status) {
case WPH_Session::STATUS_DONE: return '✅ Done';
case WPH_Session::STATUS_SKIPPED: return '⏭ Skipped';
case WPH_Session::STATUS_BLOCKED: return '🛑 Blocked';
case WPH_Session::STATUS_NA: return '— N/A';
case WPH_Session::STATUS_NOT_STARTED:
default: return '◻ Not started';
}
}