Passed
Push — master ( 99864b...6998a3 )
by Daniel
02:17
created

Writer::saveToFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
ccs 4
cts 4
cp 1
crap 1
1
<?php declare(strict_types=1);
2
3
/**
4
 * This file is part of the Csv-Machine package.
5
 *
6
 * (c) Dan McAdams <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace RoadBunch\Csv;
13
14
use RoadBunch\Csv\Exception\InvalidInputArrayException;
15
use RoadBunch\Csv\Formatter\FormatterInterface;
16
use RoadBunch\Csv\Header\Header;
17
18
/**
19
 * Class Writer
20
 *
21
 * @author  Dan McAdams
22
 * @package RoadBunch\Csv
23
 */
24
class Writer extends Csv implements WriterInterface
25
{
26
    /** @var array */
27
    protected $header = [];
28
29
    /** @var array */
30
    protected $rows = [];
31
32
    /** @var bool */
33
    protected $isStreamSeekable = false;
34
35
    /** @var resource|bool */
36
    protected $handle;
37
38
    /**
39
     * @param array $header
40
     * @param FormatterInterface[] $formatters
41
     * @throws InvalidInputArrayException
42
     */
43 5
    public function setHeader(array $header, array $formatters = [])
44
    {
45 5
        $header = new Header($header);
46 5
        foreach ($formatters as $formatter) {
47 1
            $header->addFormatter($formatter);
48
        }
49 5
        $this->header = $header->getFormattedColumns();
50 5
    }
51
52
    /**
53
     * @param  array $row
54
     * @return WriterInterface
55
     */
56 1
    public function addRow(array $row): WriterInterface
57
    {
58 1
        $this->rows[] = $row;
59 1
        return $this;
60
    }
61
62
    /**
63
     * @param array $rows
64
     * @throws InvalidInputArrayException
65
     */
66 4
    public function addRows(array $rows)
67
    {
68 4
        foreach ($rows as $row) {
69 4
            if (!is_array($row)) {
70 1
                throw new InvalidInputArrayException('Element must be an array');
71
            }
72 3
            $this->rows[] = $row;
73
        }
74 3
    }
75
76
    /**
77
     * Write the CSV to the stream
78
     *
79
     * @param string $filename
80
     */
81 5
    public function saveToFile(string $filename)
82
    {
83 5
        $this->openStream($filename);
84 5
        $this->writeRows();
85 5
        $this->closeStream();
86 5
    }
87
88
    /**
89
     * @return string
90
     */
91 1
    public function writeToString(): string
92
    {
93 1
        ob_start();
94 1
        $this->saveToFile('php://output');
95 1
        return ob_get_clean();
96
    }
97
98
    /**
99
     * @param string $filename
100
     */
101 5
    private function openStream(string $filename)
102
    {
103 5
        $this->handle = fopen($filename, 'w+');
104 5
        $this->setSeekableFlag();
105 5
    }
106
107
    /**
108
     * Close the stream
109
     */
110 5
    private function closeStream()
111
    {
112 5
        fclose($this->handle);
0 ignored issues
show
Bug introduced by
It seems like $this->handle can also be of type boolean; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

112
        fclose(/** @scrutinizer ignore-type */ $this->handle);
Loading history...
113 5
    }
114
115
    /**
116
     * If the requested newline is not PHP default \n update the newline character
117
     */
118 3
    private function updateNewLine()
119
    {
120 3
        if ((Newline::NEWLINE_LF !== $this->newline) && (0 === fseek($this->handle, -1, SEEK_CUR))) {
0 ignored issues
show
Bug introduced by
It seems like $this->handle can also be of type boolean; however, parameter $handle of fseek() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

120
        if ((Newline::NEWLINE_LF !== $this->newline) && (0 === fseek(/** @scrutinizer ignore-type */ $this->handle, -1, SEEK_CUR))) {
Loading history...
121 1
            fwrite($this->handle, $this->newline);
0 ignored issues
show
Bug introduced by
It seems like $this->handle can also be of type boolean; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

121
            fwrite(/** @scrutinizer ignore-type */ $this->handle, $this->newline);
Loading history...
122
        }
123 3
    }
124
125
    /**
126
     * Write a row to the stream, if needed, update line endings
127
     *
128
     * @param $row
129
     */
130 5
    private function writeRow(array $row)
131
    {
132 5
        fputcsv($this->handle, $row, $this->delimiter, $this->enclosure, $this->escape);
0 ignored issues
show
Bug introduced by
It seems like $this->handle can also be of type boolean; however, parameter $handle of fputcsv() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

132
        fputcsv(/** @scrutinizer ignore-type */ $this->handle, $row, $this->delimiter, $this->enclosure, $this->escape);
Loading history...
133
134 5
        if ($this->isStreamSeekable) {
135 3
            $this->updateNewLine();
136
        }
137 5
    }
138
139
    /**
140
     * Write CSV header and rows to stream
141
     */
142 5
    private function writeRows()
143
    {
144 5
        if (!empty($this->header)) {
145 5
            $this->writeRow($this->header);
146
        }
147 5
        foreach ($this->rows as $row) {
148 5
            $this->writeRow($row);
149
        }
150 5
    }
151
152
    /**
153
     * Checks resource, if it's seekable set the seekable flag
154
     * this will be used when determining if line endings should be updated
155
     */
156 5
    private function setSeekableFlag()
157
    {
158 5
        if (stream_get_meta_data($this->handle)['seekable']) {
0 ignored issues
show
Bug introduced by
It seems like $this->handle can also be of type boolean; however, parameter $stream of stream_get_meta_data() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

158
        if (stream_get_meta_data(/** @scrutinizer ignore-type */ $this->handle)['seekable']) {
Loading history...
159 3
            $this->isStreamSeekable = true;
160 3
            return;
161
        }
162 2
        $this->isStreamSeekable = false;
163 2
    }
164
}
165