Plugin: add Google Analytics + Search Console healthcheck steps (hc-u5c)

Slot two new steps between performance (Step 7) and security (Step 8):

- 72-analytics.php: sniffs the homepage for GA4 (G-), GTM, and legacy UA
  measurement IDs plus known loader URLs (gtag.js, gtm.js, analytics.js,
  ga.js) and detects common analytics/tag plugins. Warns if only UA is
  still in use.

- 74-search-console.php: looks for google-site-verification meta tags on
  the homepage, probes for a reachable sitemap (wp-sitemap.xml, then
  sitemap_index.xml, then sitemap.xml), parses robots.txt for a
  Googlebot/* Disallow: /, flags the WP "Discourage search engines"
  setting when on, and notes whether Site Kit is active.

Titles use the "Step —" (unnumbered) convention already used by the
email and handover steps so the existing numbered steps don't shift.
steps.md updated to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-16 09:45:07 +01:00
parent 8dc31c53f2
commit 8e288e1ef6
4 changed files with 304 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends ATT_HC_Step {
public function id(): string { return 'analytics'; }
public function title(): string { return 'Step — Google Analytics Check'; }
public function blurb(): string {
return 'Confirm the site is still reporting into Google Analytics. Autocheck sniffs the homepage HTML for a tracking snippet and known GA plugins — the human check is that events are actually landing in the GA property.';
}
public function sub_items(): array {
return [
'Confirm a tracking snippet is present on the homepage (view source, look for gtag/GTM/analytics.js)',
'Log into Google Analytics and confirm real-time users are being recorded when you load the site',
'Confirm the measurement ID on the site matches the client record (G-XXXX for GA4, GTM-XXXX for Tag Manager)',
'Check that today has page-view events in the property — flag if traffic has dropped to zero',
'Flag if only Universal Analytics (UA-XXXX) is still in use — GA4 has been required since July 2023',
];
}
public function watch_outs(): array {
return [
'A snippet on the page does not prove data is arriving — cookie banners that block until consent will suppress hits until accepted',
'Some caching/optimisation plugins defer or strip inline scripts — the snippet may only appear once cache is warm',
];
}
public function autocheck(array $session_state): array {
$f = [];
$home = home_url('/');
$resp = wp_remote_get($home, ['timeout' => 8, 'redirection' => 3]);
if (is_wp_error($resp)) {
$f[] = $this->finding('fetch', 'warn', 'Homepage fetch', 'failed', $resp->get_error_message());
return $f;
}
$body = (string) wp_remote_retrieve_body($resp);
// Measurement / container IDs anywhere in the HTML.
$found_ids = [];
if (preg_match_all('/\bG-[A-Z0-9]{6,}\b/', $body, $m)) $found_ids = array_merge($found_ids, $m[0]);
if (preg_match_all('/\bGTM-[A-Z0-9]{4,}\b/', $body, $m)) $found_ids = array_merge($found_ids, $m[0]);
if (preg_match_all('/\bUA-\d{4,}-\d+\b/', $body, $m)) $found_ids = array_merge($found_ids, $m[0]);
$found_ids = array_values(array_unique($found_ids));
// Snippet vendors (any script/URL referencing a known loader).
$signals = [
'gtag.js' => 'googletagmanager.com/gtag/js',
'gtm.js' => 'googletagmanager.com/gtm.js',
'analytics.js (UA)' => 'google-analytics.com/analytics.js',
'ga.js (legacy UA)' => 'google-analytics.com/ga.js',
];
$vendors = [];
foreach ($signals as $label => $needle) {
if (stripos($body, $needle) !== false) $vendors[] = $label;
}
if ($found_ids || $vendors) {
$has_ga4 = (bool) preg_grep('/^G-/', $found_ids);
$has_ua = (bool) preg_grep('/^UA-/', $found_ids);
$level = ($has_ua && !$has_ga4) ? 'warn' : 'ok';
$detail = $vendors ? 'loader: ' . implode(', ', $vendors) : '';
if ($has_ua && !$has_ga4) {
$detail = trim($detail . ' — Universal Analytics only; GA4 required since July 2023.');
}
$f[] = $this->finding(
'snippet',
$level,
'Tracking snippet',
$found_ids ? implode(', ', array_slice($found_ids, 0, 4)) : 'present',
$detail
);
} else {
$f[] = $this->finding(
'snippet',
'warn',
'Tracking snippet',
'not found',
'No GA/GTM/UA measurement ID or loader was seen in the homepage HTML. A consent banner may be blocking the script, or tracking may have been removed.'
);
}
// GA/GTM plugin detection — informational only.
if (!function_exists('is_plugin_active')) require_once ABSPATH . 'wp-admin/includes/plugin.php';
$plugins = [
'google-site-kit/google-site-kit.php' => 'Site Kit by Google',
'google-analytics-for-wordpress/googleanalytics.php' => 'MonsterInsights',
'google-analytics-premium/googleanalytics-premium.php' => 'MonsterInsights Pro',
'google-analytics-dashboard-for-wp/gadwp.php' => 'ExactMetrics',
'ga-google-analytics/ga-google-analytics.php' => 'GA Google Analytics',
'analytify/wp-analytify.php' => 'Analytify',
'duracelltomi-google-tag-manager/duracelltomi-google-tag-manager.php' => 'GTM4WP',
'header-footer-code-manager/header-footer-code-manager.php' => 'Header Footer Code Manager',
'insert-headers-and-footers/ihaf.php' => 'WPCode / Insert Headers and Footers',
];
$active = [];
foreach ($plugins as $file => $label) {
if (is_plugin_active($file)) $active[] = $label;
}
$f[] = $this->finding(
'plugin',
$active ? 'ok' : 'info',
'Analytics/tag plugin',
$active ? implode(', ', $active) : 'none detected',
$active ? '' : 'Tracking may be hard-coded in the theme or injected by a page builder — check view-source if the snippet check passed.'
);
return $f;
}
};