Completed
Pull Request — master (#210)
by ignace nyamagana
02:28
created

ControlsTrait::setHeader()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
cc 3
eloc 14
nc 3
nop 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 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
declare(strict_types=1);
14
15
namespace League\Csv\Config;
16
17
use CallbackFilterIterator;
18
use InvalidArgumentException;
19
use League\Csv\AbstractCsv;
20
use LimitIterator;
21
use SplFileObject;
22
23
/**
24
 *  An abstract class to enable basic CSV manipulation
25
 *
26
 * @package League.csv
27
 * @since  9.0.0
28
 * @internal
29
 */
30
trait ControlsTrait
31
{
32
    use ValidatorTrait;
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
     * @var string
65
     */
66
    protected $input_bom;
67
68
    /**
69
     * The Output file BOM character
70
     * @var string
71
     */
72
    protected $output_bom = '';
73
74
    /**
75
     * CSV Document header offset
76
     *
77
     * @var int|null
78
     */
79
    protected $header_offset;
80
81
    /**
82
     * Buffer flush threshold
83
     *
84
     * @var int
85
     */
86
    protected $flush_threshold = 500;
87
88
    /**
89
     * Returns the inner CSV Document Iterator object
90
     *
91
     * @return StreamIterator|SplFileObject
92
     */
93
    abstract public function getDocument();
94
95
    /**
96
     * Returns the current field delimiter
97
     *
98
     * @return string
99
     */
100
    public function getDelimiter(): string
101
    {
102
        return $this->delimiter;
103
    }
104
105
    /**
106
     * Returns the current field enclosure
107
     *
108
     * @return string
109
     */
110
    public function getEnclosure(): string
111
    {
112
        return $this->enclosure;
113
    }
114
115
    /**
116
     * Returns the current field escape character
117
     *
118
     * @return string
119
     */
120
    public function getEscape(): string
121
    {
122
        return $this->escape;
123
    }
124
125
    /**
126
     * Returns the current newline sequence characters
127
     *
128
     * @return string
129
     */
130
    public function getNewline(): string
131
    {
132
        return $this->newline;
133
    }
134
135
    /**
136
     * Get the flush threshold
137
     *
138
     * @return int
139
     */
140
    public function getFlushThreshold(): int
141
    {
142
        return $this->flush_threshold;
143
    }
144
145
    /**
146
     * Returns the BOM sequence in use on Output methods
147
     *
148
     * @return string
149
     */
150
    public function getOutputBOM(): string
151
    {
152
        return $this->output_bom;
153
    }
154
155
    /**
156
     * Returns the record offset used as header
157
     *
158
     * If no CSV record is used this method MUST return null
159
     *
160
     * @return int|null
161
     */
162
    public function getHeaderOffset()
163
    {
164
        return $this->header_offset;
165
    }
166
167
    /**
168
     * Returns the BOM sequence of the given CSV
169
     *
170
     * @return string
171
     */
172
    public function getInputBOM(): string
173
    {
174
        if (null === $this->input_bom) {
175
            $bom = [
176
                AbstractCsv::BOM_UTF32_BE, AbstractCsv::BOM_UTF32_LE,
177
                AbstractCsv::BOM_UTF16_BE, AbstractCsv::BOM_UTF16_LE, AbstractCsv::BOM_UTF8,
178
            ];
179
            $csv = $this->getDocument();
180
            $csv->setFlags(SplFileObject::READ_CSV);
181
            $csv->rewind();
182
            $line = $csv->fgets();
183
            $res  = array_filter($bom, function ($sequence) use ($line) {
184
                return strpos($line, $sequence) === 0;
185
            });
186
187
            $this->input_bom = (string) array_shift($res);
188
        }
189
190
        return $this->input_bom;
191
    }
192
193
    /**
194
     * Detect Delimiters occurences in the CSV
195
     *
196
     * Returns a associative array where each key represents
197
     * a valid delimiter and each value the number of occurences
198
     *
199
     * @param string[] $delimiters the delimiters to consider
200
     * @param int      $nb_rows    Detection is made using $nb_rows of the CSV
201
     *
202
     * @return array
203
     */
204
    public function fetchDelimitersOccurrence(array $delimiters, int $nb_rows = 1): array
205
    {
206
        $nb_rows = $this->filterInteger($nb_rows, 1, 'The number of rows to consider must be a valid positive integer');
207
        $filter_row = function ($row) {
208
            return is_array($row) && count($row) > 1;
209
        };
210
        $delimiters = array_unique(array_filter($delimiters, function ($value) {
211
            return 1 == strlen($value);
212
        }));
213
        $csv = $this->getDocument();
214
        $csv->setFlags(SplFileObject::READ_CSV);
215
        $res = [];
216
        foreach ($delimiters as $delim) {
217
            $csv->setCsvControl($delim, $this->enclosure, $this->escape);
218
            $iterator = new CallbackFilterIterator(new LimitIterator($csv, 0, $nb_rows), $filter_row);
219
            $res[$delim] = count(iterator_to_array($iterator, false), COUNT_RECURSIVE);
220
        }
221
        arsort($res, SORT_NUMERIC);
222
223
        return $res;
224
    }
225
226
    /**
227
     * Sets the field delimiter
228
     *
229
     * @param string $delimiter
230
     *
231
     * @throws InvalidArgumentException If $delimiter is not a single character
232
     *
233
     * @return $this
234
     */
235
    public function setDelimiter(string $delimiter): self
236
    {
237
        $this->delimiter = $this->filterControl($delimiter, 'delimiter');
238
239
        return $this;
240
    }
241
242
    /**
243
     * Sets the field enclosure
244
     *
245
     * @param string $enclosure
246
     *
247
     * @throws InvalidArgumentException If $enclosure is not a single character
248
     *
249
     * @return $this
250
     */
251
    public function setEnclosure(string $enclosure): self
252
    {
253
        $this->enclosure = $this->filterControl($enclosure, 'enclosure');
254
255
        return $this;
256
    }
257
258
    /**
259
     * Sets the field escape character
260
     *
261
     * @param string $escape
262
     *
263
     * @throws InvalidArgumentException If $escape is not a single character
264
     *
265
     * @return $this
266
     */
267
    public function setEscape(string $escape): self
268
    {
269
        $this->escape = $this->filterControl($escape, 'escape');
270
271
        return $this;
272
    }
273
274
    /**
275
     * Sets the newline sequence characters
276
     *
277
     * @param string $newline
278
     *
279
     * @return static
280
     */
281
    public function setNewline(string $newline): self
282
    {
283
        $this->newline = (string) $newline;
284
285
        return $this;
286
    }
287
288
    /**
289
     * Set the automatic flush threshold on write
290
     *
291
     * @param int $threshold
292
     */
293
    public function setFlushThreshold(int $threshold): self
294
    {
295
        $this->flush_threshold = $this->filterInteger($threshold, 0, 'The flush threshold must be a valid positive integer or 0');
296
297
        return $this;
298
    }
299
300
    /**
301
     * Sets the BOM sequence to prepend the CSV on output
302
     *
303
     * @param string $str The BOM sequence
304
     *
305
     * @return static
306
     */
307
    public function setOutputBOM(string $str): self
308
    {
309
        if (empty($str)) {
310
            $this->output_bom = '';
311
312
            return $this;
313
        }
314
315
        $this->output_bom = (string) $str;
316
317
        return $this;
318
    }
319
320
    /**
321
     * Selects the record to be used as the CSV header
322
     *
323
     * Because of the header is represented as an array, to be valid
324
     * a header MUST contain only unique string value.
325
     *
326
     * @param int|null $offset the header row offset
327
     *
328
     * @return $this
329
     */
330
    public function setHeaderOffset(int $offset = null): self
331
    {
332
        $this->header_offset = null;
333
        if (null !== $offset) {
334
            $this->header_offset = $this->filterInteger(
335
                $offset,
336
                0,
337
                'the header offset index must be a positive integer or 0'
338
            );
339
        }
340
341
        return $this;
342
    }
343
}
344