*/ public static function header(): array { return array( __( 'Status', 'forensix-digital' ), __( 'Severity', 'forensix-digital' ), __( 'Type', 'forensix-digital' ), __( 'Check', 'forensix-digital' ), __( 'Problem', 'forensix-digital' ), __( 'Address', 'forensix-digital' ), __( 'Location', 'forensix-digital' ), __( 'Pages affected', 'forensix-digital' ), __( 'First seen', 'forensix-digital' ), __( 'Last seen', 'forensix-digital' ), ); } /** * The cells of one finding. * * @param array $finding Finding row from FindingStore. * @return array */ public static function cells( array $finding ): array { return array( (string) $finding['status'], (string) $finding['severity'], (string) $finding['type'], (string) $finding['check_id'], (string) $finding['title'], (string) $finding['target'], (string) $finding['source_label'], (string) $finding['affected'], (string) $finding['first_seen'], (string) $finding['last_seen'], ); } /** * One CSV line, with quotes where needed and a line break at the end. * * @param array $cells Cells. */ public static function line( array $cells ): string { $out = array(); foreach ( $cells as $cell ) { $cell = str_replace( array( "\r\n", "\r", "\n" ), ' ', $cell ); if ( '' !== $cell && str_contains( "=+-@\t", $cell[0] ) ) { $cell = "'" . $cell; } $out[] = '"' . str_replace( '"', '""', $cell ) . '"'; } return implode( ',', $out ) . "\r\n"; } /** * Every open finding as CSV lines, header first. Reads 100 findings at a time. * * @param FindingStore $store Findings. * @param string $app App id. * @return \Generator */ public static function lines( FindingStore $store, string $app ): \Generator { yield self::line( self::header() ); $page = 1; do { $result = $store->query( array( 'app' => $app ), $page, 100 ); $count = count( $result['items'] ); foreach ( $result['items'] as $finding ) { yield self::line( self::cells( $finding ) ); } ++$page; } while ( 100 === $count ); } }