Passed
Pull Request — develop_3.0 (#485)
by Adrien
03:18
created

WorksheetManager::close()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 5
cts 6
cp 0.8333
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
crap 2.0185
1
<?php
2
3
namespace Box\Spout\Writer\ODS\Manager;
4
5
use Box\Spout\Common\Exception\InvalidArgumentException;
6
use Box\Spout\Common\Exception\IOException;
7
use Box\Spout\Common\Helper\Escaper\ODS as ODSEscaper;
8
use Box\Spout\Common\Helper\StringHelper;
9
use Box\Spout\Writer\Common\Entity\Cell;
10
use Box\Spout\Writer\Common\Entity\Row;
11
use Box\Spout\Writer\Common\Entity\Style\Style;
12
use Box\Spout\Writer\Common\Entity\Worksheet;
13
use Box\Spout\Writer\Common\Manager\WorksheetManagerInterface;
14
use Box\Spout\Writer\ODS\Manager\Style\StyleManager;
15
16
/**
17
 * Class WorksheetManager
18
 * ODS worksheet manager, providing the interfaces to work with ODS worksheets.
19
 */
20
class WorksheetManager implements WorksheetManagerInterface
21
{
22
    /** @var \Box\Spout\Common\Helper\Escaper\ODS Strings escaper */
23
    private $stringsEscaper;
24
25
    /** @var StringHelper String helper */
26
    private $stringHelper;
27
28
    /** @var StyleManager Manages styles */
29
    private $styleManager;
30
31
    /**
32
     * WorksheetManager constructor.
33
     *
34
     * @param StyleManager $styleManager
35
     * @param ODSEscaper $stringsEscaper
36
     * @param StringHelper $stringHelper
37
     */
38 35
    public function __construct(
39
        StyleManager $styleManager,
40
        ODSEscaper $stringsEscaper,
41
        StringHelper $stringHelper
42
    ) {
43 35
        $this->styleManager = $styleManager;
44 35
        $this->stringsEscaper = $stringsEscaper;
45 35
        $this->stringHelper = $stringHelper;
46 35
    }
47
48
    /**
49
     * Prepares the worksheet to accept data
50
     *
51
     * @param Worksheet $worksheet The worksheet to start
52
     * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
53
     * @return void
54
     */
55 35
    public function startSheet(Worksheet $worksheet)
56
    {
57 35
        $sheetFilePointer = fopen($worksheet->getFilePath(), 'w');
58 35
        $this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
59
60 35
        $worksheet->setFilePointer($sheetFilePointer);
61 35
    }
62
63
    /**
64
     * Checks if the sheet has been sucessfully created. Throws an exception if not.
65
     *
66
     * @param bool|resource $sheetFilePointer Pointer to the sheet data file or FALSE if unable to open the file
67
     * @throws IOException If the sheet data file cannot be opened for writing
68
     * @return void
69
     */
70 35
    private function throwIfSheetFilePointerIsNotAvailable($sheetFilePointer)
71
    {
72 35
        if (!$sheetFilePointer) {
73
            throw new IOException('Unable to open sheet for writing.');
74
        }
75 35
    }
76
77
    /**
78
     * Returns the table XML root node as string.
79
     *
80
     * @param Worksheet $worksheet
81
     * @return string <table> node as string
82
     */
83 32
    public function getTableElementStartAsString(Worksheet $worksheet)
84
    {
85 32
        $externalSheet = $worksheet->getExternalSheet();
86 32
        $escapedSheetName = $this->stringsEscaper->escape($externalSheet->getName());
87 32
        $tableStyleName = 'ta' . ($externalSheet->getIndex() + 1);
88
89 32
        $tableElement  = '<table:table table:style-name="' . $tableStyleName . '" table:name="' . $escapedSheetName . '">';
90 32
        $tableElement .= '<table:table-column table:default-cell-style-name="ce1" table:style-name="co1" table:number-columns-repeated="' . $worksheet->getMaxNumColumns() . '"/>';
91
92 32
        return $tableElement;
93
    }
94
95
    /**
96
     * Adds a row to the given worksheet.
97
     *
98
     * @param Worksheet $worksheet The worksheet to add the row to
99
     * @param Row $row The row to be added
100
     * @throws IOException If the data cannot be written
101
     * @throws InvalidArgumentException If a cell value's type is not supported
102
     * @return void
103
     */
104 29
    public function addRow(Worksheet $worksheet, Row $row)
105
    {
106 29
        $cells = $row->getCells();
107 29
        $cellsCount = count($cells);
108 29
        $rowStyle = $row->getStyle();
109
110 29
        $data = '<table:table-row table:style-name="ro1">';
111
112 29
        $currentCellIndex = 0;
113 29
        $nextCellIndex = 1;
114
115 29
        for ($i = 0; $i < $cellsCount; $i++) {
116
            /** @var Cell $cell */
117 29
            $cell = $cells[$currentCellIndex];
118
            /** @var Cell|null $nextCell */
119 29
            $nextCell = isset($cells[$nextCellIndex]) ? $cells[$nextCellIndex] : null;
120
121 29
            if ($nextCell === null || $cell->getValue() !== $nextCell->getValue()) {
122 29
                $data .= $this->applyStyleAndGetCellXML($cell, $rowStyle, $currentCellIndex, $nextCellIndex);
123 28
                $currentCellIndex = $nextCellIndex;
124
            }
125
126 28
            $nextCellIndex++;
127
        }
128
129 28
        $data .= '</table:table-row>';
130
131 28
        $wasWriteSuccessful = fwrite($worksheet->getFilePointer(), $data);
132 28
        if ($wasWriteSuccessful === false) {
133
            throw new IOException("Unable to write data in {$worksheet->getFilePath()}");
134
        }
135
136
        // only update the count if the write worked
137 28
        $lastWrittenRowIndex = $worksheet->getLastWrittenRowIndex();
138 28
        $worksheet->setLastWrittenRowIndex($lastWrittenRowIndex + 1);
139 28
    }
140
141
    /**
142
     * Applies styles to the given style, merging the cell's style with its row's style
143
     * Then builds and returns xml for the cell.
144
     *
145
     * @param Cell $cell
146
     * @param Style $rowStyle
147
     * @param int $currentCellIndex
148
     * @param int $nextCellIndex
149
     * @throws InvalidArgumentException If a cell value's type is not supported
150
     * @return string
151
     */
152 29
    private function applyStyleAndGetCellXML(Cell $cell, Style $rowStyle, $currentCellIndex, $nextCellIndex)
153
    {
154
        // Apply styles - the row style is merged at this point
155 29
        $cell->applyStyle($rowStyle);
156 29
        $this->styleManager->applyExtraStylesIfNeeded($cell);
157 29
        $registeredStyle = $this->styleManager->registerStyle($cell->getStyle());
158 29
        $styleIndex = $registeredStyle->getId() + 1; // 1-based
159
160 29
        $numTimesValueRepeated = ($nextCellIndex - $currentCellIndex);
161
162 29
        return $this->getCellXML($cell, $styleIndex, $numTimesValueRepeated);
163
    }
164
165
    /**
166
     * Returns the cell XML content, given its value.
167
     *
168
     * @param Cell $cell The cell to be written
169
     * @param int $styleIndex Index of the used style
170
     * @param int $numTimesValueRepeated Number of times the value is consecutively repeated
171
     * @throws InvalidArgumentException If a cell value's type is not supported
172
     * @return string The cell XML content
173
     */
174 29
    private function getCellXML(Cell $cell, $styleIndex, $numTimesValueRepeated)
175
    {
176 29
        $data = '<table:table-cell table:style-name="ce' . $styleIndex . '"';
177
178 29
        if ($numTimesValueRepeated !== 1) {
179 4
            $data .= ' table:number-columns-repeated="' . $numTimesValueRepeated . '"';
180
        }
181
182 29
        if ($cell->isString()) {
183 25
            $data .= ' office:value-type="string" calcext:value-type="string">';
184
185 25
            $cellValueLines = explode("\n", $cell->getValue());
186 25
            foreach ($cellValueLines as $cellValueLine) {
187 25
                $data .= '<text:p>' . $this->stringsEscaper->escape($cellValueLine) . '</text:p>';
188
            }
189
190 25
            $data .= '</table:table-cell>';
191 6
        } elseif ($cell->isBoolean()) {
192 2
            $data .= ' office:value-type="boolean" calcext:value-type="boolean" office:boolean-value="' . $cell->getValue() . '">';
193 2
            $data .= '<text:p>' . $cell->getValue() . '</text:p>';
194 2
            $data .= '</table:table-cell>';
195 5
        } elseif ($cell->isNumeric()) {
196 2
            $data .= ' office:value-type="float" calcext:value-type="float" office:value="' . $cell->getValue() . '">';
197 2
            $data .= '<text:p>' . $cell->getValue() . '</text:p>';
198 2
            $data .= '</table:table-cell>';
199 4
        } elseif ($cell->isEmpty()) {
200 2
            $data .= '/>';
201
        } else {
202 2
            throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cell->getValue()));
203
        }
204
205 28
        return $data;
206
    }
207
208
    /**
209
     * Closes the worksheet
210
     *
211
     * @param Worksheet $worksheet
212
     * @return void
213
     */
214 32
    public function close(Worksheet $worksheet)
215
    {
216 32
        $worksheetFilePointer = $worksheet->getFilePointer();
217
218 32
        if (!is_resource($worksheetFilePointer)) {
219
            return;
220
        }
221
222 32
        fclose($worksheetFilePointer);
223 32
    }
224
}
225