Completed
Pull Request — master (#178)
by ignace nyamagana
02:43
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
     * The Input file BOM character
64
     *
65
     * @var string|null
66 3
     */
67
    protected $input_bom;
68 3
69 3
    /**
70
     * The Output file BOM character
71 3
     * @var string
72
     */
73 3
    protected $output_bom = '';
74
75
    /**
76
     * Sets the field delimiter
77
     *
78
     * @param string $delimiter
79
     *
80
     * @throws InvalidArgumentException If $delimiter is not a single character
81
     *
82
     * @return $this
83 18
     */
84
    public function setDelimiter($delimiter)
85 18
    {
86
        if (!$this->isValidCsvControls($delimiter)) {
87
            throw new InvalidArgumentException('The delimiter must be a single character');
88
        }
89
        $this->delimiter = $delimiter;
90
91
        return $this;
92
    }
93 3
94
    /**
95 3
     * Tell whether the submitted string is a valid CSV Control character
96
     *
97
     * @param string $str The submitted string
98
     *
99
     * @return bool
100
     */
101
    protected function isValidCsvControls($str)
102
    {
103
        return 1 == mb_strlen($str);
104
    }
105
106
    /**
107
     * Returns the current field delimiter
108
     *
109 12
     * @return string
110
     */
111 12
    public function getDelimiter()
112 9
    {
113 9
        return $this->delimiter;
114 9
    }
115 9
116 9
    /**
117 9
     * Detect Delimiters occurences in the CSV
118 9
     *
119 9
     * Returns a associative array where each key represents
120 9
     * a valid delimiter and each value the number of occurences
121 9
     *
122 6
     * @param string[] $delimiters the delimiters to consider
123 9
     * @param int      $nb_rows    Detection is made using $nb_rows of the CSV
124
     *
125 9
     * @return array
126
     */
127
    public function fetchDelimitersOccurrence(array $delimiters, $nb_rows = 1)
128
    {
129
        $nb_rows = $this->validateInteger($nb_rows, 1, 'The number of rows to consider must be a valid positive integer');
130
        $filter_row = function ($row) {
131
            return is_array($row) && count($row) > 1;
132
        };
133
        $delimiters = array_unique(array_filter($delimiters, [$this, 'isValidCsvControls']));
134
        $csv = $this->getIterator();
135
        $res = [];
136
        foreach ($delimiters as $delim) {
137
            $csv->setCsvControl($delim, $this->enclosure, $this->escape);
138
            $iterator = new CallbackFilterIterator(new LimitIterator($csv, 0, $nb_rows), $filter_row);
139 114
            $res[$delim] = count(iterator_to_array($iterator, false), COUNT_RECURSIVE);
140
        }
141 114
        arsort($res, SORT_NUMERIC);
142 18
143
        return $res;
144 99
    }
145
146
    /**
147
     * Returns the CSV Iterator
148
     *
149
     * @return SplFileObject
150
     */
151
    abstract public function getIterator();
152
153
    /**
154
     * Sets the field enclosure
155
     *
156
     * @param string $enclosure
157
     *
158
     * @throws InvalidArgumentException If $enclosure is not a single character
159
     *
160
     * @return $this
161
     */
162
    public function setEnclosure($enclosure)
163 3
    {
164
        if (!$this->isValidCsvControls($enclosure)) {
165 3
            throw new InvalidArgumentException('The enclosure must be a single character');
166 3
        }
167
        $this->enclosure = $enclosure;
168 3
169
        return $this;
170 3
    }
171
172
    /**
173
     * Returns the current field enclosure
174
     *
175
     * @return string
176
     */
177
    public function getEnclosure()
178 24
    {
179
        return $this->enclosure;
180 24
    }
181
182
    /**
183
     * Sets the field escape character
184
     *
185
     * @param string $escape
186
     *
187
     * @throws InvalidArgumentException If $escape is not a single character
188
     *
189
     * @return $this
190
     */
191
    public function setEscape($escape)
192 3
    {
193
        if (!$this->isValidCsvControls($escape)) {
194 3
            throw new InvalidArgumentException('The escape character must be a single character');
195 3
        }
196
        $this->escape = $escape;
197 3
198
        return $this;
199 3
    }
200
201
    /**
202
     * Returns the current field escape character
203
     *
204
     * @return string
205
     */
206
    public function getEscape()
207 3
    {
208
        return $this->escape;
209 3
    }
210
211
    /**
212
     * Sets the newline sequence characters
213
     *
214
     * @param string $newline
215
     *
216
     * @return static
217
     */
218
    public function setNewline($newline)
219 6
    {
220
        $this->newline = (string) $newline;
221 6
222
        return $this;
223 6
    }
224
225
    /**
226
     * Returns the current newline sequence characters
227
     *
228
     * @return string
229
     */
230
    public function getNewline()
231 6
    {
232
        return $this->newline;
233 6
    }
234
235
    /**
236
     * Outputs all data on the CSV file
237
     *
238
     * @param string $filename CSV downloaded name if present adds extra headers
239
     *
240
     * @return int Returns the number of characters read from the handle
241
     *             and passed through to the output.
242
     */
243
    public function output($filename = null)
244
    {
245
        if (null !== $filename) {
246
            $filename = filter_var($filename, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
247
            header('Content-Type: text/csv');
248
            header('Content-Transfer-Encoding: binary');
249
            header("Content-Disposition: attachment; filename=\"$filename\"");
250
        }
251
252
        return $this->fpassthru();
253
    }
254
255
    /**
256
     * Returns the BOM sequence in use on Output methods
257
     *
258
     * @return string
259
     */
260
    public function getOutputBOM()
261
    {
262
        return $this->output_bom;
263
    }
264
265
    /**
266
     * Returns the BOM sequence of the given CSV
267
     *
268
     * @return string
269
     */
270
    public function getInputBOM()
271
    {
272
        if (null === $this->input_bom) {
273
            $bom = [
274
                AbstractCsv::BOM_UTF32_BE, AbstractCsv::BOM_UTF32_LE,
275
                AbstractCsv::BOM_UTF16_BE, AbstractCsv::BOM_UTF16_LE, AbstractCsv::BOM_UTF8,
276
            ];
277
            $csv = $this->getIterator();
278
            $csv->setFlags(SplFileObject::READ_CSV);
279
            $csv->rewind();
280
            $line = $csv->fgets();
281
            $res = array_filter($bom, function ($sequence) use ($line) {
282
                return strpos($line, $sequence) === 0;
283
            });
284
285
            $this->input_bom = (string) array_shift($res);
286
        }
287
288
        return $this->input_bom;
289
    }
290
    /**
291
     * Outputs all data from the CSV
292
     *
293
     * @return int Returns the number of characters read from the handle
294
     *             and passed through to the output.
295
     */
296
    protected function fpassthru()
297
    {
298
        $bom = '';
299
        $input_bom = $this->getInputBOM();
300
        if ($this->output_bom && $input_bom != $this->output_bom) {
301
            $bom = $this->output_bom;
302
        }
303
        $csv = $this->getIterator();
304
        $csv->setFlags(SplFileObject::READ_CSV);
305
        $csv->rewind();
306
        if (!empty($bom)) {
307
            $csv->fseek(mb_strlen($input_bom));
308
        }
309
        echo $bom;
310
        $res = $csv->fpassthru();
311
312
        return $res + strlen($bom);
313
    }
314
315
    /**
316
     * Retrieves the CSV content
317
     *
318
     * @return string
319
     */
320
    public function __toString()
321
    {
322
        ob_start();
323
        $this->fpassthru();
324
325
        return ob_get_clean();
326
    }
327
328
    /**
329
     * Sets the BOM sequence to prepend the CSV on output
330
     *
331
     * @param string $str The BOM sequence
332
     *
333
     * @return static
334
     */
335
    public function setOutputBOM($str)
336
    {
337
        if (empty($str)) {
338
            $this->output_bom = '';
339
340
            return $this;
341
        }
342
343
        $this->output_bom = (string) $str;
344
345
        return $this;
346
    }
347
}
348