1 | <?php |
||
22 | class CsvFormatter implements FormatterInterface |
||
23 | { |
||
24 | use RowProcessor; |
||
25 | use InvokeFormatter; |
||
26 | |||
27 | /** @var CsvFormatInterface */ |
||
28 | private $csvFormat; |
||
29 | /** @var string[] */ |
||
30 | private $escapeChars; |
||
31 | /** @var string[] */ |
||
32 | private $replaceChars; |
||
33 | /** @var string */ |
||
34 | private $initial; |
||
35 | /** @var bool */ |
||
36 | private $first = true; |
||
37 | |||
38 | /** |
||
39 | * @param CsvFormatInterface $csvFormat |
||
40 | */ |
||
41 | 21 | public function __construct(CsvFormatInterface $csvFormat) |
|
53 | |||
54 | /** |
||
55 | * Build replacements to perform for each entry |
||
56 | */ |
||
57 | 21 | private function buildReplacements() |
|
58 | { |
||
59 | 21 | if ($this->csvFormat->getEscape()) { |
|
60 | 17 | $this->escapeChars = [ |
|
61 | 17 | $this->csvFormat->getEscape(), // escape escape first so that it doesn't re-escape later on |
|
62 | 17 | $this->csvFormat->getDelimiter(), |
|
63 | 17 | "\n", |
|
64 | 17 | "\r", |
|
65 | 17 | "\t", |
|
66 | ]; |
||
67 | 17 | if ($this->csvFormat->hasQuote() && !$this->csvFormat->useDoubleQuotes()) { |
|
68 | 14 | $this->escapeChars[] = $this->csvFormat->getQuote(); |
|
69 | 14 | } |
|
70 | |||
71 | 17 | $this->escapeChars = array_unique($this->escapeChars); |
|
72 | |||
73 | 17 | $this->replaceChars = array_map(function ($char) { |
|
74 | 17 | return $this->csvFormat->getEscape() . $char; |
|
75 | 17 | }, $this->escapeChars); |
|
76 | 17 | } |
|
77 | |||
78 | 21 | if ($this->csvFormat->hasQuote() && $this->csvFormat->useDoubleQuotes()) { |
|
79 | 2 | $this->escapeChars[] = $this->csvFormat->getQuote(); |
|
80 | 2 | $this->replaceChars[] = str_repeat($this->csvFormat->getQuote(), 2); |
|
81 | 2 | } |
|
82 | 21 | } |
|
83 | |||
84 | /** |
||
85 | * @param array $data |
||
86 | * |
||
87 | * @return string |
||
88 | */ |
||
89 | 14 | public function format(array $data) |
|
90 | { |
||
91 | 14 | $prefix = ''; |
|
92 | 14 | if ($this->first && $this->csvFormat->hasHeaderRow()) { |
|
93 | 3 | $this->first = false; |
|
94 | 3 | $postHeaderPad = $this->csvFormat->getDataStart() - $this->csvFormat->getHeaderRow(); |
|
95 | 3 | $prefix = $this->format(array_keys($data)) . str_repeat($this->getRowSeparator(), $postHeaderPad); |
|
96 | 3 | } |
|
97 | |||
98 | 14 | $data = $this->process($data); |
|
99 | |||
100 | 14 | foreach ($data as &$element) { |
|
101 | 14 | if (is_null($element)) { |
|
102 | 4 | $element = $this->csvFormat->getNullValue(); |
|
103 | 4 | } else { |
|
104 | 14 | $element = $this->csvFormat->getQuote() . $this->escape($element) . $this->csvFormat->getQuote(); |
|
105 | } |
||
106 | 14 | } |
|
107 | |||
108 | 14 | return $prefix . $this->encode(implode($this->csvFormat->getDelimiter(), $data)); |
|
109 | } |
||
110 | |||
111 | /** |
||
112 | * @param string $string |
||
113 | * |
||
114 | * @return string |
||
115 | */ |
||
116 | 14 | protected function escape($string) |
|
120 | |||
121 | /** |
||
122 | * @param string $string |
||
123 | * |
||
124 | * @return string |
||
125 | */ |
||
126 | 18 | private function encode($string) |
|
130 | |||
131 | /** |
||
132 | * Return an initial block if required |
||
133 | * |
||
134 | * @return string |
||
135 | */ |
||
136 | 6 | public function getInitialBlock() |
|
144 | |||
145 | /** |
||
146 | * Get a separator between each row |
||
147 | * |
||
148 | * @return string |
||
149 | */ |
||
150 | 10 | public function getRowSeparator() |
|
154 | |||
155 | /** |
||
156 | * Return a closing block if required |
||
157 | * |
||
158 | * @return string |
||
159 | */ |
||
160 | 6 | public function getClosingBlock() |
|
164 | } |
||
165 |