Completed
Pull Request — master (#178)
by ignace nyamagana
40:39
created

Controls::getIterator()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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