Completed
Pull Request — master (#210)
by ignace nyamagana
02:58 queued 01:13
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
     * Returns the inner CSV Document Iterator object
83
     *
84
     * @return StreamIterator|SplFileObject
85
     */
86
    abstract public function getDocument();
87
88
    /**
89
     * Returns the current field delimiter
90
     *
91
     * @return string
92
     */
93
    public function getDelimiter(): string
94
    {
95
        return $this->delimiter;
96
    }
97
98
    /**
99
     * Returns the current field enclosure
100
     *
101
     * @return string
102
     */
103
    public function getEnclosure(): string
104
    {
105
        return $this->enclosure;
106
    }
107
108
    /**
109
     * Returns the current field escape character
110
     *
111
     * @return string
112
     */
113
    public function getEscape(): string
114
    {
115
        return $this->escape;
116
    }
117
118
    /**
119
     * Returns the current newline sequence characters
120
     *
121
     * @return string
122
     */
123
    public function getNewline(): string
124
    {
125
        return $this->newline;
126
    }
127
128
    /**
129
     * Returns the BOM sequence in use on Output methods
130
     *
131
     * @return string
132
     */
133
    public function getOutputBOM(): string
134
    {
135
        return $this->output_bom;
136
    }
137
138
    /**
139
     * Returns the record offset used as header
140
     *
141
     * If no CSV record is used this method MUST return null
142
     *
143
     * @return int|null
144
     */
145
    public function getHeaderOffset()
146
    {
147
        return $this->header_offset;
148
    }
149
150
    /**
151
     * Returns the BOM sequence of the given CSV
152
     *
153
     * @return string
154
     */
155
    public function getInputBOM(): string
156
    {
157
        if (null === $this->input_bom) {
158
            $bom = [
159
                AbstractCsv::BOM_UTF32_BE, AbstractCsv::BOM_UTF32_LE,
160
                AbstractCsv::BOM_UTF16_BE, AbstractCsv::BOM_UTF16_LE, AbstractCsv::BOM_UTF8,
161
            ];
162
            $csv = $this->getDocument();
163
            $csv->setFlags(SplFileObject::READ_CSV);
164
            $csv->rewind();
165
            $line = $csv->fgets();
166
            $res  = array_filter($bom, function ($sequence) use ($line) {
167
                return strpos($line, $sequence) === 0;
168
            });
169
170
            $this->input_bom = (string) array_shift($res);
171
        }
172
173
        return $this->input_bom;
174
    }
175
176
    /**
177
     * Detect Delimiters occurences in the CSV
178
     *
179
     * Returns a associative array where each key represents
180
     * a valid delimiter and each value the number of occurences
181
     *
182
     * @param string[] $delimiters the delimiters to consider
183
     * @param int      $nb_rows    Detection is made using $nb_rows of the CSV
184
     *
185
     * @return array
186
     */
187
    public function fetchDelimitersOccurrence(array $delimiters, int $nb_rows = 1): array
188
    {
189
        $nb_rows = $this->filterInteger($nb_rows, 1, 'The number of rows to consider must be a valid positive integer');
190
        $filter_row = function ($row) {
191
            return is_array($row) && count($row) > 1;
192
        };
193
        $delimiters = array_unique(array_filter($delimiters, function ($value) {
194
            return 1 == strlen($value);
195
        }));
196
        $csv = $this->getDocument();
197
        $csv->setFlags(SplFileObject::READ_CSV);
198
        $res = [];
199
        foreach ($delimiters as $delim) {
200
            $csv->setCsvControl($delim, $this->enclosure, $this->escape);
201
            $iterator = new CallbackFilterIterator(new LimitIterator($csv, 0, $nb_rows), $filter_row);
202
            $res[$delim] = count(iterator_to_array($iterator, false), COUNT_RECURSIVE);
203
        }
204
        arsort($res, SORT_NUMERIC);
205
206
        return $res;
207
    }
208
209
    /**
210
     * Sets the field delimiter
211
     *
212
     * @param string $delimiter
213
     *
214
     * @throws InvalidArgumentException If $delimiter is not a single character
215
     *
216
     * @return $this
217
     */
218
    public function setDelimiter(string $delimiter): self
219
    {
220
        $this->delimiter = $this->filterControl($delimiter, 'delimiter');
221
222
        return $this;
223
    }
224
225
    /**
226
     * Sets the field enclosure
227
     *
228
     * @param string $enclosure
229
     *
230
     * @throws InvalidArgumentException If $enclosure is not a single character
231
     *
232
     * @return $this
233
     */
234
    public function setEnclosure(string $enclosure): self
235
    {
236
        $this->enclosure = $this->filterControl($enclosure, 'enclosure');
237
238
        return $this;
239
    }
240
241
    /**
242
     * Sets the field escape character
243
     *
244
     * @param string $escape
245
     *
246
     * @throws InvalidArgumentException If $escape is not a single character
247
     *
248
     * @return $this
249
     */
250
    public function setEscape(string $escape): self
251
    {
252
        $this->escape = $this->filterControl($escape, 'escape');
253
254
        return $this;
255
    }
256
257
    /**
258
     * Sets the newline sequence characters
259
     *
260
     * @param string $newline
261
     *
262
     * @return static
263
     */
264
    public function setNewline(string $newline): self
265
    {
266
        $this->newline = (string) $newline;
267
268
        return $this;
269
    }
270
271
    /**
272
     * Sets the BOM sequence to prepend the CSV on output
273
     *
274
     * @param string $str The BOM sequence
275
     *
276
     * @return static
277
     */
278
    public function setOutputBOM(string $str): self
279
    {
280
        if (empty($str)) {
281
            $this->output_bom = '';
282
283
            return $this;
284
        }
285
286
        $this->output_bom = (string) $str;
287
288
        return $this;
289
    }
290
291
    /**
292
     * Selects the record to be used as the CSV header
293
     *
294
     * Because of the header is represented as an array, to be valid
295
     * a header MUST contain only unique string value.
296
     *
297
     * @param int|null $offset the header row offset
298
     *
299
     * @return $this
300
     */
301
    public function setHeaderOffset(int $offset = null): self
302
    {
303
        $this->header_offset = null;
304
        if (null !== $offset) {
305
            $this->header_offset = $this->filterInteger(
306
                $offset,
307
                0,
308
                'the header offset index must be a positive integer or 0'
309
            );
310
        }
311
312
        return $this;
313
    }
314
315
    /**
316
     * Returns whether the submitted value can be used as string
317
     *
318
     * @param mixed $value
319
     *
320
     * @return bool
321
     */
322
    protected function isValidKey($value): bool
323
    {
324
        return is_scalar($value) || (is_object($value) && method_exists($value, '__toString'));
325
    }
326
}
327