Failed Conditions
Pull Request — master (#3876)
by Abdul Malik
22:45 queued 13:31
created

Content   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 335
Duplicated Lines 0 %

Test Coverage

Coverage 90.78%

Importance

Changes 0
Metric Value
wmc 43
eloc 213
dl 0
loc 335
ccs 187
cts 206
cp 0.9078
rs 8.96
c 0
b 0
f 0

How to fix   Complexity   

Complex Class

Complex classes like Content often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Content, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
6
use PhpOffice\PhpSpreadsheet\Cell\Cell;
7
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
8
use PhpOffice\PhpSpreadsheet\Cell\DataType;
9
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
10
use PhpOffice\PhpSpreadsheet\Spreadsheet;
11
use PhpOffice\PhpSpreadsheet\Worksheet\RowCellIterator;
12
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
13
use PhpOffice\PhpSpreadsheet\Writer\Ods;
14
use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Comment;
15
use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Style;
16
17
/**
18
 * @author     Alexander Pervakov <[email protected]>
19
 */
20
class Content extends WriterPart
21
{
22
    public const NUMBER_COLS_REPEATED_MAX = 1024;
23
    public const NUMBER_ROWS_REPEATED_MAX = 1_048_576;
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting ',' or ';' on line 23 at column 45
Loading history...
24
25
    private Formula $formulaConvertor;
26
27
    /**
28
     * Set parent Ods writer.
29
     */
30 30
    public function __construct(Ods $writer)
31
    {
32 30
        parent::__construct($writer);
33
34 30
        $this->formulaConvertor = new Formula($this->getParentWriter()->getSpreadsheet()->getDefinedNames());
35
    }
36
37
    /**
38
     * Write content.xml to XML format.
39
     *
40
     * @return string XML Output
41
     */
42 29
    public function write(): string
43
    {
44 29
        $objWriter = null;
45 29
        if ($this->getParentWriter()->getUseDiskCaching()) {
46
            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
47
        } else {
48 29
            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
49
        }
50
51
        // XML header
52 29
        $objWriter->startDocument('1.0', 'UTF-8');
53
54
        // Content
55 29
        $objWriter->startElement('office:document-content');
56 29
        $objWriter->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0');
57 29
        $objWriter->writeAttribute('xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0');
58 29
        $objWriter->writeAttribute('xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0');
59 29
        $objWriter->writeAttribute('xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0');
60 29
        $objWriter->writeAttribute('xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0');
61 29
        $objWriter->writeAttribute('xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0');
62 29
        $objWriter->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
63 29
        $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
64 29
        $objWriter->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0');
65 29
        $objWriter->writeAttribute('xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0');
66 29
        $objWriter->writeAttribute('xmlns:presentation', 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0');
67 29
        $objWriter->writeAttribute('xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0');
68 29
        $objWriter->writeAttribute('xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0');
69 29
        $objWriter->writeAttribute('xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0');
70 29
        $objWriter->writeAttribute('xmlns:math', 'http://www.w3.org/1998/Math/MathML');
71 29
        $objWriter->writeAttribute('xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0');
72 29
        $objWriter->writeAttribute('xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0');
73 29
        $objWriter->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office');
74 29
        $objWriter->writeAttribute('xmlns:ooow', 'http://openoffice.org/2004/writer');
75 29
        $objWriter->writeAttribute('xmlns:oooc', 'http://openoffice.org/2004/calc');
76 29
        $objWriter->writeAttribute('xmlns:dom', 'http://www.w3.org/2001/xml-events');
77 29
        $objWriter->writeAttribute('xmlns:xforms', 'http://www.w3.org/2002/xforms');
78 29
        $objWriter->writeAttribute('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema');
79 29
        $objWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
80 29
        $objWriter->writeAttribute('xmlns:rpt', 'http://openoffice.org/2005/report');
81 29
        $objWriter->writeAttribute('xmlns:of', 'urn:oasis:names:tc:opendocument:xmlns:of:1.2');
82 29
        $objWriter->writeAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml');
83 29
        $objWriter->writeAttribute('xmlns:grddl', 'http://www.w3.org/2003/g/data-view#');
84 29
        $objWriter->writeAttribute('xmlns:tableooo', 'http://openoffice.org/2009/table');
85 29
        $objWriter->writeAttribute('xmlns:field', 'urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0');
86 29
        $objWriter->writeAttribute('xmlns:formx', 'urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0');
87 29
        $objWriter->writeAttribute('xmlns:css3t', 'http://www.w3.org/TR/css3-text/');
88 29
        $objWriter->writeAttribute('office:version', '1.2');
89
90 29
        $objWriter->writeElement('office:scripts');
91 29
        $objWriter->writeElement('office:font-face-decls');
92
93
        // Styles XF
94 29
        $objWriter->startElement('office:automatic-styles');
95 29
        $this->writeXfStyles($objWriter, $this->getParentWriter()->getSpreadsheet());
96 29
        $objWriter->endElement();
97
98 29
        $objWriter->startElement('office:body');
99 29
        $objWriter->startElement('office:spreadsheet');
100 29
        $objWriter->writeElement('table:calculation-settings');
101
102 29
        $this->writeSheets($objWriter);
103
104 29
        (new AutoFilters($objWriter, $this->getParentWriter()->getSpreadsheet()))->write();
105
        // Defined names (ranges and formulae)
106 29
        (new NamedExpressions($objWriter, $this->getParentWriter()->getSpreadsheet(), $this->formulaConvertor))->write();
107
108 29
        $objWriter->endElement();
109 29
        $objWriter->endElement();
110 29
        $objWriter->endElement();
111
112 29
        return $objWriter->getData();
113
    }
114
115
    /**
116
     * Write sheets.
117
     */
118 29
    private function writeSheets(XMLWriter $objWriter): void
119
    {
120 29
        $spreadsheet = $this->getParentWriter()->getSpreadsheet();
121 29
        $sheetCount = $spreadsheet->getSheetCount();
122 29
        for ($sheetIndex = 0; $sheetIndex < $sheetCount; ++$sheetIndex) {
123 29
            $objWriter->startElement('table:table');
124 29
            $objWriter->writeAttribute('table:name', $spreadsheet->getSheet($sheetIndex)->getTitle());
125 29
            $objWriter->writeAttribute('table:style-name', Style::TABLE_STYLE_PREFIX . (string) ($sheetIndex + 1));
126 29
            $objWriter->writeElement('office:forms');
127 29
            $lastColumn = 0;
128 29
            foreach ($spreadsheet->getSheet($sheetIndex)->getColumnDimensions() as $columnDimension) {
129 5
                $thisColumn = $columnDimension->getColumnNumeric();
130 5
                $emptyColumns = $thisColumn - $lastColumn - 1;
131 5
                if ($emptyColumns > 0) {
132 4
                    $objWriter->startElement('table:table-column');
133 4
                    $objWriter->writeAttribute('table:number-columns-repeated', (string) $emptyColumns);
134 4
                    $objWriter->endElement();
135
                }
136 5
                $lastColumn = $thisColumn;
137 5
                $objWriter->startElement('table:table-column');
138 5
                $objWriter->writeAttribute(
139 5
                    'table:style-name',
140 5
                    sprintf('%s_%d_%d', Style::COLUMN_STYLE_PREFIX, $sheetIndex, $columnDimension->getColumnNumeric())
141 5
                );
142 5
                $objWriter->writeAttribute('table:default-cell-style-name', 'ce0');
143
//                $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX);
144 5
                $objWriter->endElement();
145
            }
146 29
            $this->writeRows($objWriter, $spreadsheet->getSheet($sheetIndex), $sheetIndex);
147 29
            $objWriter->endElement();
148
        }
149
    }
150
151
    /**
152
     * Write rows of the specified sheet.
153
     */
154 29
    private function writeRows(XMLWriter $objWriter, Worksheet $sheet, int $sheetIndex): void
155
    {
156 29
        $numberRowsRepeated = self::NUMBER_ROWS_REPEATED_MAX;
157 29
        $span_row = 0;
158 29
        $rows = $sheet->getRowIterator();
159 29
        foreach ($rows as $row) {
160 29
            $cellIterator = $row->getCellIterator();
161 29
            --$numberRowsRepeated;
162 29
            if ($cellIterator->valid()) {
163 29
                $objWriter->startElement('table:table-row');
164 29
                if ($span_row) {
165
                    if ($span_row > 1) {
166
                        $objWriter->writeAttribute('table:number-rows-repeated', (string) $span_row);
167
                    }
168
                    $objWriter->startElement('table:table-cell');
169
                    $objWriter->writeAttribute('table:number-columns-repeated', (string) self::NUMBER_COLS_REPEATED_MAX);
170
                    $objWriter->endElement();
171
                    $span_row = 0;
172
                } else {
173 29
                    if ($sheet->rowDimensionExists($row->getRowIndex()) && $sheet->getRowDimension($row->getRowIndex())->getRowHeight() > 0) {
174
                        $objWriter->writeAttribute(
175
                            'table:style-name',
176
                            sprintf('%s_%d_%d', Style::ROW_STYLE_PREFIX, $sheetIndex, $row->getRowIndex())
177
                        );
178
                    }
179 29
                    $this->writeCells($objWriter, $cellIterator);
180
                }
181 29
                $objWriter->endElement();
182
            } else {
183
                ++$span_row;
184
            }
185
        }
186
    }
187
188
    /**
189
     * Write cells of the specified row.
190
     */
191 29
    private function writeCells(XMLWriter $objWriter, RowCellIterator $cells): void
192
    {
193 29
        $numberColsRepeated = self::NUMBER_COLS_REPEATED_MAX;
194 29
        $prevColumn = -1;
195 29
        foreach ($cells as $cell) {
196
            /** @var Cell $cell */
197 29
            $column = Coordinate::columnIndexFromString($cell->getColumn()) - 1;
198
199 29
            $this->writeCellSpan($objWriter, $column, $prevColumn);
200 29
            $objWriter->startElement('table:table-cell');
201 29
            $this->writeCellMerge($objWriter, $cell);
202
203
            // Style XF
204 29
            $style = $cell->getXfIndex();
205 29
            if ($style !== null) {
206 29
                $objWriter->writeAttribute('table:style-name', Style::CELL_STYLE_PREFIX . $style);
207
            }
208
209 29
            switch ($cell->getDataType()) {
210
                case DataType::TYPE_BOOL:
211 1
                    $objWriter->writeAttribute('office:value-type', 'boolean');
212 1
                    $objWriter->writeAttribute('office:value', $cell->getValue());
213 1
                    $objWriter->writeElement('text:p', $cell->getValue());
214
215 1
                    break;
216
                case DataType::TYPE_ERROR:
217 1
                    $objWriter->writeAttribute('table:formula', 'of:=#NULL!');
218 1
                    $objWriter->writeAttribute('office:value-type', 'string');
219 1
                    $objWriter->writeAttribute('office:string-value', '');
220 1
                    $objWriter->writeElement('text:p', '#NULL!');
221
222 1
                    break;
223
                case DataType::TYPE_FORMULA:
224 6
                    $formulaValue = $cell->getValue();
225 6
                    if ($this->getParentWriter()->getPreCalculateFormulas()) {
226
                        try {
227 5
                            $formulaValue = $cell->getCalculatedValue();
228
                        } catch (CalculationException) {
229
                            // don't do anything
230
                        }
231
                    }
232 6
                    $objWriter->writeAttribute('table:formula', $this->formulaConvertor->convertFormula($cell->getValue()));
233 6
                    if (is_numeric($formulaValue)) {
234 4
                        $objWriter->writeAttribute('office:value-type', 'float');
235
                    } else {
236 2
                        $objWriter->writeAttribute('office:value-type', 'string');
237
                    }
238 6
                    $objWriter->writeAttribute('office:value', $formulaValue);
239 6
                    $objWriter->writeElement('text:p', $formulaValue);
240
241 6
                    break;
242
                case DataType::TYPE_NUMERIC:
243 13
                    $objWriter->writeAttribute('office:value-type', 'float');
244 13
                    $objWriter->writeAttribute('office:value', $cell->getValue());
245 13
                    $objWriter->writeElement('text:p', $cell->getValue());
246
247 13
                    break;
248
                case DataType::TYPE_INLINE:
249
                    // break intentionally omitted
250
                case DataType::TYPE_STRING:
251 17
                    $objWriter->writeAttribute('office:value-type', 'string');
252 17
                    $url = $cell->getHyperlink()->getUrl();
253 17
                    if (empty($url)) {
254 17
                        $objWriter->writeElement('text:p', $cell->getValue());
255
                    } else {
256 1
                        $objWriter->startElement('text:p');
257 1
                        $objWriter->startElement('text:a');
258 1
                        $sheets = 'sheet://';
259 1
                        $lensheets = strlen($sheets);
260 1
                        if (substr($url, 0, $lensheets) === $sheets) {
261 1
                            $url = '#' . substr($url, $lensheets);
262
                        }
263 1
                        $objWriter->writeAttribute('xlink:href', $url);
264 1
                        $objWriter->writeAttribute('xlink:type', 'simple');
265 1
                        $objWriter->text($cell->getValue());
266 1
                        $objWriter->endElement(); // text:a
267 1
                        $objWriter->endElement(); // text:p
268
                    }
269
270 17
                    break;
271
            }
272 29
            Comment::write($objWriter, $cell);
273 29
            $objWriter->endElement();
274 29
            $prevColumn = $column;
275
        }
276
277 29
        $numberColsRepeated = $numberColsRepeated - $prevColumn - 1;
278 29
        if ($numberColsRepeated > 0) {
279 29
            if ($numberColsRepeated > 1) {
280 29
                $objWriter->startElement('table:table-cell');
281 29
                $objWriter->writeAttribute('table:number-columns-repeated', (string) $numberColsRepeated);
282 29
                $objWriter->endElement();
283
            } else {
284
                $objWriter->writeElement('table:table-cell');
285
            }
286
        }
287
    }
288
289
    /**
290
     * Write span.
291
     */
292 29
    private function writeCellSpan(XMLWriter $objWriter, int $curColumn, int $prevColumn): void
293
    {
294 29
        $diff = $curColumn - $prevColumn - 1;
295 29
        if (1 === $diff) {
296
            $objWriter->writeElement('table:table-cell');
297 29
        } elseif ($diff > 1) {
298
            $objWriter->startElement('table:table-cell');
299
            $objWriter->writeAttribute('table:number-columns-repeated', (string) $diff);
300
            $objWriter->endElement();
301
        }
302
    }
303
304
    /**
305
     * Write XF cell styles.
306
     */
307 29
    private function writeXfStyles(XMLWriter $writer, Spreadsheet $spreadsheet): void
308
    {
309 29
        $styleWriter = new Style($writer);
310
311 29
        $sheetCount = $spreadsheet->getSheetCount();
312 29
        for ($i = 0; $i < $sheetCount; ++$i) {
313 29
            $worksheet = $spreadsheet->getSheet($i);
314 29
            $styleWriter->writeTableStyle($worksheet, $i + 1);
315
316 29
            $worksheet->calculateColumnWidths();
317 29
            foreach ($worksheet->getColumnDimensions() as $columnDimension) {
318 5
                if ($columnDimension->getWidth() !== -1.0) {
319 5
                    $styleWriter->writeColumnStyles($columnDimension, $i);
320
                }
321
            }
322
        }
323 29
        for ($i = 0; $i < $sheetCount; ++$i) {
324 29
            $worksheet = $spreadsheet->getSheet($i);
325 29
            foreach ($worksheet->getRowDimensions() as $rowDimension) {
326 2
                if ($rowDimension->getRowHeight() > 0.0) {
327
                    $styleWriter->writeRowStyles($rowDimension, $i);
328
                }
329
            }
330
        }
331
332 29
        foreach ($spreadsheet->getCellXfCollection() as $style) {
333 29
            $styleWriter->write($style);
334
        }
335
    }
336
337
    /**
338
     * Write attributes for merged cell.
339
     */
340 29
    private function writeCellMerge(XMLWriter $objWriter, Cell $cell): void
341
    {
342 29
        if (!$cell->isMergeRangeValueCell()) {
343 29
            return;
344
        }
345
346 2
        $mergeRange = Coordinate::splitRange((string) $cell->getMergeRange());
347 2
        [$startCell, $endCell] = $mergeRange[0];
348 2
        $start = Coordinate::coordinateFromString($startCell);
349 2
        $end = Coordinate::coordinateFromString($endCell);
350 2
        $columnSpan = Coordinate::columnIndexFromString($end[0]) - Coordinate::columnIndexFromString($start[0]) + 1;
351 2
        $rowSpan = ((int) $end[1]) - ((int) $start[1]) + 1;
352
353 2
        $objWriter->writeAttribute('table:number-columns-spanned', (string) $columnSpan);
354 2
        $objWriter->writeAttribute('table:number-rows-spanned', (string) $rowSpan);
355
    }
356
}
357