false, 'score' => 0, 'grade' => 'F', 'scanned_at' => null, 'url' => null, 'issues' => [], 'groups' => [], 'summary' => self::empty_summary(), ]; } $issues = $scan['issues'] ?? []; return [ 'has_data' => true, 'score' => $scan['score'] ?? 0, 'grade' => $scan['grade'] ?? 'F', 'scanned_at' => $scan['scanned_at'] ?? null, 'url' => $scan['url'] ?? null, 'passed' => $scan['passed'] ?? 0, 'failed' => $scan['failed'] ?? 0, 'issues' => $issues, 'groups' => self::group_by_severity( $issues ), 'pour' => self::pour_breakdown( $issues ), 'summary' => self::summary_stats( $issues ), ]; } /** * Group issues by severity for the report view. */ private static function group_by_severity( array $issues ): array { $groups = [ 'critical' => [], 'warning' => [], 'notice' => [], ]; foreach ( $issues as $issue ) { $sev = $issue['severity'] ?? 'notice'; if ( isset( $groups[ $sev ] ) ) { $groups[ $sev ][] = $issue; } } return $groups; } /** * Map issues to WCAG POUR principles (Perceivable, Operable, Understandable, Robust). */ private static function pour_breakdown( array $issues ): array { $pour = [ 'perceivable' => [ 'score' => 100, 'issues' => [] ], 'operable' => [ 'score' => 100, 'issues' => [] ], 'understandable' => [ 'score' => 100, 'issues' => [] ], 'robust' => [ 'score' => 100, 'issues' => [] ], ]; $wcag_map = [ // Perceivable '1.1.1' => 'perceivable', '1.3.1' => 'perceivable', '1.3.6' => 'perceivable', '1.4.3' => 'perceivable', // Operable '2.1.1' => 'operable', '2.3.3' => 'operable', '2.4.4' => 'operable', '2.4.6' => 'operable', '2.4.7' => 'operable', // Understandable '3.1.1' => 'understandable', // Robust '4.1.2' => 'robust', ]; foreach ( $issues as $issue ) { $wcag = $issue['wcag'] ?? ''; $principle = $wcag_map[ $wcag ] ?? 'perceivable'; $pour[ $principle ]['issues'][] = $issue; $weight = $issue['severity'] === 'critical' ? 20 : 10; $pour[ $principle ]['score'] = max( 0, $pour[ $principle ]['score'] - $weight ); } return $pour; } private static function summary_stats( array $issues ): array { $critical = 0; $warnings = 0; $notices = 0; foreach ( $issues as $issue ) { switch ( $issue['severity'] ?? '' ) { case 'critical': $critical++; break; case 'warning': $warnings++; break; default: $notices++; break; } } return compact( 'critical', 'warnings', 'notices' ); } private static function empty_summary(): array { return [ 'critical' => 0, 'warnings' => 0, 'notices' => 0 ]; } /** * Return the rolling scan history (last 10 entries). */ public static function get_history(): array { return get_option( 'accessmate_scan_history', [] ); } }