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

AbstractCsv   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 182
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 16
lcom 2
cbo 2
dl 0
loc 182
ccs 49
cts 49
cp 1
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A __destruct() 0 5 1
A createFromFileObject() 0 12 2
A createFromStream() 0 4 1
A createFromString() 0 7 1
A createFromPath() 0 8 2
A output() 0 11 2
A fpassthru() 0 17 4
A __toString() 0 7 1
A __clone() 0 4 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 LogicException;
18
use RuntimeException;
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
 * @author  Ignace Nyamagana Butera <[email protected]>
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
     * 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 202
    protected function __construct($document)
69
    {
70 202
        $this->document = $document;
71 202
    }
72
73
    /**
74
     * The destructor
75
     */
76 202
    public function __destruct()
77
    {
78 202
        $this->clearStreamFilter();
79 202
        $this->document = null;
80 202
    }
81
82
    /**
83
     * Return a new {@link AbstractCsv} from a SplFileObject
84
     *
85
     * @param SplFileObject $file
86
     *
87
     * @return static
88
     */
89 192
    public static function createFromFileObject(SplFileObject $file): self
90
    {
91 192
        $csv = new static($file);
92 192
        $controls = $file->getCsvControl();
93 192
        $csv->setDelimiter($controls[0]);
94 192
        $csv->setEnclosure($controls[1]);
95 192
        if (isset($controls[2])) {
96 192
            $csv->setEscape($controls[2]);
97
        }
98
99 192
        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 12
    public static function createFromStream($stream): self
110
    {
111 12
        return new static(new StreamIterator($stream));
112
    }
113
114
    /**
115
     * Return a new {@link AbstractCsv} from a string
116
     *
117
     * @param string $str the string
118
     *
119
     * @return static
120
     */
121 14
    public static function createFromString(string $str): self
122
    {
123 14
        $stream = fopen('php://temp', 'r+');
124 14
        fwrite($stream, $str);
125
126 14
        return new static(new StreamIterator($stream));
127
    }
128
129
    /**
130
     * Return a new {@link AbstractCsv} from a file path
131
     *
132
     * @param string $path      file path
133
     * @param string $open_mode the file open mode flag
134
     *
135
     * @return static
136
     */
137 18
    public static function createFromPath(string $path, string $open_mode = 'r+'): self
138
    {
139 18
        if (!$stream = @fopen($path, $open_mode)) {
140 2
            throw new RuntimeException(error_get_last()['message']);
141
        }
142
143 16
        return new static(new StreamIterator($stream));
144
    }
145
146
    /**
147
     * Outputs all data on the CSV file
148
     *
149
     * @param string $filename CSV downloaded name if present adds extra headers
150
     *
151
     * @return int Returns the number of characters read from the handle
152
     *             and passed through to the output.
153
     */
154 4
    public function output(string $filename = null): int
155
    {
156 4
        if (null !== $filename) {
157 4
            $filename = filter_var($filename, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
158 4
            header('content-type: text/csv');
159 4
            header('content-transfer-encoding: binary');
160 4
            header('content-disposition: attachment; filename="'.rawurlencode($filename).'"');
161
        }
162
163 4
        return $this->fpassthru();
164
    }
165
166
    /**
167
     * Outputs all data from the CSV
168
     *
169
     * @return int Returns the number of characters read from the handle
170
     *             and passed through to the output.
171
     */
172 36
    protected function fpassthru(): int
173
    {
174 36
        $bom = '';
175 36
        $input_bom = $this->getInputBOM();
176 36
        if ($this->output_bom && $input_bom != $this->output_bom) {
177 4
            $bom = $this->output_bom;
178
        }
179
180 36
        $this->document->rewind();
181 36
        if ('' !== $bom) {
182 4
            $this->document->fseek(mb_strlen($input_bom));
183
        }
184 36
        echo $bom;
185 36
        $res = $this->document->fpassthru();
186
187 36
        return $res + strlen($bom);
188
    }
189
190
    /**
191
     * Retrieves the CSV content
192
     *
193
     * @return string
194
     */
195 32
    public function __toString(): string
196
    {
197 32
        ob_start();
198 32
        $this->fpassthru();
199
200 32
        return ob_get_clean();
201
    }
202
203
    /**
204
     * @inheritdoc
205
     */
206 2
    public function __clone()
207
    {
208 2
        throw new LogicException('An object of class '.get_class($this).' cannot be cloned');
209
    }
210
}
211