Completed
Push — master ( 73fa83...94d593 )
by ignace nyamagana
04:04 queued 02:28
created

Writer::setNewline()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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