Completed
Push — develop ( d383bc...d9bd45 )
by Adrien
23:59
created

Workbook::writeBookViews()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 13
nc 1
nop 2
dl 0
loc 21
ccs 14
cts 14
cp 1
crap 1
rs 9.3142
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
4
5
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
6
use PhpOffice\PhpSpreadsheet\NamedRange;
7
use PhpOffice\PhpSpreadsheet\Shared\Date;
8
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
9
use PhpOffice\PhpSpreadsheet\Spreadsheet;
10
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
11
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
12
13
class Workbook extends WriterPart
14
{
15
    /**
16
     * Write workbook to XML format.
17
     *
18
     * @param Spreadsheet $spreadsheet
19
     * @param bool $recalcRequired Indicate whether formulas should be recalculated before writing
20
     *
21
     * @throws WriterException
22
     *
23
     * @return string XML Output
24
     */
25 56
    public function writeWorkbook(Spreadsheet $spreadsheet, $recalcRequired = false)
26
    {
27
        // Create XML writer
28 56 View Code Duplication
        if ($this->getParentWriter()->getUseDiskCaching()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
29
            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
30
        } else {
31 56
            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
32
        }
33
34
        // XML header
35 56
        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
36
37
        // workbook
38 56
        $objWriter->startElement('workbook');
39 56
        $objWriter->writeAttribute('xml:space', 'preserve');
40 56
        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
41 56
        $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
42
43
        // fileVersion
44 56
        $this->writeFileVersion($objWriter);
45
46
        // workbookPr
47 56
        $this->writeWorkbookPr($objWriter);
48
49
        // workbookProtection
50 56
        $this->writeWorkbookProtection($objWriter, $spreadsheet);
51
52
        // bookViews
53 56
        if ($this->getParentWriter()->getOffice2003Compatibility() === false) {
54 56
            $this->writeBookViews($objWriter, $spreadsheet);
55
        }
56
57
        // sheets
58 56
        $this->writeSheets($objWriter, $spreadsheet);
59
60
        // definedNames
61 56
        $this->writeDefinedNames($objWriter, $spreadsheet);
62
63
        // calcPr
64 56
        $this->writeCalcPr($objWriter, $recalcRequired);
65
66 56
        $objWriter->endElement();
67
68
        // Return
69 56
        return $objWriter->getData();
70
    }
71
72
    /**
73
     * Write file version.
74
     *
75
     * @param XMLWriter $objWriter XML Writer
76
     *
77
     * @throws WriterException
78
     */
79 56
    private function writeFileVersion(XMLWriter $objWriter)
80
    {
81 56
        $objWriter->startElement('fileVersion');
82 56
        $objWriter->writeAttribute('appName', 'xl');
83 56
        $objWriter->writeAttribute('lastEdited', '4');
84 56
        $objWriter->writeAttribute('lowestEdited', '4');
85 56
        $objWriter->writeAttribute('rupBuild', '4505');
86 56
        $objWriter->endElement();
87 56
    }
88
89
    /**
90
     * Write WorkbookPr.
91
     *
92
     * @param XMLWriter $objWriter XML Writer
93
     *
94
     * @throws WriterException
95
     */
96 56
    private function writeWorkbookPr(XMLWriter $objWriter)
97
    {
98 56
        $objWriter->startElement('workbookPr');
99
100 56
        if (Date::getExcelCalendar() == Date::CALENDAR_MAC_1904) {
101
            $objWriter->writeAttribute('date1904', '1');
102
        }
103
104 56
        $objWriter->writeAttribute('codeName', 'ThisWorkbook');
105
106 56
        $objWriter->endElement();
107 56
    }
108
109
    /**
110
     * Write BookViews.
111
     *
112
     * @param XMLWriter $objWriter XML Writer
113
     * @param Spreadsheet $spreadsheet
114
     *
115
     * @throws WriterException
116
     */
117 56
    private function writeBookViews(XMLWriter $objWriter, Spreadsheet $spreadsheet)
118
    {
119
        // bookViews
120 56
        $objWriter->startElement('bookViews');
121
122
        // workbookView
123 56
        $objWriter->startElement('workbookView');
124
125 56
        $objWriter->writeAttribute('activeTab', $spreadsheet->getActiveSheetIndex());
126 56
        $objWriter->writeAttribute('autoFilterDateGrouping', '1');
127 56
        $objWriter->writeAttribute('firstSheet', '0');
128 56
        $objWriter->writeAttribute('minimized', '0');
129 56
        $objWriter->writeAttribute('showHorizontalScroll', '1');
130 56
        $objWriter->writeAttribute('showSheetTabs', '1');
131 56
        $objWriter->writeAttribute('showVerticalScroll', '1');
132 56
        $objWriter->writeAttribute('tabRatio', '600');
133 56
        $objWriter->writeAttribute('visibility', 'visible');
134
135 56
        $objWriter->endElement();
136
137 56
        $objWriter->endElement();
138 56
    }
139
140
    /**
141
     * Write WorkbookProtection.
142
     *
143
     * @param XMLWriter $objWriter XML Writer
144
     * @param Spreadsheet $spreadsheet
145
     *
146
     * @throws WriterException
147
     */
148 56
    private function writeWorkbookProtection(XMLWriter $objWriter, Spreadsheet $spreadsheet)
149
    {
150 56
        if ($spreadsheet->getSecurity()->isSecurityEnabled()) {
151 2
            $objWriter->startElement('workbookProtection');
152 2
            $objWriter->writeAttribute('lockRevision', ($spreadsheet->getSecurity()->getLockRevision() ? 'true' : 'false'));
153 2
            $objWriter->writeAttribute('lockStructure', ($spreadsheet->getSecurity()->getLockStructure() ? 'true' : 'false'));
154 2
            $objWriter->writeAttribute('lockWindows', ($spreadsheet->getSecurity()->getLockWindows() ? 'true' : 'false'));
155
156 2
            if ($spreadsheet->getSecurity()->getRevisionsPassword() != '') {
157
                $objWriter->writeAttribute('revisionsPassword', $spreadsheet->getSecurity()->getRevisionsPassword());
158
            }
159
160 2
            if ($spreadsheet->getSecurity()->getWorkbookPassword() != '') {
161 2
                $objWriter->writeAttribute('workbookPassword', $spreadsheet->getSecurity()->getWorkbookPassword());
162
            }
163
164 2
            $objWriter->endElement();
165
        }
166 56
    }
167
168
    /**
169
     * Write calcPr.
170
     *
171
     * @param XMLWriter $objWriter XML Writer
172
     * @param bool $recalcRequired Indicate whether formulas should be recalculated before writing
173
     *
174
     * @throws WriterException
175
     */
176 56
    private function writeCalcPr(XMLWriter $objWriter, $recalcRequired = true)
177
    {
178 56
        $objWriter->startElement('calcPr');
179
180
        //    Set the calcid to a higher value than Excel itself will use, otherwise Excel will always recalc
181
        //  If MS Excel does do a recalc, then users opening a file in MS Excel will be prompted to save on exit
182
        //     because the file has changed
183 56
        $objWriter->writeAttribute('calcId', '999999');
184 56
        $objWriter->writeAttribute('calcMode', 'auto');
185
        //    fullCalcOnLoad isn't needed if we've recalculating for the save
186 56
        $objWriter->writeAttribute('calcCompleted', ($recalcRequired) ? 1 : 0);
187 56
        $objWriter->writeAttribute('fullCalcOnLoad', ($recalcRequired) ? 0 : 1);
188
189 56
        $objWriter->endElement();
190 56
    }
191
192
    /**
193
     * Write sheets.
194
     *
195
     * @param XMLWriter $objWriter XML Writer
196
     * @param Spreadsheet $spreadsheet
197
     *
198
     * @throws WriterException
199
     */
200 56
    private function writeSheets(XMLWriter $objWriter, Spreadsheet $spreadsheet)
201
    {
202
        // Write sheets
203 56
        $objWriter->startElement('sheets');
204 56
        $sheetCount = $spreadsheet->getSheetCount();
205 56
        for ($i = 0; $i < $sheetCount; ++$i) {
206
            // sheet
207 56
            $this->writeSheet(
208 56
                $objWriter,
209 56
                $spreadsheet->getSheet($i)->getTitle(),
210 56
                ($i + 1),
211 56
                ($i + 1 + 3),
212 56
                $spreadsheet->getSheet($i)->getSheetState()
213
            );
214
        }
215
216 56
        $objWriter->endElement();
217 56
    }
218
219
    /**
220
     * Write sheet.
221
     *
222
     * @param XMLWriter $objWriter XML Writer
223
     * @param string $pSheetname Sheet name
224
     * @param int $pSheetId Sheet id
225
     * @param int $pRelId Relationship ID
226
     * @param string $sheetState Sheet state (visible, hidden, veryHidden)
227
     *
228
     * @throws WriterException
229
     */
230 56 View Code Duplication
    private function writeSheet(XMLWriter $objWriter, $pSheetname, $pSheetId = 1, $pRelId = 1, $sheetState = 'visible')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
231
    {
232 56
        if ($pSheetname != '') {
233
            // Write sheet
234 56
            $objWriter->startElement('sheet');
235 56
            $objWriter->writeAttribute('name', $pSheetname);
236 56
            $objWriter->writeAttribute('sheetId', $pSheetId);
237 56
            if ($sheetState != 'visible' && $sheetState != '') {
238
                $objWriter->writeAttribute('state', $sheetState);
239
            }
240 56
            $objWriter->writeAttribute('r:id', 'rId' . $pRelId);
241 56
            $objWriter->endElement();
242
        } else {
243
            throw new WriterException('Invalid parameters passed.');
244
        }
245 56
    }
246
247
    /**
248
     * Write Defined Names.
249
     *
250
     * @param XMLWriter $objWriter XML Writer
251
     * @param Spreadsheet $spreadsheet
252
     *
253
     * @throws WriterException
254
     */
255 56
    private function writeDefinedNames(XMLWriter $objWriter, Spreadsheet $spreadsheet)
256
    {
257
        // Write defined names
258 56
        $objWriter->startElement('definedNames');
259
260
        // Named ranges
261 56
        if (count($spreadsheet->getNamedRanges()) > 0) {
262
            // Named ranges
263 4
            $this->writeNamedRanges($objWriter, $spreadsheet);
264
        }
265
266
        // Other defined names
267 56
        $sheetCount = $spreadsheet->getSheetCount();
268 56
        for ($i = 0; $i < $sheetCount; ++$i) {
269
            // definedName for autoFilter
270 56
            $this->writeDefinedNameForAutofilter($objWriter, $spreadsheet->getSheet($i), $i);
271
272
            // definedName for Print_Titles
273 56
            $this->writeDefinedNameForPrintTitles($objWriter, $spreadsheet->getSheet($i), $i);
274
275
            // definedName for Print_Area
276 56
            $this->writeDefinedNameForPrintArea($objWriter, $spreadsheet->getSheet($i), $i);
277
        }
278
279 56
        $objWriter->endElement();
280 56
    }
281
282
    /**
283
     * Write named ranges.
284
     *
285
     * @param XMLWriter $objWriter XML Writer
286
     * @param Spreadsheet $spreadsheet
287
     *
288
     * @throws WriterException
289
     */
290 4
    private function writeNamedRanges(XMLWriter $objWriter, Spreadsheet $spreadsheet)
291
    {
292
        // Loop named ranges
293 4
        $namedRanges = $spreadsheet->getNamedRanges();
294 4
        foreach ($namedRanges as $namedRange) {
295 4
            $this->writeDefinedNameForNamedRange($objWriter, $namedRange);
296
        }
297 4
    }
298
299
    /**
300
     * Write Defined Name for named range.
301
     *
302
     * @param XMLWriter $objWriter XML Writer
303
     * @param NamedRange $pNamedRange
304
     *
305
     * @throws WriterException
306
     */
307 4
    private function writeDefinedNameForNamedRange(XMLWriter $objWriter, NamedRange $pNamedRange)
308
    {
309
        // definedName for named range
310 4
        $objWriter->startElement('definedName');
311 4
        $objWriter->writeAttribute('name', $pNamedRange->getName());
312 4
        if ($pNamedRange->getLocalOnly()) {
313
            $objWriter->writeAttribute('localSheetId', $pNamedRange->getScope()->getParent()->getIndex($pNamedRange->getScope()));
314
        }
315
316
        // Create absolute coordinate and write as raw text
317 4
        $range = Coordinate::splitRange($pNamedRange->getRange());
318 4
        $iMax = count($range);
319 4 View Code Duplication
        for ($i = 0; $i < $iMax; ++$i) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
320 4
            $range[$i][0] = '\'' . str_replace("'", "''", $pNamedRange->getWorksheet()->getTitle()) . '\'!' . Coordinate::absoluteReference($range[$i][0]);
321 4
            if (isset($range[$i][1])) {
322 3
                $range[$i][1] = Coordinate::absoluteReference($range[$i][1]);
323
            }
324
        }
325 4
        $range = Coordinate::buildRange($range);
326
327 4
        $objWriter->writeRawData($range);
328
329 4
        $objWriter->endElement();
330 4
    }
331
332
    /**
333
     * Write Defined Name for autoFilter.
334
     *
335
     * @param XMLWriter $objWriter XML Writer
336
     * @param Worksheet $pSheet
337
     * @param int $pSheetId
338
     *
339
     * @throws WriterException
340
     */
341 56
    private function writeDefinedNameForAutofilter(XMLWriter $objWriter, Worksheet $pSheet, $pSheetId = 0)
342
    {
343
        // definedName for autoFilter
344 56
        $autoFilterRange = $pSheet->getAutoFilter()->getRange();
345 56
        if (!empty($autoFilterRange)) {
346 3
            $objWriter->startElement('definedName');
347 3
            $objWriter->writeAttribute('name', '_xlnm._FilterDatabase');
348 3
            $objWriter->writeAttribute('localSheetId', $pSheetId);
349 3
            $objWriter->writeAttribute('hidden', '1');
350
351
            // Create absolute coordinate and write as raw text
352 3
            $range = Coordinate::splitRange($autoFilterRange);
353 3
            $range = $range[0];
354
            //    Strip any worksheet ref so we can make the cell ref absolute
355 3 View Code Duplication
            if (strpos($range[0], '!') !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
356
                list($ws, $range[0]) = explode('!', $range[0]);
357
            }
358
359 3
            $range[0] = Coordinate::absoluteCoordinate($range[0]);
360 3
            $range[1] = Coordinate::absoluteCoordinate($range[1]);
361 3
            $range = implode(':', $range);
362
363 3
            $objWriter->writeRawData('\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . $range);
364
365 3
            $objWriter->endElement();
366
        }
367 56
    }
368
369
    /**
370
     * Write Defined Name for PrintTitles.
371
     *
372
     * @param XMLWriter $objWriter XML Writer
373
     * @param Worksheet $pSheet
374
     * @param int $pSheetId
375
     *
376
     * @throws WriterException
377
     */
378 56
    private function writeDefinedNameForPrintTitles(XMLWriter $objWriter, Worksheet $pSheet, $pSheetId = 0)
379
    {
380
        // definedName for PrintTitles
381 56
        if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet() || $pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
382 1
            $objWriter->startElement('definedName');
383 1
            $objWriter->writeAttribute('name', '_xlnm.Print_Titles');
384 1
            $objWriter->writeAttribute('localSheetId', $pSheetId);
385
386
            // Setting string
387 1
            $settingString = '';
388
389
            // Columns to repeat
390 1
            if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
391
                $repeat = $pSheet->getPageSetup()->getColumnsToRepeatAtLeft();
392
393
                $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];
394
            }
395
396
            // Rows to repeat
397 1
            if ($pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
398 1
                if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
399
                    $settingString .= ',';
400
                }
401
402 1
                $repeat = $pSheet->getPageSetup()->getRowsToRepeatAtTop();
403
404 1
                $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];
405
            }
406
407 1
            $objWriter->writeRawData($settingString);
408
409 1
            $objWriter->endElement();
410
        }
411 56
    }
412
413
    /**
414
     * Write Defined Name for PrintTitles.
415
     *
416
     * @param XMLWriter $objWriter XML Writer
417
     * @param Worksheet $pSheet
418
     * @param int $pSheetId
419
     *
420
     * @throws WriterException
421
     */
422 56
    private function writeDefinedNameForPrintArea(XMLWriter $objWriter, Worksheet $pSheet, $pSheetId = 0)
423
    {
424
        // definedName for PrintArea
425 56
        if ($pSheet->getPageSetup()->isPrintAreaSet()) {
426
            $objWriter->startElement('definedName');
427
            $objWriter->writeAttribute('name', '_xlnm.Print_Area');
428
            $objWriter->writeAttribute('localSheetId', $pSheetId);
429
430
            // Print area
431
            $printArea = Coordinate::splitRange($pSheet->getPageSetup()->getPrintArea());
432
433
            $chunks = [];
434
            foreach ($printArea as $printAreaRect) {
435
                $printAreaRect[0] = Coordinate::absoluteReference($printAreaRect[0]);
436
                $printAreaRect[1] = Coordinate::absoluteReference($printAreaRect[1]);
437
                $chunks[] = '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . implode(':', $printAreaRect);
438
            }
439
440
            $objWriter->writeRawData(implode(',', $chunks));
441
442
            $objWriter->endElement();
443
        }
444 56
    }
445
}
446