, started_at: string, finished_at: string|null}> $scans Scans, newest first. * @return array */ public static function rows( array $scans ): array { $rows = array(); foreach ( $scans as $scan ) { $rows[] = array( 'id' => $scan['id'], 'status' => $scan['status'], 'started_at' => $scan['started_at'], 'finished_at' => $scan['finished_at'], 'new' => (int) ( $scan['stats']['new'] ?? 0 ), 'resolved' => (int) ( $scan['stats']['resolved'] ?? 0 ), 'open' => (int) ( $scan['stats']['open'] ?? 0 ), ); } return $rows; } /** * The plain list for one scan: what is new and what was resolved. * * @param array{id: int, status: string, started_at: string, finished_at: string|null, stats: array} $scan Scan. * @param array{new: array>, resolved: array>} $changes From FindingStore::changes(). * @return array */ public static function changes( array $scan, array $changes ): array { $pick = static fn( array $finding ): array => array( 'id' => $finding['id'], 'title' => $finding['title'], 'severity' => $finding['severity'], 'target' => $finding['target'], 'label' => $finding['source_label'], ); return array( 'scan' => array( 'id' => $scan['id'], 'started_at' => $scan['started_at'], ), 'new' => array_map( $pick, $changes['new'] ), 'resolved' => array_map( $pick, $changes['resolved'] ), 'new_total' => (int) ( $scan['stats']['new'] ?? 0 ), 'resolved_total' => (int) ( $scan['stats']['resolved'] ?? 0 ), ); } /** * How the last two completed scans compare: "better", "worse", "same" or "" with fewer than two. * * @param array $rows Rows from rows(), newest first. */ public static function trend( array $rows ): string { $done = array_values( array_filter( $rows, static fn( array $row ): bool => FindingStore::SCAN_COMPLETED === $row['status'] ) ); if ( count( $done ) < 2 ) { return ''; } if ( $done[0]['open'] === $done[1]['open'] ) { return 'same'; } return $done[0]['open'] < $done[1]['open'] ? 'better' : 'worse'; } /** * Number of scans to show. */ public static function limit(): int { return self::LIMIT; } }