Passed
Push — develop ( 22d531...b70061 )
by Adrien
34:06
created

Workbook::writeFileVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 1
dl 0
loc 8
ccs 7
cts 7
cp 1
crap 1
rs 9.4285
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 77
    public function writeWorkbook(Spreadsheet $spreadsheet, $recalcRequired = false)
26
    {
27
        // Create XML writer
28 77
        if ($this->getParentWriter()->getUseDiskCaching()) {
29
            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
30
        } else {
31 77
            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
32
        }
33
34
        // XML header
35 77
        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
36
37
        // workbook
38 77
        $objWriter->startElement('workbook');
39 77
        $objWriter->writeAttribute('xml:space', 'preserve');
40 77
        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
41 77
        $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
42
43
        // fileVersion
44 77
        $this->writeFileVersion($objWriter);
45
46
        // workbookPr
47 77
        $this->writeWorkbookPr($objWriter);
48
49
        // workbookProtection
50 77
        $this->writeWorkbookProtection($objWriter, $spreadsheet);
51
52
        // bookViews
53 77
        if ($this->getParentWriter()->getOffice2003Compatibility() === false) {
54 77
            $this->writeBookViews($objWriter, $spreadsheet);
55
        }
56
57
        // sheets
58 77
        $this->writeSheets($objWriter, $spreadsheet);
59
60
        // definedNames
61 77
        $this->writeDefinedNames($objWriter, $spreadsheet);
62
63
        // calcPr
64 77
        $this->writeCalcPr($objWriter, $recalcRequired);
65
66 77
        $objWriter->endElement();
67
68
        // Return
69 77
        return $objWriter->getData();
70
    }
71
72
    /**
73
     * Write file version.
74
     *
75
     * @param XMLWriter $objWriter XML Writer
76
     */
77 77
    private function writeFileVersion(XMLWriter $objWriter)
78
    {
79 77
        $objWriter->startElement('fileVersion');
80 77
        $objWriter->writeAttribute('appName', 'xl');
81 77
        $objWriter->writeAttribute('lastEdited', '4');
82 77
        $objWriter->writeAttribute('lowestEdited', '4');
83 77
        $objWriter->writeAttribute('rupBuild', '4505');
84 77
        $objWriter->endElement();
85 77
    }
86
87
    /**
88
     * Write WorkbookPr.
89
     *
90
     * @param XMLWriter $objWriter XML Writer
91
     */
92 77
    private function writeWorkbookPr(XMLWriter $objWriter)
93
    {
94 77
        $objWriter->startElement('workbookPr');
95
96 77
        if (Date::getExcelCalendar() == Date::CALENDAR_MAC_1904) {
97
            $objWriter->writeAttribute('date1904', '1');
98
        }
99
100 77
        $objWriter->writeAttribute('codeName', 'ThisWorkbook');
101
102 77
        $objWriter->endElement();
103 77
    }
104
105
    /**
106
     * Write BookViews.
107
     *
108
     * @param XMLWriter $objWriter XML Writer
109
     * @param Spreadsheet $spreadsheet
110
     */
111 77
    private function writeBookViews(XMLWriter $objWriter, Spreadsheet $spreadsheet)
112
    {
113
        // bookViews
114 77
        $objWriter->startElement('bookViews');
115
116
        // workbookView
117 77
        $objWriter->startElement('workbookView');
118
119 77
        $objWriter->writeAttribute('activeTab', $spreadsheet->getActiveSheetIndex());
120 77
        $objWriter->writeAttribute('autoFilterDateGrouping', '1');
121 77
        $objWriter->writeAttribute('firstSheet', '0');
122 77
        $objWriter->writeAttribute('minimized', '0');
123 77
        $objWriter->writeAttribute('showHorizontalScroll', '1');
124 77
        $objWriter->writeAttribute('showSheetTabs', '1');
125 77
        $objWriter->writeAttribute('showVerticalScroll', '1');
126 77
        $objWriter->writeAttribute('tabRatio', '600');
127 77
        $objWriter->writeAttribute('visibility', 'visible');
128
129 77
        $objWriter->endElement();
130
131 77
        $objWriter->endElement();
132 77
    }
133
134
    /**
135
     * Write WorkbookProtection.
136
     *
137
     * @param XMLWriter $objWriter XML Writer
138
     * @param Spreadsheet $spreadsheet
139
     */
140 77
    private function writeWorkbookProtection(XMLWriter $objWriter, Spreadsheet $spreadsheet)
141
    {
142 77
        if ($spreadsheet->getSecurity()->isSecurityEnabled()) {
143 3
            $objWriter->startElement('workbookProtection');
144 3
            $objWriter->writeAttribute('lockRevision', ($spreadsheet->getSecurity()->getLockRevision() ? 'true' : 'false'));
145 3
            $objWriter->writeAttribute('lockStructure', ($spreadsheet->getSecurity()->getLockStructure() ? 'true' : 'false'));
146 3
            $objWriter->writeAttribute('lockWindows', ($spreadsheet->getSecurity()->getLockWindows() ? 'true' : 'false'));
147
148 3
            if ($spreadsheet->getSecurity()->getRevisionsPassword() != '') {
149
                $objWriter->writeAttribute('revisionsPassword', $spreadsheet->getSecurity()->getRevisionsPassword());
150
            }
151
152 3
            if ($spreadsheet->getSecurity()->getWorkbookPassword() != '') {
153 3
                $objWriter->writeAttribute('workbookPassword', $spreadsheet->getSecurity()->getWorkbookPassword());
154
            }
155
156 3
            $objWriter->endElement();
157
        }
158 77
    }
159
160
    /**
161
     * Write calcPr.
162
     *
163
     * @param XMLWriter $objWriter XML Writer
164
     * @param bool $recalcRequired Indicate whether formulas should be recalculated before writing
165
     */
166 77
    private function writeCalcPr(XMLWriter $objWriter, $recalcRequired = true)
167
    {
168 77
        $objWriter->startElement('calcPr');
169
170
        //    Set the calcid to a higher value than Excel itself will use, otherwise Excel will always recalc
171
        //  If MS Excel does do a recalc, then users opening a file in MS Excel will be prompted to save on exit
172
        //     because the file has changed
173 77
        $objWriter->writeAttribute('calcId', '999999');
174 77
        $objWriter->writeAttribute('calcMode', 'auto');
175
        //    fullCalcOnLoad isn't needed if we've recalculating for the save
176 77
        $objWriter->writeAttribute('calcCompleted', ($recalcRequired) ? 1 : 0);
177 77
        $objWriter->writeAttribute('fullCalcOnLoad', ($recalcRequired) ? 0 : 1);
178 77
        $objWriter->writeAttribute('forceFullCalc', ($recalcRequired) ? 0 : 1);
179
180 77
        $objWriter->endElement();
181 77
    }
182
183
    /**
184
     * Write sheets.
185
     *
186
     * @param XMLWriter $objWriter XML Writer
187
     * @param Spreadsheet $spreadsheet
188
     *
189
     * @throws WriterException
190
     */
191 77
    private function writeSheets(XMLWriter $objWriter, Spreadsheet $spreadsheet)
192
    {
193
        // Write sheets
194 77
        $objWriter->startElement('sheets');
195 77
        $sheetCount = $spreadsheet->getSheetCount();
196 77
        for ($i = 0; $i < $sheetCount; ++$i) {
197
            // sheet
198 77
            $this->writeSheet(
199 77
                $objWriter,
200 77
                $spreadsheet->getSheet($i)->getTitle(),
201 77
                ($i + 1),
202 77
                ($i + 1 + 3),
203 77
                $spreadsheet->getSheet($i)->getSheetState()
204
            );
205
        }
206
207 77
        $objWriter->endElement();
208 77
    }
209
210
    /**
211
     * Write sheet.
212
     *
213
     * @param XMLWriter $objWriter XML Writer
214
     * @param string $pSheetname Sheet name
215
     * @param int $pSheetId Sheet id
216
     * @param int $pRelId Relationship ID
217
     * @param string $sheetState Sheet state (visible, hidden, veryHidden)
218
     *
219
     * @throws WriterException
220
     */
221 77
    private function writeSheet(XMLWriter $objWriter, $pSheetname, $pSheetId = 1, $pRelId = 1, $sheetState = 'visible')
222
    {
223 77
        if ($pSheetname != '') {
224
            // Write sheet
225 77
            $objWriter->startElement('sheet');
226 77
            $objWriter->writeAttribute('name', $pSheetname);
227 77
            $objWriter->writeAttribute('sheetId', $pSheetId);
228 77
            if ($sheetState != 'visible' && $sheetState != '') {
229 1
                $objWriter->writeAttribute('state', $sheetState);
230
            }
231 77
            $objWriter->writeAttribute('r:id', 'rId' . $pRelId);
232 77
            $objWriter->endElement();
233
        } else {
234
            throw new WriterException('Invalid parameters passed.');
235
        }
236 77
    }
237
238
    /**
239
     * Write Defined Names.
240
     *
241
     * @param XMLWriter $objWriter XML Writer
242
     * @param Spreadsheet $spreadsheet
243
     *
244
     * @throws WriterException
245
     */
246 77
    private function writeDefinedNames(XMLWriter $objWriter, Spreadsheet $spreadsheet)
247
    {
248
        // Write defined names
249 77
        $objWriter->startElement('definedNames');
250
251
        // Named ranges
252 77
        if (count($spreadsheet->getNamedRanges()) > 0) {
253
            // Named ranges
254 4
            $this->writeNamedRanges($objWriter, $spreadsheet);
255
        }
256
257
        // Other defined names
258 77
        $sheetCount = $spreadsheet->getSheetCount();
259 77
        for ($i = 0; $i < $sheetCount; ++$i) {
260
            // definedName for autoFilter
261 77
            $this->writeDefinedNameForAutofilter($objWriter, $spreadsheet->getSheet($i), $i);
262
263
            // definedName for Print_Titles
264 77
            $this->writeDefinedNameForPrintTitles($objWriter, $spreadsheet->getSheet($i), $i);
265
266
            // definedName for Print_Area
267 77
            $this->writeDefinedNameForPrintArea($objWriter, $spreadsheet->getSheet($i), $i);
268
        }
269
270 77
        $objWriter->endElement();
271 77
    }
272
273
    /**
274
     * Write named ranges.
275
     *
276
     * @param XMLWriter $objWriter XML Writer
277
     * @param Spreadsheet $spreadsheet
278
     *
279
     * @throws WriterException
280
     */
281 4
    private function writeNamedRanges(XMLWriter $objWriter, Spreadsheet $spreadsheet)
282
    {
283
        // Loop named ranges
284 4
        $namedRanges = $spreadsheet->getNamedRanges();
285 4
        foreach ($namedRanges as $namedRange) {
286 4
            $this->writeDefinedNameForNamedRange($objWriter, $namedRange);
287
        }
288 4
    }
289
290
    /**
291
     * Write Defined Name for named range.
292
     *
293
     * @param XMLWriter $objWriter XML Writer
294
     * @param NamedRange $pNamedRange
295
     */
296 4
    private function writeDefinedNameForNamedRange(XMLWriter $objWriter, NamedRange $pNamedRange)
297
    {
298
        // definedName for named range
299 4
        $objWriter->startElement('definedName');
300 4
        $objWriter->writeAttribute('name', $pNamedRange->getName());
301 4
        if ($pNamedRange->getLocalOnly()) {
302
            $objWriter->writeAttribute('localSheetId', $pNamedRange->getScope()->getParent()->getIndex($pNamedRange->getScope()));
303
        }
304
305
        // Create absolute coordinate and write as raw text
306 4
        $range = Coordinate::splitRange($pNamedRange->getRange());
307 4
        $iMax = count($range);
308 4
        for ($i = 0; $i < $iMax; ++$i) {
309 4
            $range[$i][0] = '\'' . str_replace("'", "''", $pNamedRange->getWorksheet()->getTitle()) . '\'!' . Coordinate::absoluteReference($range[$i][0]);
310 4
            if (isset($range[$i][1])) {
311 3
                $range[$i][1] = Coordinate::absoluteReference($range[$i][1]);
312
            }
313
        }
314 4
        $range = Coordinate::buildRange($range);
315
316 4
        $objWriter->writeRawData($range);
317
318 4
        $objWriter->endElement();
319 4
    }
320
321
    /**
322
     * Write Defined Name for autoFilter.
323
     *
324
     * @param XMLWriter $objWriter XML Writer
325
     * @param Worksheet $pSheet
326
     * @param int $pSheetId
327
     */
328 77
    private function writeDefinedNameForAutofilter(XMLWriter $objWriter, Worksheet $pSheet, $pSheetId = 0)
329
    {
330
        // definedName for autoFilter
331 77
        $autoFilterRange = $pSheet->getAutoFilter()->getRange();
332 77
        if (!empty($autoFilterRange)) {
333 3
            $objWriter->startElement('definedName');
334 3
            $objWriter->writeAttribute('name', '_xlnm._FilterDatabase');
335 3
            $objWriter->writeAttribute('localSheetId', $pSheetId);
336 3
            $objWriter->writeAttribute('hidden', '1');
337
338
            // Create absolute coordinate and write as raw text
339 3
            $range = Coordinate::splitRange($autoFilterRange);
340 3
            $range = $range[0];
341
            //    Strip any worksheet ref so we can make the cell ref absolute
342 3
            if (strpos($range[0], '!') !== false) {
343
                list($ws, $range[0]) = explode('!', $range[0]);
344
            }
345
346 3
            $range[0] = Coordinate::absoluteCoordinate($range[0]);
347 3
            $range[1] = Coordinate::absoluteCoordinate($range[1]);
348 3
            $range = implode(':', $range);
349
350 3
            $objWriter->writeRawData('\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . $range);
351
352 3
            $objWriter->endElement();
353
        }
354 77
    }
355
356
    /**
357
     * Write Defined Name for PrintTitles.
358
     *
359
     * @param XMLWriter $objWriter XML Writer
360
     * @param Worksheet $pSheet
361
     * @param int $pSheetId
362
     */
363 77
    private function writeDefinedNameForPrintTitles(XMLWriter $objWriter, Worksheet $pSheet, $pSheetId = 0)
364
    {
365
        // definedName for PrintTitles
366 77
        if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet() || $pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
367 1
            $objWriter->startElement('definedName');
368 1
            $objWriter->writeAttribute('name', '_xlnm.Print_Titles');
369 1
            $objWriter->writeAttribute('localSheetId', $pSheetId);
370
371
            // Setting string
372 1
            $settingString = '';
373
374
            // Columns to repeat
375 1
            if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
376
                $repeat = $pSheet->getPageSetup()->getColumnsToRepeatAtLeft();
377
378
                $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];
379
            }
380
381
            // Rows to repeat
382 1
            if ($pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
383 1
                if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
384
                    $settingString .= ',';
385
                }
386
387 1
                $repeat = $pSheet->getPageSetup()->getRowsToRepeatAtTop();
388
389 1
                $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];
390
            }
391
392 1
            $objWriter->writeRawData($settingString);
393
394 1
            $objWriter->endElement();
395
        }
396 77
    }
397
398
    /**
399
     * Write Defined Name for PrintTitles.
400
     *
401
     * @param XMLWriter $objWriter XML Writer
402
     * @param Worksheet $pSheet
403
     * @param int $pSheetId
404
     */
405 77
    private function writeDefinedNameForPrintArea(XMLWriter $objWriter, Worksheet $pSheet, $pSheetId = 0)
406
    {
407
        // definedName for PrintArea
408 77
        if ($pSheet->getPageSetup()->isPrintAreaSet()) {
409 1
            $objWriter->startElement('definedName');
410 1
            $objWriter->writeAttribute('name', '_xlnm.Print_Area');
411 1
            $objWriter->writeAttribute('localSheetId', $pSheetId);
412
413
            // Print area
414 1
            $printArea = Coordinate::splitRange($pSheet->getPageSetup()->getPrintArea());
415
416 1
            $chunks = [];
417 1
            foreach ($printArea as $printAreaRect) {
418 1
                $printAreaRect[0] = Coordinate::absoluteReference($printAreaRect[0]);
419 1
                $printAreaRect[1] = Coordinate::absoluteReference($printAreaRect[1]);
420 1
                $chunks[] = '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . implode(':', $printAreaRect);
421
            }
422
423 1
            $objWriter->writeRawData(implode(',', $chunks));
424
425 1
            $objWriter->endElement();
426
        }
427 77
    }
428
}
429