Completed
Push — master ( c4412d...290452 )
by ignace nyamagana
02:58
created

Converter::itemToElement()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 2
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
* This file is part of the League.csv library
4
*
5
* @license http://opensource.org/licenses/MIT
6
* @link https://github.com/thephpleague/csv/
7
* @version 9.0.0
8
* @package League.csv
9
*
10
* For the full copyright and license information, please view the LICENSE
11
* file that was distributed with this source code.
12
*/
13
declare(strict_types=1);
14
15
namespace League\Csv;
16
17
use DOMDocument;
18
use DOMElement;
19
use Iterator;
20
use League\Csv\Exception\InvalidArgumentException;
21
use Traversable;
22
23
/**
24
 * A class to convert CSV records into a DOMDOcument object
25
 *
26
 * @package League.csv
27
 * @since   9.0.0
28
 * @author  Ignace Nyamagana Butera <[email protected]>
29
 * @internal used internally to returns the DOMDocument object representation of a CSV document
30
 *
31
 */
32
class Converter
33
{
34
    use ValidatorTrait;
35
36
    /**
37
     * XML Root name
38
     *
39
     * @var string
40
     */
41
    protected $root_name = 'csv';
42
43
    /**
44
     * XML Node name
45
     *
46
     * @var string
47
     */
48
    protected $record_name = 'row';
49
50
    /**
51
     * XML Item name
52
     *
53
     * @var string
54
     */
55
    protected $item_name = 'cell';
56
57
    /**
58
     * XML column attribute name
59
     *
60
     * @var string
61
     */
62
    protected $column_attr = 'name';
63
64
    /**
65
     * XML offset attribute name
66
     *
67
     * @var string
68
     */
69
    protected $offset_attr = 'offset';
70
71
    /**
72
     * Tell whether to preserve offset
73
     *
74
     * @var bool
75
     */
76
    protected $preserve_offset = false;
77
78
    /**
79
     * Charset Encoding for the CSV
80
     *
81
     * This information is used when converting the CSV to XML or JSON
82
     *
83
     * @var string
84
     */
85
    protected $input_encoding = 'UTF-8';
86
87
    /**
88
     * Conversion method list
89
     *
90
     * @var array
91
     */
92
    protected $encoder = [
93
        'item' => [
94
            true => 'itemToElementWithAttribute',
95
            false => 'itemToElement',
96
        ],
97
        'record' => [
98
            true => 'recordToElementWithAttribute',
99
            false => 'recordToElement',
100
        ],
101
    ];
102
103
    /**
104
     * XML Root name setter
105
     *
106
     * @param string $root_name
107
     *
108
     * @return self
109
     */
110 6
    public function rootName(string $root_name): self
111
    {
112 6
        if ($root_name === $this->root_name) {
113 2
            return $this;
114
        }
115
116 4
        $clone = clone $this;
117 4
        $clone->root_name = $root_name;
118
119 4
        return $clone;
120
    }
121
122
    /**
123
     * XML Record name setter
124
     *
125
     * @param string $record_name
126
     *
127
     * @return self
128
     */
129 6
    public function recordName(string $record_name): self
130
    {
131 6
        if ($record_name === $this->record_name) {
132 2
            return $this;
133
        }
134
135 4
        $clone = clone $this;
136 4
        $clone->record_name = $record_name;
137
138 4
        return $clone;
139
    }
140
141
    /**
142
     * XML Item name setter
143
     *
144
     * @param string $item_name
145
     *
146
     * @return self
147
     */
148 6
    public function itemName(string $item_name): self
149
    {
150 6
        if ($item_name === $this->item_name) {
151 2
            return $this;
152
        }
153
154 4
        $clone = clone $this;
155 4
        $clone->item_name = $item_name;
156
157 4
        return $clone;
158
    }
159
160
    /**
161
     * XML Column attribute name setter
162
     *
163
     * @param string $column_attr
164
     *
165
     * @return self
166
     */
167 4
    public function columnAttributeName(string $column_attr): self
168
    {
169 4
        if ($column_attr === $this->column_attr) {
170 2
            return $this;
171
        }
172
173 2
        $clone = clone $this;
174 2
        $clone->column_attr = $column_attr;
175
176 2
        return $clone;
177
    }
178
179
    /**
180
     * XML Offset attribute name setter
181
     *
182
     * @param string $offset_attr
183
     *
184
     * @return self
185
     */
186 4
    public function offsetAttributeName(string $offset_attr): self
187
    {
188 4
        if ($offset_attr === $this->offset_attr) {
189 2
            return $this;
190
        }
191
192 2
        $clone = clone $this;
193 2
        $clone->offset_attr = $offset_attr;
194
195 2
        return $clone;
196
    }
197
198
199
    /**
200
     * Whether we should preserve the CSV records keys.
201
     *
202
     * If set to true CSV document record keys will added to
203
     * the conversion output .
204
     *
205
     * @param bool $status
206
     *
207
     * @return self
208
     */
209 6
    public function preserveOffset(bool $status): self
210
    {
211 6
        if ($status === $this->preserve_offset) {
212 2
            return $this;
213
        }
214
215 4
        $clone = clone $this;
216 4
        $clone->preserve_offset = $status;
217
218 4
        return $clone;
219
    }
220
221
    /**
222
     * Sets the CSV encoding charset
223
     *
224
     * @param string $str
225
     *
226
     * @throws InvalidArgumentException if the charset is empty
227
     *
228
     * @return static
229
     */
230 12
    public function inputEncoding(string $str): self
231
    {
232 12
        $str = str_replace('_', '-', $str);
233 12
        $str = filter_var($str, FILTER_SANITIZE_STRING, ['flags' => FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH]);
234 12
        $str = trim($str);
235 12
        if ('' === $str) {
236 2
            throw new InvalidArgumentException('you should use a valid charset');
237
        }
238 10
        $input_encoding = strtoupper($str);
239 10
        if ($this->input_encoding == $input_encoding) {
240 2
            return $this;
241
        }
242
243 8
        $clone = clone $this;
244 8
        $clone->input_encoding = $input_encoding;
245
246 8
        return $clone;
247
    }
248
249
    /**
250
     * Convert Csv file into UTF-8
251
     *
252
     * @param array|Traversable $records the CSV records collection
253
     *
254
     * @return array|Iterator
255
     */
256 6
    protected function convertToUtf8($records)
257
    {
258 6
        if (stripos($this->input_encoding, 'UTF-8') !== false) {
259 2
            return $records;
260
        }
261
262
        $walker = function (&$value, &$offset) {
263 4
            $value = mb_convert_encoding((string) $value, 'UTF-8', $this->input_encoding);
264 4
            $offset = mb_convert_encoding((string) $offset, 'UTF-8', $this->input_encoding);
265 4
        };
266
267 4
        $convert = function (array $record) use ($walker): array {
268 4
            array_walk($record, $walker);
269 4
            return $record;
270 4
        };
271
272 4
        return is_array($records) ? array_map($convert, $records) : new MapIterator($records, $convert);
273
    }
274
275
    /**
276
     * Convert an Record collection into a DOMDocument
277
     *
278
     * @param array|Traversable $records the CSV records collection
279
     *
280
     * @return DOMDocument
281
     */
282 4
    public function toXML($records): DOMDocument
283
    {
284 4
        $item_encoder = $this->encoder['item'][$this->preserve_offset];
285 4
        $record_encoder = $this->encoder['record'][$this->preserve_offset];
286 4
        $doc = new DOMDocument('1.0', 'UTF-8');
287 4
        $root = $doc->createElement($this->root_name);
288 4
        $records = $this->convertToUtf8($this->filterIterable($records, __METHOD__));
289 4
        foreach ($records as $offset => $record) {
290 4
            $node = $this->{$record_encoder}($doc, $record, $item_encoder, $offset);
291 4
            $root->appendChild($node);
292
        }
293 4
        $doc->appendChild($root);
294
295 4
        return $doc;
296
    }
297
298
    /**
299
     * Convert an Record collection into a Json string
300
     *
301
     * @param array|Traversable $records the CSV records collection
302
     *
303
     * @return string
304
     */
305 2
    public function toJson($records, int $options = 0, int $depth = 512): string
306
    {
307 2
        $records = $this->convertToUtf8($this->filterIterable($records, __METHOD__));
308 2
        if (!is_array($records)) {
309 2
            return json_encode(iterator_to_array($records, $this->preserve_offset), $options, $depth);
310
        }
311
312 2
        return json_encode(!$this->preserve_offset ? array_values($records) : $records, $options, $depth);
313
    }
314
315
    /**
316
     * Convert an Record collection into a HTML table
317
     *
318
     * @param array|Traversable $records the CSV records collection
319
     *
320
     * @return DOMDocument
321
     */
322 2
    public function toHTML($records, string $class_attr = 'table-csv-data'): string
323
    {
324 2
        $doc = $this->rootName('table')->recordName('tr')->itemName('td')->toXML($records);
325 2
        $doc->documentElement->setAttribute('class', $class_attr);
326
327 2
        return $doc->saveHTML($doc->documentElement);
328
    }
329
330
    /**
331
     * Convert a CSV record into a DOMElement and
332
     * adds its offset as DOMElement attribute
333
     *
334
     * @param DOMDocument $doc
335
     * @param array       $record       CSV record
336
     * @param string      $item_encoder CSV Cell encoder method name
337
     * @param int         $offset       CSV record offset
338
     *
339
     * @return DOMElement
340
     */
341 2
    protected function recordToElementWithAttribute(DOMDocument $doc, array $record, string $item_encoder, int $offset): DOMElement
342
    {
343 2
        $node = $this->recordToElement($doc, $record, $item_encoder);
344 2
        $node->setAttribute($this->offset_attr, (string) $offset);
345
346 2
        return $node;
347
    }
348
349
    /**
350
     * Convert a CSV record into a DOMElement
351
     *
352
     * @param DOMDocument $doc
353
     * @param array       $record       CSV record
354
     * @param string      $item_encoder CSV Cell encoder method name
355
     *
356
     * @return DOMElement
357
     */
358 4
    protected function recordToElement(DOMDocument $doc, array $record, string $item_encoder): DOMElement
359
    {
360 4
        $node = $doc->createElement($this->record_name);
361 4
        foreach ($record as $name => $value) {
362 4
            $item = $this->{$item_encoder}($doc, $value, $name);
363 4
            $node->appendChild($item);
364
        }
365
366 4
        return $node;
367
    }
368
369
    /**
370
     * Convert Cell to Item.
371
     *
372
     * Convert the CSV item into a DOMElement and adds the item offset
373
     * as attribute to the returned DOMElement
374
     *
375
     * @param DOMDocument $doc
376
     * @param string      $value Record item value
377
     * @param int|string  $name  Record item offset
378
     *
379
     * @return DOMElement
380
     */
381 2
    protected function itemToElementWithAttribute(DOMDocument $doc, string $value, $name): DOMElement
382
    {
383 2
        $item = $this->itemToElement($doc, $value);
384 2
        $item->setAttribute($this->column_attr, (string) $name);
385
386 2
        return $item;
387
    }
388
389
    /**
390
     * Convert Cell to Item
391
     *
392
     * @param DOMDocument $doc
393
     * @param string      $value Record item value
394
     *
395
     * @return DOMElement
396
     */
397 4
    protected function itemToElement(DOMDocument $doc, string $value): DOMElement
398
    {
399 4
        $item = $doc->createElement($this->item_name);
400 4
        $item->appendChild($doc->createTextNode($value));
401
402 4
        return $item;
403
    }
404
}
405