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

AbstractCsv::newInstance()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 1
cts 1
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 9
nc 1
nop 2
crap 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;
16
17
use League\Csv\Config\ControlsTrait;
18
use LogicException;
19
use RuntimeException;
20
use SplFileObject;
21
22
/**
23
 *  An abstract class to enable basic CSV manipulation
24
 *
25
 * @package League.csv
26
 * @since  4.0.0
27
 *
28
 */
29
abstract class AbstractCsv
30
{
31
    use ControlsTrait;
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 218
    protected function __construct($document)
69
    {
70 218
        $this->document = $document;
71 218
    }
72
73
    /**
74
     * The destructor
75
     */
76 218
    public function __destruct()
77
    {
78 218
        $this->clearStreamFilter();
79 218
        $this->document = null;
80 218
    }
81
82
    /**
83
     * Return a new {@link AbstractCsv} from a SplFileObject
84
     *
85
     * @param SplFileObject $file
86
     *
87
     * @return static
88
     */
89 180
    public static function createFromFileObject(SplFileObject $file): self
90
    {
91 180
        $csv = new static($file);
92 180
        $controls = $file->getCsvControl();
93 180
        $csv->setDelimiter($controls[0]);
94 180
        $csv->setEnclosure($controls[1]);
95 180
        if (isset($controls[2])) {
96 180
            $csv->setEscape($controls[2]);
97
        }
98
99 180
        return $csv;
100
    }
101
102
    /**
103
     * Return a new {@link AbstractCsv} from a PHP resource stream
104
     *
105
     * @param resource $stream
106
     *
107
     * @return static
108
     */
109 28
    public static function createFromStream($stream): self
110
    {
111 28
        return new static(new StreamIterator($stream));
112
    }
113
114
    /**
115
     * Return a new {@link AbstractCsv} from a string
116
     *
117
     * The string must be an object that implements the `__toString` method,
118
     * or a string
119
     *
120
     * @param string $str the string
121
     *
122
     * @return static
123
     */
124 14
    public static function createFromString(string $str): self
125
    {
126 14
        $stream = fopen('php://temp', 'r+');
127 14
        fwrite($stream, $str);
128
129 14
        return new static(new StreamIterator($stream));
130
    }
131
132
    /**
133
     * Return a new {@link AbstractCsv} from a file path
134
     *
135
     * @param string $path      file path
136
     * @param string $open_mode the file open mode flag
137
     *
138
     * @return static
139
     */
140 18
    public static function createFromPath(string $path, string $open_mode = 'r+'): self
141
    {
142 18
        if (!$stream = @fopen($path, $open_mode)) {
143 2
            throw new RuntimeException(error_get_last()['message']);
144
        }
145
146 16
        return new static(new StreamIterator($stream));
147
    }
148
149
    /**
150
     * Outputs all data on the CSV file
151
     *
152
     * @param string $filename CSV downloaded name if present adds extra headers
153
     *
154
     * @return int Returns the number of characters read from the handle
155
     *             and passed through to the output.
156
     */
157 4
    public function output(string $filename = null): int
158
    {
159 4
        if (null !== $filename) {
160 4
            $filename = filter_var($filename, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
161 4
            header('Content-Type: text/csv');
162 4
            header('Content-Transfer-Encoding: binary');
163 4
            header("Content-Disposition: attachment; filename=\"$filename\"");
164
        }
165
166 4
        return $this->fpassthru();
167
    }
168
169
    /**
170
     * Outputs all data from the CSV
171
     *
172
     * @return int Returns the number of characters read from the handle
173
     *             and passed through to the output.
174
     */
175 36
    protected function fpassthru(): int
176
    {
177 36
        $bom = '';
178 36
        $input_bom = $this->getInputBOM();
179 36
        if ($this->output_bom && $input_bom != $this->output_bom) {
180 4
            $bom = $this->output_bom;
181
        }
182
183 36
        $this->document->rewind();
184 36
        if ('' !== $bom) {
185 4
            $this->document->fseek(mb_strlen($input_bom));
186
        }
187 36
        echo $bom;
188 36
        $res = $this->document->fpassthru();
189
190 36
        return $res + strlen($bom);
191
    }
192
193
    /**
194
     * Retrieves the CSV content
195
     *
196
     * @return string
197
     */
198 32
    public function __toString(): string
199
    {
200 32
        ob_start();
201 32
        $this->fpassthru();
202
203 32
        return ob_get_clean();
204
    }
205
206
    /**
207
     * @inheritdoc
208
     */
209 2
    public function __clone()
210
    {
211 2
        throw new LogicException('An object of class '.get_class($this).' cannot be cloned');
212
    }
213
}
214