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