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

AbstractCsv::getHeader()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 1
cts 1
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
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
declare(strict_types=1);
14
15
namespace League\Csv;
16
17
use League\Csv\Config\ControlsTrait;
18
use League\Csv\Config\StreamTrait;
19
use SplFileObject;
20
21
/**
22
 *  An abstract class to enable basic CSV manipulation
23
 *
24
 * @package League.csv
25
 * @since  4.0.0
26
 *
27
 */
28
abstract class AbstractCsv
29
{
30
    use ControlsTrait;
31
    use StreamTrait;
32
33
    /**
34
     *  UTF-8 BOM sequence
35
     */
36
    const BOM_UTF8 = "\xEF\xBB\xBF";
37
38
    /**
39
     * UTF-16 BE BOM sequence
40
     */
41
    const BOM_UTF16_BE = "\xFE\xFF";
42
43
    /**
44
     * UTF-16 LE BOM sequence
45
     */
46
    const BOM_UTF16_LE = "\xFF\xFE";
47
48
    /**
49
     * UTF-32 BE BOM sequence
50
     */
51
    const BOM_UTF32_BE = "\x00\x00\xFE\xFF";
52
53
    /**
54
     * UTF-32 LE BOM sequence
55
     */
56
    const BOM_UTF32_LE = "\xFF\xFE\x00\x00";
57
58
    /**
59
     * Creates a new instance
60
     *
61
     * The file path can be:
62
     *
63
     * - a SplFileObject
64
     * - a StreamIterator
65
     *
66
     * @param SplFileObject|StreamIterator $document The CSV Object instance
67
     */
68
    protected function __construct($document)
69
    {
70
        $this->document = $document;
71
    }
72
73
    /**
74
     * The destructor
75
     */
76
    public function __destruct()
77
    {
78
        if ($this->isActiveStreamFilter()) {
79
            $this->clearStreamFilter();
80
        }
81
82
        $this->document = null;
83
    }
84
85
    /**
86
     * Set the Inner Iterator
87
     *
88
     * @return StreamIterator|SplFileObject
89
     */
90
    public function getDocument()
91
    {
92
        return $this->document;
93
    }
94
95
    /**
96
     * Return a new {@link AbstractCsv} from a SplFileObject
97
     *
98
     * @param SplFileObject $file
99
     *
100
     * @return static
101
     */
102
    public static function createFromFileObject(SplFileObject $file): self
103
    {
104
        $csv = new static($file);
105 357
        $controls = $file->getCsvControl();
106
        $csv->setDelimiter($controls[0]);
107 357
        $csv->setEnclosure($controls[1]);
108 357
        if (isset($controls[2])) {
109 357
            $csv->setEscape($controls[2]);
110 357
        }
111
112
        return $csv;
113
    }
114
115 240
    /**
116
     * Return a new {@link AbstractCsv} from a PHP resource stream
117 240
     *
118 240
     * @param resource $stream
119
     *
120
     * @return static
121
     */
122
    public static function createFromStream($stream): self
123
    {
124
        return new static(new StreamIterator($stream));
125
    }
126
127 312
    /**
128
     * Return a new {@link AbstractCsv} from a string
129 312
     *
130
     * The string must be an object that implements the `__toString` method,
131
     * or a string
132
     *
133
     * @param string $str the string
134
     *
135
     * @return static
136
     */
137
    public static function createFromString(string $str): self
138
    {
139
        $stream = fopen('php://temp', 'r+');
140
        fwrite($stream, $str);
141
142 27
        return new static(new StreamIterator($stream));
143
    }
144 27
145 27
    /**
146
     * Return a new {@link AbstractCsv} from a file path
147 27
     *
148
     * @param string $path      file path
149
     * @param string $open_mode the file open mode flag
150
     *
151
     * @return static
152
     */
153
    public static function createFromPath(string $path, string $open_mode = 'r+'): self
154
    {
155
        $stream = fopen($path, $open_mode);
156
157
        return new static(new StreamIterator($stream));
158
    }
159 90
160
    /**
161 90
     * Return a new {@link AbstractCsv} instance from another {@link AbstractCsv} object
162 87
     *
163
     * @param string $class the class to be instantiated
164 3
     *
165
     * @return static
166
     */
167
    protected function newInstance(string $class): self
168
    {
169
        $csv = new $class($this->document);
170
        $csv->delimiter = $this->delimiter;
171
        $csv->enclosure = $this->enclosure;
172
        $csv->escape = $this->escape;
173
        $csv->input_bom = $this->input_bom;
174
        $csv->output_bom = $this->output_bom;
175
        $csv->newline = $this->newline;
176
        $csv->flush_threshold = $this->flush_threshold;
177 54
        $csv->clearStreamFilter();
178
179 54
        return $csv;
180 3
    }
181
182
    /**
183 51
     * Return a new {@link Writer} instance from a {@link AbstractCsv} object
184 3
     *
185 2
     * @return Writer
186
     */
187 51
    public function newWriter(): self
188
    {
189
        return $this->newInstance(Writer::class);
190
    }
191
192
    /**
193
     * Return a new {@link Reader} instance from a {@link AbstractCsv} object
194
     *
195
     * @return Reader
196
     */
197
    public function newReader(): self
198 6
    {
199
        return $this->newInstance(Reader::class);
200 6
    }
201 6
202 6
    /**
203 6
     * Returns the header
204 6
     *
205 6
     * If no CSV record is used this method MUST return an empty array
206 6
     *
207 6
     * @return string[]
208
     */
209 6
    public function getHeader(): array
210
    {
211
        if (null === $this->header_offset) {
212
            return [];
213
        }
214
215
        return $this->filterHeader($this->getRow($this->header_offset));
216
    }
217
218
    /**
219 3
     * Returns a single row from the CSV without filtering
220
     *
221 3
     * @param int $offset
222
     *
223
     * @throws Exception If the $offset is not valid or the row does not exist
224
     *
225
     * @return array
226
     */
227
    protected function getRow(int $offset): array
228
    {
229
        $csv = $this->getDocument();
230
        $csv->setFlags(SplFileObject::READ_CSV);
231 3
        $csv->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
232
        $csv->seek($offset);
233 3
        $row = $csv->current();
234
        if (empty($row) || [null] === $row) {
235
            throw new Exception('the specified row does not exist or is empty');
236
        }
237
238
        if (0 != $offset) {
239
            return $row;
240
        }
241 267
242
        return $this->removeBOM($row, mb_strlen($this->getInputBOM()), $this->enclosure);
243 267
    }
244 267
245 33
    /**
246 22
     * Outputs all data on the CSV file
247 267
     *
248 267
     * @param string $filename CSV downloaded name if present adds extra headers
249
     *
250 267
     * @return int Returns the number of characters read from the handle
251
     *             and passed through to the output.
252
     */
253
    public function output(string $filename = null): int
254
    {
255
        if (null !== $filename) {
256
            $filename = filter_var($filename, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
257
            header('Content-Type: text/csv');
258
            header('Content-Transfer-Encoding: binary');
259
            header("Content-Disposition: attachment; filename=\"$filename\"");
260
        }
261
262
        return $this->fpassthru();
263
    }
264
265
    /**
266
     * Outputs all data from the CSV
267
     *
268
     * @return int Returns the number of characters read from the handle
269
     *             and passed through to the output.
270
     */
271
    protected function fpassthru(): int
272
    {
273
        $bom = '';
274
        $input_bom = $this->getInputBOM();
275
        if ($this->output_bom && $input_bom != $this->output_bom) {
276
            $bom = $this->output_bom;
277
        }
278
        $csv = $this->getDocument();
279
        $csv->rewind();
280
        if ('' !== $bom) {
281
            $csv->fseek(mb_strlen($input_bom));
282
        }
283
        echo $bom;
284
        $res = $csv->fpassthru();
285
286
        return $res + strlen($bom);
287
    }
288
289
    /**
290
     * Retrieves the CSV content
291
     *
292
     * @return string
293
     */
294
    public function __toString(): string
295
    {
296
        ob_start();
297
        $this->fpassthru();
298
299
        return ob_get_clean();
300
    }
301
}
302