Completed
Pull Request — master (#178)
by ignace nyamagana
12:35
created

Controls::validateInteger()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 3
crap 2
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 8.1.1
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
namespace League\Csv\Config;
14
15
use CallbackFilterIterator;
16
use InvalidArgumentException;
17
use League\Csv\AbstractCsv;
18
use LimitIterator;
19
use SplFileObject;
20
21
/**
22
 *  A trait to configure and check CSV file and content
23
 *
24
 * @package League.csv
25
 * @since  6.0.0
26
 *
27
 */
28
trait Controls
29
{
30
    use Validator;
31
32
    use StreamFilter;
33
34
    /**
35
     * the field delimiter (one character only)
36
     *
37
     * @var string
38
     */
39
    protected $delimiter = ',';
40
41
    /**
42
     * the field enclosure character (one character only)
43
     *
44
     * @var string
45
     */
46
    protected $enclosure = '"';
47
48
    /**
49
     * the field escape character (one character only)
50
     *
51
     * @var string
52
     */
53
    protected $escape = '\\';
54
55
    /**
56
     * newline character
57
     *
58
     * @var string
59
     */
60
    protected $newline = "\n";
61
62
    /**
63
     * Charset Encoding for the CSV
64
     *
65
     * @var string
66 3
     */
67
    protected $input_encoding = 'UTF-8';
68 3
69 3
    /**
70
     * The Input file BOM character
71 3
     *
72
     * @var string|null
73 3
     */
74
    protected $input_bom;
75
76
    /**
77
     * The Output file BOM character
78
     * @var string
79
     */
80
    protected $output_bom = '';
81
82
    /**
83 18
     * Sets the field delimiter
84
     *
85 18
     * @param string $delimiter
86
     *
87
     * @throws InvalidArgumentException If $delimiter is not a single character
88
     *
89
     * @return $this
90
     */
91
    public function setDelimiter($delimiter)
92
    {
93 3
        if (!$this->isValidCsvControls($delimiter)) {
94
            throw new InvalidArgumentException('The delimiter must be a single character');
95 3
        }
96
        $this->delimiter = $delimiter;
97
98
        return $this;
99
    }
100
101
    /**
102
     * Tell whether the submitted string is a valid CSV Control character
103
     *
104
     * @param string $str The submitted string
105
     *
106
     * @return bool
107
     */
108
    protected function isValidCsvControls($str)
109 12
    {
110
        return 1 == mb_strlen($str);
111 12
    }
112 9
113 9
    /**
114 9
     * Returns the current field delimiter
115 9
     *
116 9
     * @return string
117 9
     */
118 9
    public function getDelimiter()
119 9
    {
120 9
        return $this->delimiter;
121 9
    }
122 6
123 9
    /**
124
     * Detect Delimiters occurences in the CSV
125 9
     *
126
     * Returns a associative array where each key represents
127
     * a valid delimiter and each value the number of occurences
128
     *
129
     * @param string[] $delimiters the delimiters to consider
130
     * @param int      $nb_rows    Detection is made using $nb_rows of the CSV
131
     *
132
     * @return array
133
     */
134
    public function fetchDelimitersOccurrence(array $delimiters, $nb_rows = 1)
135
    {
136
        $nb_rows = $this->validateInteger($nb_rows, 1, 'The number of rows to consider must be a valid positive integer');
137
        $filter_row = function ($row) {
138
            return is_array($row) && count($row) > 1;
139 114
        };
140
        $delimiters = array_unique(array_filter($delimiters, [$this, 'isValidCsvControls']));
141 114
        $csv = $this->getIterator();
142 18
        $res = [];
143
        foreach ($delimiters as $delim) {
144 99
            $csv->setCsvControl($delim, $this->enclosure, $this->escape);
145
            $iterator = new CallbackFilterIterator(new LimitIterator($csv, 0, $nb_rows), $filter_row);
146
            $res[$delim] = count(iterator_to_array($iterator, false), COUNT_RECURSIVE);
147
        }
148
        arsort($res, SORT_NUMERIC);
149
150
        return $res;
151
    }
152
153
    /**
154
     * Returns the CSV Iterator
155
     *
156
     * @return SplFileObject
157
     */
158
    abstract public function getIterator();
159
160
    /**
161
     * Sets the field enclosure
162
     *
163 3
     * @param string $enclosure
164
     *
165 3
     * @throws InvalidArgumentException If $enclosure is not a single character
166 3
     *
167
     * @return $this
168 3
     */
169
    public function setEnclosure($enclosure)
170 3
    {
171
        if (!$this->isValidCsvControls($enclosure)) {
172
            throw new InvalidArgumentException('The enclosure must be a single character');
173
        }
174
        $this->enclosure = $enclosure;
175
176
        return $this;
177
    }
178 24
179
    /**
180 24
     * Returns the current field enclosure
181
     *
182
     * @return string
183
     */
184
    public function getEnclosure()
185
    {
186
        return $this->enclosure;
187
    }
188
189
    /**
190
     * Sets the field escape character
191
     *
192 3
     * @param string $escape
193
     *
194 3
     * @throws InvalidArgumentException If $escape is not a single character
195 3
     *
196
     * @return $this
197 3
     */
198
    public function setEscape($escape)
199 3
    {
200
        if (!$this->isValidCsvControls($escape)) {
201
            throw new InvalidArgumentException('The escape character must be a single character');
202
        }
203
        $this->escape = $escape;
204
205
        return $this;
206
    }
207 3
208
    /**
209 3
     * Returns the current field escape character
210
     *
211
     * @return string
212
     */
213
    public function getEscape()
214
    {
215
        return $this->escape;
216
    }
217
218
    /**
219 6
     * Sets the newline sequence characters
220
     *
221 6
     * @param string $newline
222
     *
223 6
     * @return static
224
     */
225
    public function setNewline($newline)
226
    {
227
        $this->newline = (string) $newline;
228
229
        return $this;
230
    }
231 6
232
    /**
233 6
     * Returns the current newline sequence characters
234
     *
235
     * @return string
236
     */
237
    public function getNewline()
238
    {
239
        return $this->newline;
240
    }
241
242
    /**
243
     * Outputs all data on the CSV file
244
     *
245
     * @param string $filename CSV downloaded name if present adds extra headers
246
     *
247
     * @return int Returns the number of characters read from the handle
248
     *             and passed through to the output.
249
     */
250
    public function output($filename = null)
251
    {
252
        if (null !== $filename) {
253
            $filename = filter_var($filename, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
254
            header('Content-Type: text/csv');
255
            header('Content-Transfer-Encoding: binary');
256
            header("Content-Disposition: attachment; filename=\"$filename\"");
257
        }
258
259
        return $this->fpassthru();
260
    }
261
262
    /**
263
     * Gets the source CSV encoding charset
264
     *
265
     * @return string
266
     */
267
    public function getInputEncoding()
268
    {
269
        return $this->input_encoding;
270
    }
271
272
    /**
273
     * Returns the BOM sequence in use on Output methods
274
     *
275
     * @return string
276
     */
277
    public function getOutputBOM()
278
    {
279
        return $this->output_bom;
280
    }
281
282
    /**
283
     * Returns the BOM sequence of the given CSV
284
     *
285
     * @return string
286
     */
287
    public function getInputBOM()
288
    {
289
        if (null === $this->input_bom) {
290
            $bom = [
291
                AbstractCsv::BOM_UTF32_BE, AbstractCsv::BOM_UTF32_LE,
292
                AbstractCsv::BOM_UTF16_BE, AbstractCsv::BOM_UTF16_LE, AbstractCsv::BOM_UTF8,
293
            ];
294
            $csv = $this->getIterator();
295
            $csv->setFlags(SplFileObject::READ_CSV);
296
            $csv->rewind();
297
            $line = $csv->fgets();
298
            $res = array_filter($bom, function ($sequence) use ($line) {
299
                return strpos($line, $sequence) === 0;
300
            });
301
302
            $this->input_bom = (string) array_shift($res);
303
        }
304
305
        return $this->input_bom;
306
    }
307
    /**
308
     * Outputs all data from the CSV
309
     *
310
     * @return int Returns the number of characters read from the handle
311
     *             and passed through to the output.
312
     */
313
    protected function fpassthru()
314
    {
315
        $bom = '';
316
        $input_bom = $this->getInputBOM();
317
        if ($this->output_bom && $input_bom != $this->output_bom) {
318
            $bom = $this->output_bom;
319
        }
320
        $csv = $this->getIterator();
321
        $csv->setFlags(SplFileObject::READ_CSV);
322
        $csv->rewind();
323
        if (!empty($bom)) {
324
            $csv->fseek(mb_strlen($input_bom));
325
        }
326
        echo $bom;
327
        $res = $csv->fpassthru();
328
329
        return $res + strlen($bom);
330
    }
331
332
    /**
333
     * Retrieves the CSV content
334
     *
335
     * @return string
336
     */
337
    public function __toString()
338
    {
339
        ob_start();
340
        $this->fpassthru();
341
342
        return ob_get_clean();
343
    }
344
345
    /**
346
     * Sets the CSV encoding charset
347
     *
348
     * @param string $str
349
     *
350
     * @return static
351
     */
352
    public function setInputEncoding($str)
353
    {
354
        $str = str_replace('_', '-', $str);
355
        $str = filter_var($str, FILTER_SANITIZE_STRING, ['flags' => FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH]);
356
        if (empty($str)) {
357
            throw new InvalidArgumentException('you should use a valid charset');
358
        }
359
        $this->input_encoding = strtoupper($str);
360
361
        return $this;
362
    }
363
364
    /**
365
     * Sets the BOM sequence to prepend the CSV on output
366
     *
367
     * @param string $str The BOM sequence
368
     *
369
     * @return static
370
     */
371
    public function setOutputBOM($str)
372
    {
373
        if (empty($str)) {
374
            $this->output_bom = '';
375
376
            return $this;
377
        }
378
379
        $this->output_bom = (string) $str;
380
381
        return $this;
382
    }
383
}
384