Failed Conditions
Pull Request — develop_3.0 (#434)
by Hura
03:05
created

WorksheetManager::addRow()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 46
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 6.0023

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 46
ccs 24
cts 25
cp 0.96
rs 8.4751
cc 6
eloc 24
nc 10
nop 2
crap 6.0023
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\Creator\EntityFactory;
10
use Box\Spout\Writer\Common\Entity\Cell;
11
use Box\Spout\Writer\Common\Entity\Row;
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
     * @param StyleManager $styleManager
34
     * @param ODSEscaper $stringsEscaper
35
     * @param StringHelper $stringHelper
36
     */
37 41
    public function __construct(
38
        StyleManager $styleManager,
39
        ODSEscaper $stringsEscaper,
40
        StringHelper $stringHelper)
41
    {
42 41
        $this->stringsEscaper = $stringsEscaper;
43 41
        $this->stringHelper = $stringHelper;
44 41
        $this->styleManager = $styleManager;
45 41
    }
46
47
    /**
48
     * Prepares the worksheet to accept data
49
     *
50
     * @param Worksheet $worksheet The worksheet to start
51
     * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
52
     * @return void
53
     */
54 41
    public function startSheet(Worksheet $worksheet)
55
    {
56 41
        $sheetFilePointer = fopen($worksheet->getFilePath(), 'w');
57 41
        $this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
58
59 41
        $worksheet->setFilePointer($sheetFilePointer);
60 41
    }
61
62
    /**
63
     * Checks if the sheet has been sucessfully created. Throws an exception if not.
64
     *
65
     * @param bool|resource $sheetFilePointer Pointer to the sheet data file or FALSE if unable to open the file
66
     * @throws IOException If the sheet data file cannot be opened for writing
67
     * @return void
68
     */
69 41
    private function throwIfSheetFilePointerIsNotAvailable($sheetFilePointer)
70
    {
71 41
        if (!$sheetFilePointer) {
72
            throw new IOException('Unable to open sheet for writing.');
73
        }
74 41
    }
75
76
    /**
77
     * Returns the table XML root node as string.
78
     *
79
     * @param Worksheet $worksheet
80
     * @return string <table> node as string
81
     */
82 34
    public function getTableElementStartAsString(Worksheet $worksheet)
83
    {
84 34
        $externalSheet = $worksheet->getExternalSheet();
85 34
        $escapedSheetName = $this->stringsEscaper->escape($externalSheet->getName());
86 34
        $tableStyleName = 'ta' . ($externalSheet->getIndex() + 1);
87
88 34
        $tableElement  = '<table:table table:style-name="' . $tableStyleName . '" table:name="' . $escapedSheetName . '">';
89 34
        $tableElement .= '<table:table-column table:default-cell-style-name="ce1" table:style-name="co1" table:number-columns-repeated="' . $worksheet->getMaxNumColumns() . '"/>';
90
91 34
        return $tableElement;
92
    }
93
94
    /**
95
     * Adds a row to the worksheet.
96
     *
97
     * @param Worksheet $worksheet The worksheet to add the row to
98
     * @param Row $row The row to be added
99
     * @return void
100
     *
101
     * @throws IOException If the data cannot be written
102
     * @throws InvalidArgumentException If a cell value's type is not supported
103
     * @return void
104
     */
105 30
    public function addRow(Worksheet $worksheet, Row $row)
106
    {
107
108 30
        $cells = $row->getCells();
109 30
        $cellsCount = count($cells);
110
111 30
        $data = '<table:table-row table:style-name="ro1">';
112
113 30
        $currentCellIndex = 0;
114 30
        $nextCellIndex = 1;
115
116 30
        for ($i = 0; $i < $cellsCount; $i++) {
117
118
            /** @var Cell $cell */
119 30
            $cell = $cells[$currentCellIndex];
120
            /** @var Cell|null $nextCell */
121 30
            $nextCell = isset($cells[$nextCellIndex]) ? $cells[$nextCellIndex] : null;
122
123
            // @TODO refactoring: move this to its own method
124 30
            if (null === $nextCell || $cell->getValue() !== $nextCell->getValue()) {
125
126
                // Apply styles - the row style is merged at this point
127 30
                $cell->applyStyle($row->getStyle());
128 30
                $this->styleManager->applyExtraStylesIfNeeded($cell);
129 30
                $registeredStyle = $this->styleManager->registerStyle($cell->getStyle());
0 ignored issues
show
Bug introduced by
It seems like $cell->getStyle() can be null; however, registerStyle() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
130 30
                $styleIndex = $registeredStyle->getId() + 1; // 1-based
131
132 30
                $numTimesValueRepeated = ($nextCellIndex - $currentCellIndex);
133 30
                $data .= $this->getCellXML($cell, $styleIndex, $numTimesValueRepeated);
134 29
                $currentCellIndex = $nextCellIndex;
135
            }
136
137 29
            $nextCellIndex++;
138
        }
139
140 29
        $data .= '</table:table-row>';
141
142 29
        $wasWriteSuccessful = fwrite($worksheet->getFilePointer(), $data);
143 29
        if ($wasWriteSuccessful === false) {
144
            throw new IOException("Unable to write data in {$worksheet->getFilePath()}");
145
        }
146
147
        // only update the count if the write worked
148 29
        $lastWrittenRowIndex = $worksheet->getLastWrittenRowIndex();
149 29
        $worksheet->setLastWrittenRowIndex($lastWrittenRowIndex + 1);
150 29
    }
151
152
    /**
153
     * Returns the cell XML content, given its value.
154
     *
155
     * @param Cell $cell The cell to be written
156
     * @param int $styleIndex Index of the used style
157
     * @param int $numTimesValueRepeated Number of times the value is consecutively repeated
158
     * @throws \Box\Spout\Common\Exception\InvalidArgumentException If a cell value's type is not supported
159
     * @return string The cell XML content
160
     */
161 30
    protected function getCellXML(Cell $cell, $styleIndex, $numTimesValueRepeated)
162
    {
163 30
        $data = '<table:table-cell table:style-name="ce' . $styleIndex . '"';
164
165 30
        if ($numTimesValueRepeated !== 1) {
166 4
            $data .= ' table:number-columns-repeated="' . $numTimesValueRepeated . '"';
167
        }
168
169 30
        if ($cell->isString()) {
170 26
            $data .= ' office:value-type="string" calcext:value-type="string">';
171
172 26
            $cellValueLines = explode("\n", $cell->getValue());
173 26
            foreach ($cellValueLines as $cellValueLine) {
174 26
                $data .= '<text:p>' . $this->stringsEscaper->escape($cellValueLine) . '</text:p>';
175
            }
176
177 26
            $data .= '</table:table-cell>';
178 6
        } elseif ($cell->isBoolean()) {
179 3
            $data .= ' office:value-type="boolean" calcext:value-type="boolean" office:boolean-value="' . $cell->getValue() . '">';
180 3
            $data .= '<text:p>' . $cell->getValue() . '</text:p>';
181 3
            $data .= '</table:table-cell>';
182 5
        } elseif ($cell->isNumeric()) {
183 3
            $data .= ' office:value-type="float" calcext:value-type="float" office:value="' . $cell->getValue() . '">';
184 3
            $data .= '<text:p>' . $cell->getValue() . '</text:p>';
185 3
            $data .= '</table:table-cell>';
186 3
        } elseif ($cell->isEmpty()) {
187 2
            $data .= '/>';
188
        } else {
189 1
            throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cell->getValue()));
190
        }
191
192 29
        return $data;
193
    }
194
195
    /**
196
     * Closes the worksheet
197
     *
198
     * @param Worksheet $worksheet
199
     * @return void
200
     */
201 34
    public function close(Worksheet $worksheet)
202
    {
203 34
        $worksheetFilePointer = $worksheet->getFilePointer();
204
205 34
        if (!is_resource($worksheetFilePointer)) {
206
            return;
207
        }
208
209 34
        fclose($worksheetFilePointer);
210 34
    }
211
}
212