Passed
Push — develop ( b53b24...004a44 )
by nguereza
02:57
created

CsvReader::parse()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 17
c 1
b 0
f 0
nc 6
nop 0
dl 0
loc 30
rs 9.0777
1
<?php
2
3
/**
4
 * Platine Framework
5
 *
6
 * Platine Framework is a lightweight, high-performance, simple and elegant
7
 * PHP Web framework
8
 *
9
 * This content is released under the MIT License (MIT)
10
 *
11
 * Copyright (c) 2020 Platine Framework
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
/**
33
 *  @file CsvReader.php
34
 *
35
 *  The CSV Parser reader class
36
 *
37
 *  @package    Platine\Framework\Helper
38
 *  @author Platine Developers team
39
 *  @copyright  Copyright (c) 2020
40
 *  @license    http://opensource.org/licenses/MIT  MIT License
41
 *  @link   https://www.platine-php.com
42
 *  @version 1.0.0
43
 *  @filesource
44
 */
45
46
declare(strict_types=1);
47
48
namespace Platine\Framework\Helper;
49
50
use InvalidArgumentException;
51
52
/**
53
 * @class CsvReader
54
 * @package Platine\Framework\Helper
55
 */
56
class CsvReader
57
{
58
    /**
59
     * The valid delimiters list
60
     * @var array<string>
61
     */
62
    protected array $validDelimiters = [',', ';', "\t", '|', ':'];
63
64
    /**
65
     * The CSV file to be used
66
     * @var string
67
     */
68
    protected string $file;
69
70
    /**
71
     * The parsed CSV data
72
     * @var array<int, array<string, mixed>>
73
     */
74
    protected array $data = [];
75
76
    /**
77
     * The headers columns names
78
     * @var array<string>
79
     */
80
    protected array $headers = [];
81
82
    /**
83
     * Setting to 0 makes the maximum
84
     * line length not limited
85
     * @var int<0, max>
86
     */
87
    protected int $limit = 0;
88
89
    /**
90
     * The CSV delimiter for each field
91
     * @var string
92
     */
93
    protected string $delimiter = ';';
94
95
    /**
96
     * Return the data list
97
     * @return array<int, array<string, mixed>>
98
     */
99
    public function all(): array
100
    {
101
        return $this->data;
102
    }
103
104
    /**
105
     * Return the total of CSV rows
106
     * @return int
107
     */
108
    public function count(): int
109
    {
110
        return count($this->data);
111
    }
112
113
    /**
114
     * Return the header list
115
     * @return array<string>
116
     */
117
    public function getHeaders(): array
118
    {
119
        return $this->headers;
120
    }
121
122
    /**
123
     * Return the limit
124
     * @return int<0, max>
125
     */
126
    public function getLimit(): int
127
    {
128
        return $this->limit;
129
    }
130
131
    /**
132
     * Return the delimiter
133
     * @return string
134
     */
135
    public function getDelimiter(): string
136
    {
137
        return $this->delimiter;
138
    }
139
140
    /**
141
     * Set the file to be parsed
142
     * @param string $file
143
     * @return $this
144
     */
145
    public function setFile(string $file): self
146
    {
147
        $this->checkFile($file);
148
149
        $this->file = $file;
150
        return $this;
151
    }
152
153
    /**
154
     * The parse limit
155
     * @param int<0, max> $limit
156
     * @return $this
157
     */
158
    public function setLimit(int $limit): self
159
    {
160
        $this->limit = $limit;
161
        return $this;
162
    }
163
164
    /**
165
     * Set the field delimiter
166
     * @param string $delimiter
167
     * @return $this
168
     */
169
    public function setDelimiter(string $delimiter): self
170
    {
171
        if (!in_array($delimiter, $this->validDelimiters)) {
172
            throw new InvalidArgumentException(sprintf(
173
                'Invalid delimiter [%s], must be one of [%s]',
174
                $delimiter,
175
                implode(',', $this->validDelimiters)
176
            ));
177
        }
178
        $this->delimiter = $delimiter;
179
        return $this;
180
    }
181
182
183
    /**
184
     * Parse the CSV file
185
     * @return $this
186
     */
187
    public function parse(): self
188
    {
189
        $fp = fopen($this->file, 'r');
190
        if ($fp === false) {
191
            throw new InvalidArgumentException(sprintf(
192
                'The file [%s] does not exist or readable',
193
                $this->file
194
            ));
195
        }
196
197
        $i = 0;
198
        while (($data = fgetcsv($fp, $this->limit, $this->delimiter)) !== false) {
199
            // skip all empty lines
200
            if ($data[0] !== null) {
201
                if ($i === 0) {
202
                    $this->headers = array_map([$this, 'sanitize'], $data);
203
                } else {
204
                    $result = array_combine($this->headers, $data);
205
                    if ($result !== false) {
206
                        $this->data[] = $result;
207
                    }
208
                }
209
210
                $i++;
211
            }
212
        }
213
214
        fclose($fp);
215
216
        return $this;
217
    }
218
219
    /**
220
     * Validate the given file
221
     * @param string $file
222
     * @return void
223
     */
224
    protected function checkFile(string $file): void
225
    {
226
        if (file_exists($file) === false) {
227
            throw new InvalidArgumentException(sprintf(
228
                'The file [%s] does not exist',
229
                $file
230
            ));
231
        }
232
    }
233
234
    /**
235
     * Sanitize the given string
236
     * @param string $value
237
     * @return string
238
     */
239
    private function sanitize(string $value): string
240
    {
241
        return (string) preg_replace('/\xEF\xBB\xBF/', '', $value);
242
    }
243
}
244