CsvTrait::readCsv()   A
last analyzed

Complexity

Conditions 4
Paths 19

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 12
c 2
b 1
f 0
dl 0
loc 18
rs 9.8666
cc 4
nc 19
nop 2
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * BEdita, API-first content management framework
6
 * Copyright 2023 Atlas Srl, Chialab Srl
7
 *
8
 * This file is part of BEdita: you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published
10
 * by the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
14
 */
15
namespace BEdita\ImportTools\Utility;
16
17
use Cake\Core\InstanceConfigTrait;
18
19
/**
20
 * Trait for share Csv stuff.
21
 *
22
 * This provides `readCsv` method to progressively read a csv file line by line.
23
 *
24
 * Usage example:
25
 * ```php
26
 * use BEdita\ImportTools\Utility\CsvTrait;
27
 *
28
 * class MyImporter
29
 * {
30
 *     use CsvTrait;
31
 *
32
 *     public function import(string $filename): void
33
 *     {
34
 *         foreach ($this->readCsv($filename) as $obj) {
35
 *             // process $obj
36
 *         }
37
 *     }
38
 * }
39
 * ```
40
 */
41
trait CsvTrait
42
{
43
    use InstanceConfigTrait;
44
    use FileTrait;
45
46
    /**
47
     * Progressively read a CSV file, line by line
48
     *
49
     * @param string $path Path to CSV file
50
     * @param bool $assoc If `true` uses first CSV row as column names, thus yielding associative arrays. Otherwise, all rows are yielded and columns are indexed by their positions.
51
     * @return \Generator<array<array-key, string>>
52
     */
53
    protected function readCsv(string $path, bool $assoc = true): \Generator
54
    {
55
        $delimiter = $this->getConfig('csv.delimiter', ',');
56
        $enclosure = $this->getConfig('csv.enclosure', '"');
57
        $escape = $this->getConfig('csv.escape', '\\');
58
59
        [$fh, $close] = static::readFileStream($path);
60
61
        try {
62
            flock($fh, LOCK_SH);
63
64
            $header = $assoc ? fgetcsv($fh, 0, $delimiter, $enclosure, $escape) : null;
0 ignored issues
show
Bug introduced by
It seems like $enclosure can also be of type array<string,mixed> and null; however, parameter $enclosure of fgetcsv() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

64
            $header = $assoc ? fgetcsv($fh, 0, $delimiter, /** @scrutinizer ignore-type */ $enclosure, $escape) : null;
Loading history...
Bug introduced by
It seems like $delimiter can also be of type array<string,mixed> and null; however, parameter $separator of fgetcsv() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

64
            $header = $assoc ? fgetcsv($fh, 0, /** @scrutinizer ignore-type */ $delimiter, $enclosure, $escape) : null;
Loading history...
Bug introduced by
It seems like $escape can also be of type array<string,mixed> and null; however, parameter $escape of fgetcsv() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

64
            $header = $assoc ? fgetcsv($fh, 0, $delimiter, $enclosure, /** @scrutinizer ignore-type */ $escape) : null;
Loading history...
65
            $i = 0;
66
            while (($row = fgetcsv($fh, 0, $delimiter, $enclosure, $escape)) !== false) {
67
                yield $i++ => $header !== null ? array_combine($header, $row) : $row;
68
            }
69
        } finally {
70
            $close();
71
        }
72
    }
73
}
74