Test Failed
Push — develop ( c11a27...e53328 )
by nguereza
03:40
created

CsvReader::setEscape()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
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
     * The CSV field enclosure
97
     * @var string
98
     */
99
    protected string $enclosure = '"';
100
101
    /**
102
     * The escape character
103
     * @var string
104
     */
105
    protected string $escape = '\\';
106
107
    /**
108
     * Return the data list
109
     * @return array<int, array<string, mixed>>
110
     */
111
    public function all(): array
112
    {
113
        return $this->data;
114
    }
115
116
    /**
117
     * Return the total of CSV rows
118
     * @return int
119
     */
120
    public function count(): int
121
    {
122
        return count($this->data);
123
    }
124
125
    /**
126
     * Return the header list
127
     * @return array<string>
128
     */
129
    public function getHeaders(): array
130
    {
131
        return $this->headers;
132
    }
133
134
    /**
135
     * Return the limit
136
     * @return int<0, max>
137
     */
138
    public function getLimit(): int
139
    {
140
        return $this->limit;
141
    }
142
143
    /**
144
     * Return the delimiter
145
     * @return string
146
     */
147
    public function getDelimiter(): string
148
    {
149
        return $this->delimiter;
150
    }
151
152
    /**
153
     * Set the file to be parsed
154
     * @param string $file
155
     * @return $this
156
     */
157
    public function setFile(string $file): self
158
    {
159
        $this->checkFile($file);
160
161
        $this->file = $file;
162
        return $this;
163
    }
164
165
    /**
166
     * The parse limit
167
     * @param int<0, max> $limit
168
     * @return $this
169
     */
170
    public function setLimit(int $limit): self
171
    {
172
        $this->limit = $limit;
173
        return $this;
174
    }
175
176
    /**
177
     * Set the field delimiter
178
     * @param string $delimiter
179
     * @return $this
180
     */
181
    public function setDelimiter(string $delimiter): self
182
    {
183
        if (in_array($delimiter, $this->validDelimiters) === false) {
184
            throw new InvalidArgumentException(sprintf(
185
                'Invalid delimiter [%s], must be one of [%s]',
186
                $delimiter,
187
                implode(',', $this->validDelimiters)
188
            ));
189
        }
190
        $this->delimiter = $delimiter;
191
192
        return $this;
193
    }
194
195
    /**
196
     * Return the field enclosure
197
     * @return string
198
     */
199
    public function getEnclosure(): string
200
    {
201
        return $this->enclosure;
202
    }
203
204
    /**
205
     * Return the escape character
206
     * @return string
207
     */
208
    public function getEscape(): string
209
    {
210
        return $this->escape;
211
    }
212
213
    /**
214
     * Set field enclosure
215
     * @param string $enclosure
216
     * @return $this
217
     */
218
    public function setEnclosure(string $enclosure): self
219
    {
220
        $this->enclosure = $enclosure;
221
        return $this;
222
    }
223
224
    /**
225
     * Set the escape character
226
     * @param string $escape
227
     * @return $this
228
     */
229
    public function setEscape(string $escape): self
230
    {
231
        $this->escape = $escape;
232
        return $this;
233
    }
234
235
236
237
    /**
238
     * Parse the CSV file
239
     * @return $this
240
     */
241
    public function parse(): self
242
    {
243
        $fp = fopen($this->file, 'r');
244
        if ($fp === false) {
245
            throw new InvalidArgumentException(sprintf(
246
                'The file [%s] does not exist or is not readable',
247
                $this->file
248
            ));
249
        }
250
251
        $i = 0;
252
        while (
253
            ($data = fgetcsv(
254
                $fp,
255
                $this->limit,
256
                $this->delimiter,
257
                $this->enclosure,
258
                $this->escape
259
            )) !== false
260
        ) {
261
            // skip all empty lines
262
            if ($data[0] !== null) {
263
                if ($i === 0) {
264
                    $this->headers = array_map([$this, 'sanitize'], $data);
265
                } else {
266
                    $this->data[] = array_combine($this->headers, $data);
267
                }
268
269
                $i++;
270
            }
271
        }
272
273
        fclose($fp);
274
275
        return $this;
276
    }
277
278
    /**
279
     * Validate the given file
280
     * @param string $file
281
     * @return void
282
     */
283
    protected function checkFile(string $file): void
284
    {
285
        if (file_exists($file) === false) {
286
            throw new InvalidArgumentException(sprintf(
287
                'The file [%s] does not exist',
288
                $file
289
            ));
290
        }
291
    }
292
293
    /**
294
     * Sanitize the given string
295
     * @param string $value
296
     * @return string
297
     */
298
    private function sanitize(string $value): string
299
    {
300
        return (string) preg_replace('/\xEF\xBB\xBF/', '', $value);
301
    }
302
}
303