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

Writer::consolidate()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

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