Writer   A
last analyzed

Coupling/Cohesion

Components 1
Dependencies 2

Complexity

Total Complexity 21

Size/Duplication

Total Lines 230
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 230
ccs 57
cts 57
cp 1
rs 10
c 0
b 0
f 0
wmc 21
lcom 1
cbo 2

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getNewline() 0 4 1
A getFlushThreshold() 0 4 1
A insertAll() 0 16 3
A insertOne() 0 11 2
A formatRecord() 0 4 1
A validateRecord() 0 8 3
A consolidate() 0 20 4
A addFormatter() 0 6 1
A addValidator() 0 6 1
A setNewline() 0 6 1
A setFlushThreshold() 0 15 3
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\Exception\InsertionException;
18
use Traversable;
19
use TypeError;
20
21
/**
22
 * A class to manage records insertion into a CSV Document
23
 *
24
 * @package League.csv
25
 * @since   4.0.0
26
 * @author  Ignace Nyamagana Butera <[email protected]>
27
 *
28
 */
29
class Writer extends AbstractCsv
30
{
31
    /**
32
     * callable collection to format the record before insertion
33
     *
34
     * @var callable[]
35
     */
36
    protected $formatters = [];
37
38
    /**
39
     * callable collection to validate the record before insertion
40
     *
41
     * @var callable[]
42
     */
43
    protected $validators = [];
44
45
    /**
46
     * newline character
47
     *
48
     * @var string
49
     */
50
    protected $newline = "\n";
51
52
    /**
53
     * Insert records count for flushing
54
     *
55
     * @var int
56
     */
57
    protected $flush_counter = 0;
58
59
    /**
60
     * Buffer flush threshold
61
     *
62
     * @var int|null
63
     */
64
    protected $flush_threshold;
65
66
    /**
67
     * @inheritdoc
68
     */
69
    protected $stream_filter_mode = STREAM_FILTER_WRITE;
70
71
    /**
72
     * Returns the current newline sequence characters
73
     *
74
     * @return string
75
     */
76 2
    public function getNewline(): string
77
    {
78 2
        return $this->newline;
79
    }
80
81
    /**
82
     * Get the flush threshold
83
     *
84
     * @return int|null
85
     */
86 2
    public function getFlushThreshold()
87
    {
88 2
        return $this->flush_threshold;
89
    }
90
91
    /**
92
     * Adds multiple lines to the CSV document
93
     *
94
     * a simple wrapper method around insertOne
95
     *
96
     * @param Traversable|array $records a multidimensional array or a Traversable object
97
     *
98
     * @return int
99
     */
100 6
    public function insertAll($records): int
101
    {
102 6
        if (!is_iterable($records)) {
103 2
            throw new TypeError(sprintf('%s() expects argument passed to be iterable, %s given', __METHOD__, gettype($records)));
104
        }
105
106 4
        $bytes = 0;
107 4
        foreach ($records as $record) {
108 4
            $bytes += $this->insertOne($record);
109
        }
110
111 4
        $this->flush_counter = 0;
112 4
        $this->document->fflush();
113
114 4
        return $bytes;
115
    }
116
117
    /**
118
     * Adds a single line to a CSV document
119
     *
120
     * @param string[] $record an array
121
     *
122
     * @throws InsertionException If the record can not be inserted
123
     *
124
     * @return int
125
     */
126 16
    public function insertOne(array $record): int
127
    {
128 16
        $record = array_reduce($this->formatters, [$this, 'formatRecord'], $record);
129 16
        $this->validateRecord($record);
130 14
        $bytes = $this->document->fputcsv($record, ...$this->document->getCsvControl());
131 14
        if (!$bytes) {
132 2
            throw InsertionException::createFromStream($record);
133
        }
134
135 12
        return $bytes + $this->consolidate();
136
    }
137
138
    /**
139
     * Format the given record
140
     *
141
     * @param string[] $record
142
     * @param callable $formatter
143
     *
144
     * @return string[]
145
     */
146 2
    protected function formatRecord(array $record, callable $formatter): array
147
    {
148 2
        return $formatter($record);
149
    }
150
151
    /**
152
     * Validate a record
153
     *
154
     * @param string[] $record
155
     *
156
     * @throws InsertionException If the validation failed
157
     */
158 8
    protected function validateRecord(array $record)
159
    {
160 8
        foreach ($this->validators as $name => $validator) {
161 2
            if (true !== $validator($record)) {
162 2
                throw InsertionException::createFromValidator($name, $record);
163
            }
164
        }
165 6
    }
166
167
    /**
168
     * Apply post insertion actions
169
     *
170
     * @return int
171
     */
172 8
    protected function consolidate(): int
173
    {
174 8
        $bytes = 0;
175 8
        if ("\n" !== $this->newline) {
176 2
            $this->document->fseek(-1, SEEK_CUR);
177 2
            $bytes = $this->document->fwrite($this->newline, strlen($this->newline)) - 1;
178
        }
179
180 8
        if (null === $this->flush_threshold) {
181 6
            return $bytes;
182
        }
183
184 2
        ++$this->flush_counter;
185 2
        if (0 === $this->flush_counter % $this->flush_threshold) {
186 2
            $this->flush_counter = 0;
187 2
            $this->document->fflush();
188
        }
189
190 2
        return $bytes;
191
    }
192
193
    /**
194
     * add a formatter to the collection
195
     *
196
     * @param callable $formatter
197
     *
198
     * @return static
199
     */
200 2
    public function addFormatter(callable $formatter): self
201
    {
202 2
        $this->formatters[] = $formatter;
203
204 2
        return $this;
205
    }
206
207
    /**
208
     * add a Validator to the collection
209
     *
210
     * @param callable $validator
211
     * @param string   $validator_name the validator name
212
     *
213
     * @return static
214
     */
215 2
    public function addValidator(callable $validator, string $validator_name): self
216
    {
217 2
        $this->validators[$validator_name] = $validator;
218
219 2
        return $this;
220
    }
221
222
    /**
223
     * Sets the newline sequence characters
224
     *
225
     * @param string $newline
226
     *
227
     * @return static
228
     */
229 2
    public function setNewline(string $newline): self
230
    {
231 2
        $this->newline = $newline;
232
233 2
        return $this;
234
    }
235
236
    /**
237
     * Set the automatic flush threshold on write
238
     *
239
     * @param int|null $threshold
240
     *
241
     * @return static
242
     */
243 6
    public function setFlushThreshold($threshold): self
244
    {
245 6
        $this->filterNullableInteger($threshold, 1, __METHOD__.'() expects 1 Argument to be null or a valid integer greater or equal to 1');
246 4
        if ($threshold === $this->flush_threshold) {
247 2
            return $this;
248
        }
249
250 4
        $this->flush_threshold = $threshold;
251 4
        if (0 < $this->flush_counter) {
252 2
            $this->flush_counter = 0;
253 2
            $this->document->fflush();
254
        }
255
256 4
        return $this;
257
    }
258
}
259