Completed
Pull Request — master (#178)
by ignace nyamagana
02:51
created

Controls::getOutputBOM()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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