Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
26 / 26 |
|
100.00% |
3 / 3 |
CRAP | |
100.00% |
1 / 1 |
CSV | |
100.00% |
26 / 26 |
|
100.00% |
3 / 3 |
10 | |
100.00% |
1 / 1 |
build | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
8 | |||
stringifyRow | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
collectColumnNamesFromHeterogeneousObjects | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** @noinspection PhpUnhandledExceptionInspection */ |
3 | declare(strict_types=1); |
4 | |
5 | // TODO unit Test |
6 | |
7 | class CSV { |
8 | const allowedDelimiters = [',', ';', '|', '\t', '\s']; |
9 | const allowedEnclosures = ['"', "'"]; |
10 | const allowedLineEndings = ["\r\n", "\r", "\n"]; // Win / Mac / Unix |
11 | |
12 | /** |
13 | * builds string containing data CSV from a collections of heterogeneous arrays |
14 | * |
15 | * example: |
16 | * $data = [ |
17 | * [ |
18 | * "color" => "green", |
19 | * "form" => "circle" |
20 | * ], |
21 | * [ |
22 | * "color" => "blue", |
23 | * "pattern" => "dotted" |
24 | * ] |
25 | * ] |
26 | * echo CSV::build($data); |
27 | * color,form,pattern |
28 | * green,circle, |
29 | * blue,,dotted |
30 | * |
31 | * |
32 | * @param array $data - array of (assoc) arrays. keys are columns names, values are cell values |
33 | * @param array $columnNames - names of columns to be written in csv. if empty, it will take all keys from the |
34 | * provided arrays as columns |
35 | * @param string $delimiter - delimiter. falls back to default if not allowed |
36 | * @param string $enclosure - enclosure. falls back to default if not allowed |
37 | * @param string $lineDelimiter - line-delimiter. falls back to default if not allowed |
38 | * @return string |
39 | */ |
40 | static function build(array $data, array $columnNames = [], |
41 | string $delimiter = ',', string $enclosure = '"', string $lineDelimiter = '\n'): string { |
42 | $columns = (is_array($columnNames) and count($columnNames)) |
43 | ? $columnNames |
44 | : CSV::collectColumnNamesFromHeterogeneousObjects($data); |
45 | $enclosure = in_array($enclosure, CSV::allowedEnclosures) ? $enclosure : '"'; |
46 | $delimiter = in_array($delimiter, CSV::allowedDelimiters) ? $delimiter : ','; |
47 | $lineDelimiter = in_array($lineDelimiter, CSV::allowedLineEndings) ? $lineDelimiter : "\n"; |
48 | |
49 | $csvRows = []; |
50 | |
51 | $csvRows[] = CSV::stringifyRow($columns, $delimiter, $enclosure); |
52 | |
53 | foreach ($data as $set) { |
54 | $row = []; |
55 | |
56 | foreach ($columns as $column) { |
57 | $row[] = $set[$column] ?? ''; |
58 | } |
59 | |
60 | $csvRows[] = CSV::stringifyRow($row, $delimiter, $enclosure); |
61 | } |
62 | |
63 | return implode($lineDelimiter, $csvRows); |
64 | } |
65 | |
66 | private static function stringifyRow($row, $delimiter, $enclosure): string { |
67 | return implode( |
68 | $delimiter, |
69 | array_map( |
70 | function($cell) use ($enclosure, $delimiter) { |
71 | return $enclosure . preg_replace('#(\\' . $enclosure . ')#', '`', (string) $cell) . $enclosure; |
72 | }, |
73 | $row |
74 | ) |
75 | ); |
76 | } |
77 | |
78 | /** |
79 | * @param array $data - an array ofd assoc arrays |
80 | * @return array - all used keys once |
81 | */ |
82 | static function collectColumnNamesFromHeterogeneousObjects(array $data): array { |
83 | return array_values(array_unique(array_reduce($data, function($agg, $array) { |
84 | return array_merge($agg, array_keys($array)); |
85 | }, []))); |
86 | } |
87 | |
88 | } |