Passed
Push — master ( fde2cc...f1d90a )
by Mark
17:04 queued 08:09
created

Content   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 323
Duplicated Lines 0 %

Test Coverage

Coverage 90.16%

Importance

Changes 0
Metric Value
wmc 40
eloc 199
dl 0
loc 323
ccs 174
cts 193
cp 0.9016
rs 9.2
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B write() 0 71 2
B writeXfStyles() 0 27 8
C writeCells() 0 78 14
A writeCellMerge() 0 15 2
A writeCellSpan() 0 9 3
B writeRows() 0 30 6
A writeSheets() 0 30 4

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