1
|
|
|
<?php |
2
|
|
|
namespace Consolidation\OutputFormatters\Formatters; |
3
|
|
|
|
4
|
|
|
use Consolidation\OutputFormatters\Validate\ValidDataTypesInterface; |
5
|
|
|
use Consolidation\OutputFormatters\Options\FormatterOptions; |
6
|
|
|
use Consolidation\OutputFormatters\Validate\ValidDataTypesTrait; |
7
|
|
|
use Consolidation\OutputFormatters\Transformations\TableTransformation; |
8
|
|
|
use Consolidation\OutputFormatters\Exception\IncompatibleDataException; |
9
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* Comma-separated value formatters |
13
|
|
|
* |
14
|
|
|
* Display the provided structured data in a comma-separated list. If |
15
|
|
|
* there are multiple records provided, then they will be printed |
16
|
|
|
* one per line. The primary data types accepted are RowsOfFields and |
17
|
|
|
* PropertyList. The later behaves exactly like the former, save for |
18
|
|
|
* the fact that it contains but a single row. This formmatter can also |
19
|
|
|
* accept a PHP array; this is also interpreted as a single-row of data |
20
|
|
|
* with no header. |
21
|
|
|
*/ |
22
|
|
|
class CsvFormatter implements FormatterInterface, ValidDataTypesInterface, RenderDataInterface |
23
|
|
|
{ |
24
|
|
|
use ValidDataTypesTrait; |
25
|
12 |
|
use RenderTableDataTrait; |
26
|
|
|
|
27
|
|
|
public function validDataTypes() |
28
|
|
|
{ |
29
|
|
|
return |
30
|
12 |
|
[ |
31
|
1 |
|
new \ReflectionClass('\Consolidation\OutputFormatters\StructuredData\RowsOfFields'), |
32
|
1 |
|
new \ReflectionClass('\Consolidation\OutputFormatters\StructuredData\PropertyList'), |
33
|
1 |
|
new \ReflectionClass('\ArrayObject'), |
34
|
|
|
]; |
35
|
1 |
|
} |
36
|
1 |
|
|
37
|
1 |
|
public function validate($structuredData) |
38
|
|
|
{ |
39
|
1 |
|
// If the provided data was of class RowsOfFields |
40
|
|
|
// or PropertyList, it will be converted into |
41
|
|
|
// a TableTransformation object. |
42
|
|
|
if (!is_array($structuredData) && (!$structuredData instanceof TableTransformation)) { |
43
|
11 |
|
throw new IncompatibleDataException( |
44
|
6 |
|
$this, |
45
|
6 |
|
$structuredData, |
46
|
5 |
|
$this->validDataTypes() |
47
|
|
|
); |
48
|
1 |
|
} |
49
|
6 |
|
// If the data was provided to us as a single array, then |
50
|
|
|
// convert it to a single row. |
51
|
|
|
if (is_array($structuredData) && !empty($structuredData)) { |
52
|
|
|
$firstRow = reset($structuredData); |
53
|
|
|
if (!is_array($firstRow)) { |
54
|
|
|
return [$structuredData]; |
55
|
11 |
|
} |
56
|
|
|
} |
57
|
|
|
return $structuredData; |
58
|
11 |
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
11 |
|
* Return default values for formatter options |
62
|
5 |
|
* @return array |
63
|
5 |
|
*/ |
64
|
5 |
|
protected function getDefaultFormatterOptions() |
65
|
|
|
{ |
66
|
11 |
|
return [ |
67
|
11 |
|
FormatterOptions::INCLUDE_FIELD_LABELS => true, |
68
|
11 |
|
FormatterOptions::DELIMITER => ',', |
69
|
11 |
|
FormatterOptions::CSV_ENCLOSURE => '"', |
70
|
|
|
FormatterOptions::CSV_ESCAPE_CHAR => "\\", |
71
|
11 |
|
]; |
72
|
|
|
} |
73
|
11 |
|
|
74
|
11 |
|
/** |
75
|
|
|
* @inheritdoc |
76
|
11 |
|
*/ |
77
|
|
|
public function write(OutputInterface $output, $data, FormatterOptions $options) |
78
|
11 |
|
{ |
79
|
11 |
|
$defaults = $this->getDefaultFormatterOptions(); |
80
|
11 |
|
|
81
|
11 |
|
$includeFieldLabels = $options->get(FormatterOptions::INCLUDE_FIELD_LABELS, $defaults); |
82
|
11 |
|
if ($includeFieldLabels && ($data instanceof TableTransformation)) { |
83
|
11 |
|
$headers = $data->getHeaders(); |
84
|
|
|
$this->writeOneLine($output, $headers, $options); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
foreach ($data as $line) { |
88
|
|
|
$this->writeOneLine($output, $line, $options); |
89
|
|
|
} |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Writes a single a single line of formatted CSV data to the output stream. |
94
|
|
|
* |
95
|
|
|
* @param OutputInterface $output the output stream to write to. |
96
|
|
|
* @param array $data an array of field data to convert to a CSV string. |
97
|
|
|
* @param FormatterOptions $options the specified options for this formatter. |
98
|
|
|
*/ |
99
|
|
|
protected function writeOneLine(OutputInterface $output, $data, $options) |
100
|
|
|
{ |
101
|
|
|
$defaults = $this->getDefaultFormatterOptions(); |
102
|
|
|
$delimiter = $options->get(FormatterOptions::DELIMITER, $defaults); |
103
|
|
|
$enclosure = $options->get(FormatterOptions::CSV_ENCLOSURE, $defaults); |
104
|
|
|
$escapeChar = $options->get(FormatterOptions::CSV_ESCAPE_CHAR, $defaults); |
105
|
|
|
$output->write($this->csvEscape($data, $delimiter, $enclosure, $escapeChar)); |
|
|
|
|
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Generates a CSV-escaped string from an array of field data. |
110
|
|
|
* |
111
|
|
|
* @param array $data an array of field data to format as a CSV. |
112
|
|
|
* @param string $delimiter the delimiter to use between fields. |
113
|
|
|
* @param string $enclosure character to use when enclosing complex fields. |
114
|
|
|
* @param string $escapeChar character to use when escaping special characters. |
115
|
|
|
* |
116
|
|
|
* @return string|bool the formatted CSV string, or FALSE if the formatting failed. |
117
|
|
|
*/ |
118
|
|
|
protected function csvEscape($data, $delimiter = ',', $enclosure = '"', $escapeChar = "\\") |
119
|
|
|
{ |
120
|
|
|
$buffer = fopen('php://temp', 'r+'); |
121
|
|
|
if (version_compare(PHP_VERSION, '5.5.4', '>=')) { |
122
|
|
|
fputcsv($buffer, $data, $delimiter, $enclosure, $escapeChar); |
123
|
|
|
} else { |
124
|
|
|
fputcsv($buffer, $data, $delimiter, $enclosure); |
125
|
|
|
} |
126
|
|
|
rewind($buffer); |
127
|
|
|
$csv = fgets($buffer); |
128
|
|
|
fclose($buffer); |
129
|
|
|
return $csv; |
130
|
|
|
} |
131
|
|
|
} |
132
|
|
|
|
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.