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

WorksheetManager::getCellXML()   C

Complexity

Conditions 7
Paths 12

Size

Total Lines 33
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 33
ccs 0
cts 22
cp 0
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 23
nc 12
nop 3
crap 56
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\StringHelper;
8
use Box\Spout\Writer\Common\Entity\Cell;
9
use Box\Spout\Writer\Common\Entity\Row;
10
use Box\Spout\Writer\Common\Entity\Worksheet;
11
use Box\Spout\Writer\Common\Manager\WorksheetManagerInterface;
12
use Box\Spout\Writer\ODS\Manager\Style\StyleManager;
13
14
/**
15
 * Class WorksheetManager
16
 * ODS worksheet manager, providing the interfaces to work with ODS worksheets.
17
 *
18
 * @package Box\Spout\Writer\ODS\Manager
19
 */
20
class WorksheetManager implements WorksheetManagerInterface
21
{
22
    /** @var \Box\Spout\Common\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 \Box\Spout\Common\Escaper\ODS $stringsEscaper
35
     * @param StringHelper $stringHelper
36
     */
37 38
    public function __construct(
38
        StyleManager $styleManager,
39
        \Box\Spout\Common\Escaper\ODS $stringsEscaper,
40
        StringHelper $stringHelper)
41
    {
42 38
        $this->stringsEscaper = $stringsEscaper;
43 38
        $this->stringHelper = $stringHelper;
44 38
        $this->styleManager = $styleManager;
45 38
    }
46
47
    /**
48
     * Prepares the worksheet to accept data
49
     *
50
     * @param Worksheet $worksheet The worksheet to start
51
     * @return void
52
     * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
53
     */
54 38
    public function startSheet(Worksheet $worksheet)
55
    {
56 38
        $sheetFilePointer = fopen($worksheet->getFilePath(), 'w');
57 38
        $this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
58
59 38
        $worksheet->setFilePointer($sheetFilePointer);
60 38
    }
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
     * @return void
67
     * @throws IOException If the sheet data file cannot be opened for writing
68
     */
69 38
    private function throwIfSheetFilePointerIsNotAvailable($sheetFilePointer)
70
    {
71 38
        if (!$sheetFilePointer) {
72
            throw new IOException('Unable to open sheet for writing.');
73
        }
74 38
    }
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 4
    public function getTableElementStartAsString(Worksheet $worksheet)
83
    {
84 4
        $externalSheet = $worksheet->getExternalSheet();
85 4
        $escapedSheetName = $this->stringsEscaper->escape($externalSheet->getName());
86 4
        $tableStyleName = 'ta' . ($externalSheet->getIndex() + 1);
87
88 4
        $tableElement  = '<table:table table:style-name="' . $tableStyleName . '" table:name="' . $escapedSheetName . '">';
89 4
        $tableElement .= '<table:table-column table:default-cell-style-name="ce1" table:style-name="co1" table:number-columns-repeated="' . $worksheet->getMaxNumColumns() . '"/>';
90
91 4
        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
     */
104
    public function addRow(Worksheet $worksheet, Row $row)
105
    {
106
107
        $cells = $row->getCells();
108
        $cellsCount = count($cells);
109
110
        $data = '<table:table-row table:style-name="ro1">';
111
112
        $currentCellIndex = 0;
113
        $nextCellIndex = 1;
114
115
        for ($i = 0; $i < $cellsCount; $i++) {
116
117
            /** @var Cell $cell */
118
            $cell = $cells[$currentCellIndex];
119
            /** @var Cell|null $nextCell */
120
            $nextCell = isset($cells[$nextCellIndex]) ? $cells[$nextCellIndex] : null;
121
122
            // @TODO refactoring: move this to its own method
123
            if (null === $nextCell || $cell->getValue() !== $nextCell->getValue()) {
124
125
                // Apply styles - the row style is merged at this point
126
                $cell->applyStyle($row->getStyle());
127
                $this->styleManager->applyExtraStylesIfNeeded($cell);
128
                $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...
129
                $styleIndex = $registeredStyle->getId() + 1; // 1-based
130
131
                $numTimesValueRepeated = ($nextCellIndex - $currentCellIndex);
132
                $data .= $this->getCellXML($cell, $styleIndex, $numTimesValueRepeated);
133
                $currentCellIndex = $nextCellIndex;
134
            }
135
136
            $nextCellIndex++;
137
        }
138
139
        $data .= '</table:table-row>';
140
141
        $wasWriteSuccessful = fwrite($worksheet->getFilePointer(), $data);
142
        if ($wasWriteSuccessful === false) {
143
            throw new IOException("Unable to write data in {$worksheet->getFilePath()}");
144
        }
145
146
        // only update the count if the write worked
147
        $lastWrittenRowIndex = $worksheet->getLastWrittenRowIndex();
148
        $worksheet->setLastWrittenRowIndex($lastWrittenRowIndex + 1);
149
    }
150
151
    /**
152
     * Returns the cell XML content, given its value.
153
     *
154
     * @param Cell $cell The cell to be written
155
     * @param int $styleIndex Index of the used style
156
     * @param int $numTimesValueRepeated Number of times the value is consecutively repeated
157
     * @return string The cell XML content
158
     * @throws \Box\Spout\Common\Exception\InvalidArgumentException If a cell value's type is not supported
159
     */
160
    protected function getCellXML(Cell $cell, $styleIndex, $numTimesValueRepeated)
161
    {
162
        $data = '<table:table-cell table:style-name="ce' . $styleIndex . '"';
163
164
        if ($numTimesValueRepeated !== 1) {
165
            $data .= ' table:number-columns-repeated="' . $numTimesValueRepeated . '"';
166
        }
167
168
        if ($cell->isString()) {
169
            $data .= ' office:value-type="string" calcext:value-type="string">';
170
171
            $cellValueLines = explode("\n", $cell->getValue());
172
            foreach ($cellValueLines as $cellValueLine) {
173
                $data .= '<text:p>' . $this->stringsEscaper->escape($cellValueLine) . '</text:p>';
174
            }
175
176
            $data .= '</table:table-cell>';
177
        } else if ($cell->isBoolean()) {
178
            $data .= ' office:value-type="boolean" calcext:value-type="boolean" office:boolean-value="' . $cell->getValue() . '">';
179
            $data .= '<text:p>' . $cell->getValue() . '</text:p>';
180
            $data .= '</table:table-cell>';
181
        } else if ($cell->isNumeric()) {
182
            $data .= ' office:value-type="float" calcext:value-type="float" office:value="' . $cell->getValue() . '">';
183
            $data .= '<text:p>' . $cell->getValue() . '</text:p>';
184
            $data .= '</table:table-cell>';
185
        } else if ($cell->isEmpty()) {
186
            $data .= '/>';
187
        } else {
188
            throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cell->getValue()));
189
        }
190
191
        return $data;
192
    }
193
194
    /**
195
     * Closes the worksheet
196
     *
197
     * @param Worksheet $worksheet
198
     * @return void
199
     */
200 4
    public function close(Worksheet $worksheet)
201
    {
202 4
        $worksheetFilePointer = $worksheet->getFilePointer();
203
204 4
        if (!is_resource($worksheetFilePointer)) {
205
            return;
206
        }
207
208 4
        fclose($worksheetFilePointer);
209
    }
210
}