Writer   A
last analyzed

Coupling/Cohesion

Components 1
Dependencies 3

Complexity

Total Complexity 23

Size/Duplication

Total Lines 252
Duplicated Lines 0 %

Test Coverage

Coverage 98.31%

Importance

Changes 0
Metric Value
dl 0
loc 252
ccs 58
cts 59
cp 0.9831
rs 10
c 0
b 0
f 0
wmc 23
lcom 1
cbo 3

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