1 | <?php |
||
23 | class CsvFormatter implements FormatterInterface |
||
24 | { |
||
25 | use RowProcessor; |
||
26 | use InvokeFormatter; |
||
27 | use TraversableTrait; |
||
28 | |||
29 | /** @var CsvFormatInterface */ |
||
30 | private $csvFormat; |
||
31 | /** @var string[] */ |
||
32 | private $escapeChars; |
||
33 | /** @var string[] */ |
||
34 | private $replaceChars; |
||
35 | /** @var string */ |
||
36 | private $initial; |
||
37 | /** @var bool */ |
||
38 | private $first = true; |
||
39 | |||
40 | /** |
||
41 | * @param CsvFormatInterface $csvFormat |
||
42 | */ |
||
43 | 22 | public function __construct(CsvFormatInterface $csvFormat) |
|
55 | |||
56 | /** |
||
57 | * Build replacements to perform for each entry |
||
58 | */ |
||
59 | 22 | private function buildReplacements() |
|
60 | { |
||
61 | 22 | if ($this->csvFormat->getEscape()) { |
|
62 | 18 | $this->escapeChars = [ |
|
63 | 18 | $this->csvFormat->getEscape(), // escape escape first so that it doesn't re-escape later on |
|
64 | 18 | $this->csvFormat->getDelimiter(), |
|
65 | 18 | "\n", |
|
66 | 18 | "\r", |
|
67 | 18 | "\t", |
|
68 | ]; |
||
69 | 18 | if ($this->csvFormat->hasQuote() && !$this->csvFormat->useDoubleQuotes()) { |
|
70 | 15 | $this->escapeChars[] = $this->csvFormat->getQuote(); |
|
71 | } |
||
72 | |||
73 | 18 | $this->escapeChars = array_unique($this->escapeChars); |
|
74 | |||
75 | 18 | $this->replaceChars = array_map(function ($char) { |
|
76 | 18 | return $this->csvFormat->getEscape() . $char; |
|
77 | 18 | }, $this->escapeChars); |
|
78 | } |
||
79 | |||
80 | 22 | if ($this->csvFormat->hasQuote() && $this->csvFormat->useDoubleQuotes()) { |
|
81 | 2 | $this->escapeChars[] = $this->csvFormat->getQuote(); |
|
82 | 2 | $this->replaceChars[] = str_repeat($this->csvFormat->getQuote(), 2); |
|
83 | } |
||
84 | 22 | } |
|
85 | |||
86 | /** |
||
87 | * Gets a prefix for headers if required |
||
88 | * |
||
89 | * @param array $data |
||
90 | * |
||
91 | * @return string |
||
92 | */ |
||
93 | 14 | private function getHeaderPrefix(array $data) |
|
102 | |||
103 | /** |
||
104 | * @param array|Traversable $row |
||
105 | * |
||
106 | * @return string |
||
107 | */ |
||
108 | 15 | public function format($row) |
|
109 | { |
||
110 | 15 | $data = $this->getArray($row); |
|
111 | 14 | $prefix = $this->getHeaderPrefix($data); |
|
112 | 14 | $data = $this->process($data); |
|
113 | |||
114 | 14 | foreach ($data as &$element) { |
|
115 | 14 | if (is_null($element)) { |
|
116 | 4 | $element = $this->csvFormat->getNullValue(); |
|
117 | } else { |
||
118 | 14 | $element = $this->csvFormat->getQuote() . $this->escape($element) . $this->csvFormat->getQuote(); |
|
119 | } |
||
120 | } |
||
121 | |||
122 | 14 | return $prefix . $this->encode(implode($this->csvFormat->getDelimiter(), $data)); |
|
123 | } |
||
124 | |||
125 | /** |
||
126 | * @param string $string |
||
127 | * |
||
128 | * @return string |
||
129 | */ |
||
130 | 14 | protected function escape($string) |
|
134 | |||
135 | /** |
||
136 | * @param string $string |
||
137 | * |
||
138 | * @return string |
||
139 | */ |
||
140 | 18 | private function encode($string) |
|
144 | |||
145 | /** |
||
146 | * Return an initial block if required |
||
147 | * |
||
148 | * @return string |
||
149 | */ |
||
150 | 6 | public function getInitialBlock() |
|
158 | |||
159 | /** |
||
160 | * Get a separator between each row |
||
161 | * |
||
162 | * @return string |
||
163 | */ |
||
164 | 10 | public function getRowSeparator() |
|
168 | |||
169 | /** |
||
170 | * Return a closing block if required |
||
171 | * |
||
172 | * @return string |
||
173 | */ |
||
174 | 6 | public function getClosingBlock() |
|
178 | } |
||
179 |