Completed
Pull Request — master (#211)
by Gabriel
02:23
created

AbstractCsv::saveTofile()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 28
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 28
ccs 0
cts 0
cp 0
rs 8.439
c 0
b 0
f 0
cc 5
eloc 19
nc 8
nop 1
crap 30
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
     * The file open mode flag
60
     *
61
     * @var string
62
     */
63
    protected $open_mode;
64
65
    /**
66
     * Creates a new instance
67
     *
68
     * The file path can be:
69
     *
70
     * - a string
71
     * - a SplFileObject
72
     * - a StreamIterator
73
     *
74
     * @param mixed $path The file path
75
     * @param string $open_mode The file open mode flag
76
     */
77
    protected function __construct($path, string $open_mode = 'r+')
78
    {
79
        $this->open_mode = strtolower($open_mode);
80
        $this->path = $path;
81
        $this->initStreamFilter();
82
    }
83
84
    /**
85
     * The destructor
86
     */
87
    public function __destruct()
88
    {
89
        $this->path = null;
90
    }
91
92
    /**
93
     * Return a new {@link AbstractCsv} from a SplFileObject
94
     *
95
     * @param SplFileObject $file
96
     *
97
     * @return static
98
     */
99
    public static function createFromFileObject(SplFileObject $file): self
100
    {
101
        $csv = new static($file);
102
        $controls = $file->getCsvControl();
103
        $csv->setDelimiter($controls[0]);
104
        $csv->setEnclosure($controls[1]);
105 357
        if (isset($controls[2])) {
106
            $csv->setEscape($controls[2]);
107 357
        }
108 357
109 357
        return $csv;
110 357
    }
111
112
    /**
113
     * Return a new {@link AbstractCsv} from a PHP resource stream
114
     *
115 240
     * @param resource $stream
116
     *
117 240
     * @return static
118 240
     */
119
    public static function createFromStream($stream): self
120
    {
121
        return new static(new StreamIterator($stream));
122
    }
123
124
    /**
125
     * Return a new {@link AbstractCsv} from a string
126
     *
127 312
     * The string must be an object that implements the `__toString` method,
128
     * or a string
129 312
     *
130
     * @param string $str the string
131
     *
132
     * @return static
133
     */
134
    public static function createFromString(string $str): self
135
    {
136
        $stream = fopen('php://temp', 'r+');
137
        fwrite($stream, $str);
138
139
        return new static(new StreamIterator($stream));
140
    }
141
142 27
    /**
143
     * Return a new {@link AbstractCsv} from a file path
144 27
     *
145 27
     * @param string $path file path
146
     * @param string $open_mode the file open mode flag
147 27
     *
148
     * @return static
149
     */
150
    public static function createFromPath(string $path, string $open_mode = 'r+'): self
151
    {
152
        return new static($path, $open_mode);
153
    }
154
155
    /**
156
     * Return a new {@link AbstractCsv} instance from another {@link AbstractCsv} object
157
     *
158
     * @param string $class the class to be instantiated
159 90
     * @param string $open_mode the file open mode flag
160
     *
161 90
     * @return static
162 87
     */
163
    protected function newInstance(string $class, string $open_mode): self
164 3
    {
165
        $csv = new $class($this->path, $open_mode);
166
        $csv->delimiter = $this->delimiter;
167
        $csv->enclosure = $this->enclosure;
168
        $csv->escape = $this->escape;
169
        $csv->input_bom = $this->input_bom;
170
        $csv->output_bom = $this->output_bom;
171
        $csv->newline = $this->newline;
172
173
        return $csv;
174
    }
175
176
    /**
177 54
     * Return a new {@link Writer} instance from a {@link AbstractCsv} object
178
     *
179 54
     * @param string $open_mode the file open mode flag
180 3
     *
181
     * @return Writer
182
     */
183 51
    public function newWriter(string $open_mode = 'r+'): self
184 3
    {
185 2
        return $this->newInstance(Writer::class, $open_mode);
186
    }
187 51
188
    /**
189
     * Return a new {@link Reader} instance from a {@link AbstractCsv} object
190
     *
191
     * @param string $open_mode the file open mode flag
192
     *
193
     * @return Reader
194
     */
195
    public function newReader(string $open_mode = 'r+'): self
196
    {
197
        return $this->newInstance(Reader::class, $open_mode);
198 6
    }
199
200 6
    /**
201 6
     * Set the Inner Iterator
202 6
     *
203 6
     * @return StreamIterator|SplFileObject
204 6
     */
205 6
    protected function getCsvDocument()
206 6
    {
207 6
        if ($this->path instanceof StreamIterator || $this->path instanceof SplFileObject) {
208
            return $this->path;
209 6
        }
210
211
        return new SplFileObject($this->getStreamFilterPath(), $this->open_mode);
212
    }
213
214
    /**
215
     * Outputs all data on the CSV file
216
     *
217
     * @param string $filename CSV downloaded name if present adds extra headers
218
     *
219 3
     * @return int Returns the number of characters read from the handle
220
     *             and passed through to the output.
221 3
     */
222
    public function output(string $filename = null): int
223
    {
224
        if (null !== $filename) {
225
            $filename = filter_var($filename, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
226
            header('Content-Type: text/csv');
227
            header('Content-Transfer-Encoding: binary');
228
            header("Content-Disposition: attachment; filename=\"$filename\"");
229
        }
230
231 3
        return $this->fpassthru();
232
    }
233 3
234
    /**
235
     * Outputs all data on the csv file to disk
236
     *
237
     * @param string $filename CSV File to be saved on disk
238
     *
239
     * @return int Returns the file
240
     */
241 267
    public function outputFile(string $filename): int
242
    {
243 267
        if (null !== $filename) {
244 267
            $filename = filter_var($filename, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
245 33
        }
246 22
247 267
        return $this->saveTofile($filename);
248 267
249
    }
250 267
251
    /**
252
     * Save csv to file in the disk
253
     *
254
     * @param string $filename CSV File to be saved on disk
255
     *
256
     * @return int Return the size of file
257
     */
258
    protected function saveTofile(string $filename): int
259
    {
260
        $bom = '';
261
        $input_bom = $this->getInputBOM();
262
        if ($this->output_bom && $input_bom != $this->output_bom) {
263
            $bom = $this->output_bom;
264
        }
265
        $csv = $this->getCsvDocument();
266
        $csv->rewind();
267
        if ('' !== $bom) {
268
            $csv->fseek(mb_strlen($input_bom));
269
        } else {
270
            $csv->fseek(0);
271
        }
272
273
        $filesize = 0;
274
        $outputFile = new SplFileObject($filename, 'w+');
275
276
        while (!$csv->eof()) {
0 ignored issues
show
Bug introduced by
The method eof does only exist in SplFileObject, but not in League\Csv\StreamIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
277
            $line = $csv->fgets();
278
            $outputFile->fwrite($line, strlen($line));
279
            $filesize += strlen($line);
280
        }
281
282
        $outputFile->fflush();
283
284
        return $filesize;
285
    }
286
287
288
    /**
289
     * Outputs all data from the CSV
290
     *
291
     * @return int Returns the number of characters read from the handle
292
     *             and passed through to the output.
293
     */
294
    protected function fpassthru(): int
295
    {
296
        $bom = '';
297
        $input_bom = $this->getInputBOM();
298
        if ($this->output_bom && $input_bom != $this->output_bom) {
299
            $bom = $this->output_bom;
300
        }
301
        $csv = $this->getCsvDocument();
302
        $csv->rewind();
303
        if ('' !== $bom) {
304
            $csv->fseek(mb_strlen($input_bom));
305
        }
306
        echo $bom;
307
        $res = $csv->fpassthru();
308
309
        return $res + strlen($bom);
310
    }
311
312
    /**
313
     * Retrieves the CSV content
314
     *
315
     * @return string
316
     */
317
    public function __toString(): string
318
    {
319
        ob_start();
320
        $this->fpassthru();
321
322
        return ob_get_clean();
323
    }
324
}
325