Total Complexity | 89 |
Total Lines | 1107 |
Duplicated Lines | 4.43 % |
Coverage | 84.08% |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Workbook 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 Workbook, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
46 | class Workbook extends BIFFwriter |
||
47 | { |
||
48 | /** |
||
49 | * Formula parser. |
||
50 | * |
||
51 | * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Parser |
||
52 | */ |
||
53 | private $parser; |
||
54 | |||
55 | /** |
||
56 | * The BIFF file size for the workbook. |
||
57 | * |
||
58 | * @var int |
||
59 | * |
||
60 | * @see calcSheetOffsets() |
||
61 | */ |
||
62 | private $biffSize; |
||
63 | |||
64 | /** |
||
65 | * XF Writers. |
||
66 | * |
||
67 | * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Xf[] |
||
68 | */ |
||
69 | private $xfWriters = []; |
||
70 | |||
71 | /** |
||
72 | * Array containing the colour palette. |
||
73 | * |
||
74 | * @var array |
||
75 | */ |
||
76 | private $palette; |
||
77 | |||
78 | /** |
||
79 | * The codepage indicates the text encoding used for strings. |
||
80 | * |
||
81 | * @var int |
||
82 | */ |
||
83 | private $codepage; |
||
84 | |||
85 | /** |
||
86 | * The country code used for localization. |
||
87 | * |
||
88 | * @var int |
||
89 | */ |
||
90 | private $countryCode; |
||
91 | |||
92 | /** |
||
93 | * Workbook. |
||
94 | * |
||
95 | * @var Spreadsheet |
||
96 | */ |
||
97 | private $spreadsheet; |
||
98 | |||
99 | /** |
||
100 | * Fonts writers. |
||
101 | * |
||
102 | * @var Font[] |
||
103 | */ |
||
104 | private $fontWriters = []; |
||
105 | |||
106 | /** |
||
107 | * Added fonts. Maps from font's hash => index in workbook. |
||
108 | * |
||
109 | * @var array |
||
110 | */ |
||
111 | private $addedFonts = []; |
||
112 | |||
113 | /** |
||
114 | * Shared number formats. |
||
115 | * |
||
116 | * @var array |
||
117 | */ |
||
118 | private $numberFormats = []; |
||
119 | |||
120 | /** |
||
121 | * Added number formats. Maps from numberFormat's hash => index in workbook. |
||
122 | * |
||
123 | * @var array |
||
124 | */ |
||
125 | private $addedNumberFormats = []; |
||
126 | |||
127 | /** |
||
128 | * Sizes of the binary worksheet streams. |
||
129 | * |
||
130 | * @var array |
||
131 | */ |
||
132 | private $worksheetSizes = []; |
||
133 | |||
134 | /** |
||
135 | * Offsets of the binary worksheet streams relative to the start of the global workbook stream. |
||
136 | * |
||
137 | * @var array |
||
138 | */ |
||
139 | private $worksheetOffsets = []; |
||
140 | |||
141 | /** |
||
142 | * Total number of shared strings in workbook. |
||
143 | * |
||
144 | * @var int |
||
145 | */ |
||
146 | private $stringTotal; |
||
147 | |||
148 | /** |
||
149 | * Number of unique shared strings in workbook. |
||
150 | * |
||
151 | * @var int |
||
152 | */ |
||
153 | private $stringUnique; |
||
154 | |||
155 | /** |
||
156 | * Array of unique shared strings in workbook. |
||
157 | * |
||
158 | * @var array |
||
159 | */ |
||
160 | private $stringTable; |
||
161 | |||
162 | /** |
||
163 | * Color cache. |
||
164 | */ |
||
165 | private $colors; |
||
166 | |||
167 | /** |
||
168 | * Escher object corresponding to MSODRAWINGGROUP. |
||
169 | * |
||
170 | * @var \PhpOffice\PhpSpreadsheet\Shared\Escher |
||
171 | */ |
||
172 | private $escher; |
||
173 | |||
174 | /** |
||
175 | * Class constructor. |
||
176 | * |
||
177 | * @param Spreadsheet $spreadsheet The Workbook |
||
178 | * @param int $str_total Total number of strings |
||
179 | * @param int $str_unique Total number of unique strings |
||
180 | * @param array $str_table String Table |
||
181 | * @param array $colors Colour Table |
||
182 | * @param Parser $parser The formula parser created for the Workbook |
||
183 | */ |
||
184 | 48 | public function __construct(Spreadsheet $spreadsheet, &$str_total, &$str_unique, &$str_table, &$colors, Parser $parser) |
|
185 | { |
||
186 | // It needs to call its parent's constructor explicitly |
||
187 | 48 | parent::__construct(); |
|
188 | |||
189 | 48 | $this->parser = $parser; |
|
190 | 48 | $this->biffSize = 0; |
|
191 | 48 | $this->palette = []; |
|
192 | 48 | $this->countryCode = -1; |
|
193 | |||
194 | 48 | $this->stringTotal = &$str_total; |
|
195 | 48 | $this->stringUnique = &$str_unique; |
|
196 | 48 | $this->stringTable = &$str_table; |
|
197 | 48 | $this->colors = &$colors; |
|
198 | 48 | $this->setPaletteXl97(); |
|
199 | |||
200 | 48 | $this->spreadsheet = $spreadsheet; |
|
201 | |||
202 | 48 | $this->codepage = 0x04B0; |
|
203 | |||
204 | // Add empty sheets and Build color cache |
||
205 | 48 | $countSheets = $spreadsheet->getSheetCount(); |
|
206 | 48 | for ($i = 0; $i < $countSheets; ++$i) { |
|
207 | 48 | $phpSheet = $spreadsheet->getSheet($i); |
|
208 | |||
209 | 48 | $this->parser->setExtSheet($phpSheet->getTitle(), $i); // Register worksheet name with parser |
|
210 | |||
211 | 48 | $supbook_index = 0x00; |
|
212 | 48 | $ref = pack('vvv', $supbook_index, $i, $i); |
|
213 | 48 | $this->parser->references[] = $ref; // Register reference with parser |
|
214 | |||
215 | // Sheet tab colors? |
||
216 | 48 | if ($phpSheet->isTabColorSet()) { |
|
217 | 5 | $this->addColor($phpSheet->getTabColor()->getRGB()); |
|
218 | } |
||
219 | } |
||
220 | 48 | } |
|
221 | |||
222 | /** |
||
223 | * Add a new XF writer. |
||
224 | * |
||
225 | * @param Style |
||
226 | * @param bool Is it a style XF? |
||
|
|||
227 | * @param Style $style |
||
228 | * @param bool $isStyleXf |
||
229 | * |
||
230 | * @return int Index to XF record |
||
231 | */ |
||
232 | 39 | public function addXfWriter(Style $style, $isStyleXf = false) |
|
233 | { |
||
234 | 39 | $xfWriter = new Xf($style); |
|
235 | 39 | $xfWriter->setIsStyleXf($isStyleXf); |
|
236 | |||
237 | // Add the font if not already added |
||
238 | 39 | $fontIndex = $this->addFont($style->getFont()); |
|
239 | |||
240 | // Assign the font index to the xf record |
||
241 | 39 | $xfWriter->setFontIndex($fontIndex); |
|
242 | |||
243 | // Background colors, best to treat these after the font so black will come after white in custom palette |
||
244 | 39 | $xfWriter->setFgColor($this->addColor($style->getFill()->getStartColor()->getRGB())); |
|
245 | 39 | $xfWriter->setBgColor($this->addColor($style->getFill()->getEndColor()->getRGB())); |
|
246 | 39 | $xfWriter->setBottomColor($this->addColor($style->getBorders()->getBottom()->getColor()->getRGB())); |
|
247 | 39 | $xfWriter->setTopColor($this->addColor($style->getBorders()->getTop()->getColor()->getRGB())); |
|
248 | 39 | $xfWriter->setRightColor($this->addColor($style->getBorders()->getRight()->getColor()->getRGB())); |
|
249 | 39 | $xfWriter->setLeftColor($this->addColor($style->getBorders()->getLeft()->getColor()->getRGB())); |
|
250 | 39 | $xfWriter->setDiagColor($this->addColor($style->getBorders()->getDiagonal()->getColor()->getRGB())); |
|
251 | |||
252 | // Add the number format if it is not a built-in one and not already added |
||
253 | 39 | if ($style->getNumberFormat()->getBuiltInFormatCode() === false) { |
|
254 | 14 | $numberFormatHashCode = $style->getNumberFormat()->getHashCode(); |
|
255 | |||
256 | 14 | if (isset($this->addedNumberFormats[$numberFormatHashCode])) { |
|
257 | 6 | $numberFormatIndex = $this->addedNumberFormats[$numberFormatHashCode]; |
|
258 | } else { |
||
259 | 14 | $numberFormatIndex = 164 + count($this->numberFormats); |
|
260 | 14 | $this->numberFormats[$numberFormatIndex] = $style->getNumberFormat(); |
|
261 | 14 | $this->addedNumberFormats[$numberFormatHashCode] = $numberFormatIndex; |
|
262 | } |
||
263 | } else { |
||
264 | 39 | $numberFormatIndex = (int) $style->getNumberFormat()->getBuiltInFormatCode(); |
|
265 | } |
||
266 | |||
267 | // Assign the number format index to xf record |
||
268 | 39 | $xfWriter->setNumberFormatIndex($numberFormatIndex); |
|
269 | |||
270 | 39 | $this->xfWriters[] = $xfWriter; |
|
271 | |||
272 | 39 | $xfIndex = count($this->xfWriters) - 1; |
|
273 | |||
274 | 39 | return $xfIndex; |
|
275 | } |
||
276 | |||
277 | /** |
||
278 | * Add a font to added fonts. |
||
279 | * |
||
280 | * @param \PhpOffice\PhpSpreadsheet\Style\Font $font |
||
281 | * |
||
282 | * @return int Index to FONT record |
||
283 | */ |
||
284 | 39 | public function addFont(\PhpOffice\PhpSpreadsheet\Style\Font $font) |
|
285 | { |
||
286 | 39 | $fontHashCode = $font->getHashCode(); |
|
287 | 39 | if (isset($this->addedFonts[$fontHashCode])) { |
|
288 | 39 | $fontIndex = $this->addedFonts[$fontHashCode]; |
|
289 | } else { |
||
290 | 39 | $countFonts = count($this->fontWriters); |
|
291 | 39 | $fontIndex = ($countFonts < 4) ? $countFonts : $countFonts + 1; |
|
292 | |||
293 | 39 | $fontWriter = new Font($font); |
|
294 | 39 | $fontWriter->setColorIndex($this->addColor($font->getColor()->getRGB())); |
|
295 | 39 | $this->fontWriters[] = $fontWriter; |
|
296 | |||
297 | 39 | $this->addedFonts[$fontHashCode] = $fontIndex; |
|
298 | } |
||
299 | |||
300 | 39 | return $fontIndex; |
|
301 | } |
||
302 | |||
303 | /** |
||
304 | * Alter color palette adding a custom color. |
||
305 | * |
||
306 | * @param string $rgb E.g. 'FF00AA' |
||
307 | * |
||
308 | * @return int Color index |
||
309 | */ |
||
310 | 48 | private function addColor($rgb) |
|
311 | { |
||
312 | 48 | if (!isset($this->colors[$rgb])) { |
|
313 | $color = |
||
314 | [ |
||
315 | 48 | hexdec(substr($rgb, 0, 2)), |
|
316 | 48 | hexdec(substr($rgb, 2, 2)), |
|
317 | 48 | hexdec(substr($rgb, 4)), |
|
318 | 48 | 0, |
|
319 | ]; |
||
320 | 48 | $colorIndex = array_search($color, $this->palette); |
|
321 | 48 | if ($colorIndex) { |
|
322 | 45 | $this->colors[$rgb] = $colorIndex; |
|
323 | } else { |
||
324 | 17 | if (count($this->colors) == 0) { |
|
325 | 9 | $lastColor = 7; |
|
326 | } else { |
||
327 | 15 | $lastColor = end($this->colors); |
|
328 | } |
||
329 | 17 | if ($lastColor < 57) { |
|
330 | // then we add a custom color altering the palette |
||
331 | 15 | $colorIndex = $lastColor + 1; |
|
332 | 15 | $this->palette[$colorIndex] = $color; |
|
333 | 15 | $this->colors[$rgb] = $colorIndex; |
|
334 | } else { |
||
335 | // no room for more custom colors, just map to black |
||
336 | 48 | $colorIndex = 0; |
|
337 | } |
||
338 | } |
||
339 | } else { |
||
340 | // fetch already added custom color |
||
341 | 41 | $colorIndex = $this->colors[$rgb]; |
|
342 | } |
||
343 | |||
344 | 48 | return $colorIndex; |
|
345 | } |
||
346 | |||
347 | /** |
||
348 | * Sets the colour palette to the Excel 97+ default. |
||
349 | */ |
||
350 | 48 | private function setPaletteXl97() |
|
351 | { |
||
352 | 48 | $this->palette = [ |
|
353 | 0x08 => [0x00, 0x00, 0x00, 0x00], |
||
354 | 0x09 => [0xff, 0xff, 0xff, 0x00], |
||
355 | 0x0A => [0xff, 0x00, 0x00, 0x00], |
||
356 | 0x0B => [0x00, 0xff, 0x00, 0x00], |
||
357 | 0x0C => [0x00, 0x00, 0xff, 0x00], |
||
358 | 0x0D => [0xff, 0xff, 0x00, 0x00], |
||
359 | 0x0E => [0xff, 0x00, 0xff, 0x00], |
||
360 | 0x0F => [0x00, 0xff, 0xff, 0x00], |
||
361 | 0x10 => [0x80, 0x00, 0x00, 0x00], |
||
362 | 0x11 => [0x00, 0x80, 0x00, 0x00], |
||
363 | 0x12 => [0x00, 0x00, 0x80, 0x00], |
||
364 | 0x13 => [0x80, 0x80, 0x00, 0x00], |
||
365 | 0x14 => [0x80, 0x00, 0x80, 0x00], |
||
366 | 0x15 => [0x00, 0x80, 0x80, 0x00], |
||
367 | 0x16 => [0xc0, 0xc0, 0xc0, 0x00], |
||
368 | 0x17 => [0x80, 0x80, 0x80, 0x00], |
||
369 | 0x18 => [0x99, 0x99, 0xff, 0x00], |
||
370 | 0x19 => [0x99, 0x33, 0x66, 0x00], |
||
371 | 0x1A => [0xff, 0xff, 0xcc, 0x00], |
||
372 | 0x1B => [0xcc, 0xff, 0xff, 0x00], |
||
373 | 0x1C => [0x66, 0x00, 0x66, 0x00], |
||
374 | 0x1D => [0xff, 0x80, 0x80, 0x00], |
||
375 | 0x1E => [0x00, 0x66, 0xcc, 0x00], |
||
376 | 0x1F => [0xcc, 0xcc, 0xff, 0x00], |
||
377 | 0x20 => [0x00, 0x00, 0x80, 0x00], |
||
378 | 0x21 => [0xff, 0x00, 0xff, 0x00], |
||
379 | 0x22 => [0xff, 0xff, 0x00, 0x00], |
||
380 | 0x23 => [0x00, 0xff, 0xff, 0x00], |
||
381 | 0x24 => [0x80, 0x00, 0x80, 0x00], |
||
382 | 0x25 => [0x80, 0x00, 0x00, 0x00], |
||
383 | 0x26 => [0x00, 0x80, 0x80, 0x00], |
||
384 | 0x27 => [0x00, 0x00, 0xff, 0x00], |
||
385 | 0x28 => [0x00, 0xcc, 0xff, 0x00], |
||
386 | 0x29 => [0xcc, 0xff, 0xff, 0x00], |
||
387 | 0x2A => [0xcc, 0xff, 0xcc, 0x00], |
||
388 | 0x2B => [0xff, 0xff, 0x99, 0x00], |
||
389 | 0x2C => [0x99, 0xcc, 0xff, 0x00], |
||
390 | 0x2D => [0xff, 0x99, 0xcc, 0x00], |
||
391 | 0x2E => [0xcc, 0x99, 0xff, 0x00], |
||
392 | 0x2F => [0xff, 0xcc, 0x99, 0x00], |
||
393 | 0x30 => [0x33, 0x66, 0xff, 0x00], |
||
394 | 0x31 => [0x33, 0xcc, 0xcc, 0x00], |
||
395 | 0x32 => [0x99, 0xcc, 0x00, 0x00], |
||
396 | 0x33 => [0xff, 0xcc, 0x00, 0x00], |
||
397 | 0x34 => [0xff, 0x99, 0x00, 0x00], |
||
398 | 0x35 => [0xff, 0x66, 0x00, 0x00], |
||
399 | 0x36 => [0x66, 0x66, 0x99, 0x00], |
||
400 | 0x37 => [0x96, 0x96, 0x96, 0x00], |
||
401 | 0x38 => [0x00, 0x33, 0x66, 0x00], |
||
402 | 0x39 => [0x33, 0x99, 0x66, 0x00], |
||
403 | 0x3A => [0x00, 0x33, 0x00, 0x00], |
||
404 | 0x3B => [0x33, 0x33, 0x00, 0x00], |
||
405 | 0x3C => [0x99, 0x33, 0x00, 0x00], |
||
406 | 0x3D => [0x99, 0x33, 0x66, 0x00], |
||
407 | 0x3E => [0x33, 0x33, 0x99, 0x00], |
||
408 | 0x3F => [0x33, 0x33, 0x33, 0x00], |
||
409 | ]; |
||
410 | 48 | } |
|
411 | |||
412 | /** |
||
413 | * Assemble worksheets into a workbook and send the BIFF data to an OLE |
||
414 | * storage. |
||
415 | * |
||
416 | * @param array $pWorksheetSizes The sizes in bytes of the binary worksheet streams |
||
417 | * |
||
418 | * @return string Binary data for workbook stream |
||
419 | */ |
||
420 | 39 | public function writeWorkbook(array $pWorksheetSizes) |
|
421 | { |
||
422 | 39 | $this->worksheetSizes = $pWorksheetSizes; |
|
423 | |||
424 | // Calculate the number of selected worksheet tabs and call the finalization |
||
425 | // methods for each worksheet |
||
426 | 39 | $total_worksheets = $this->spreadsheet->getSheetCount(); |
|
427 | |||
428 | // Add part 1 of the Workbook globals, what goes before the SHEET records |
||
429 | 39 | $this->storeBof(0x0005); |
|
430 | 39 | $this->writeCodepage(); |
|
431 | 39 | $this->writeWindow1(); |
|
432 | |||
433 | 39 | $this->writeDateMode(); |
|
434 | 39 | $this->writeAllFonts(); |
|
435 | 39 | $this->writeAllNumberFormats(); |
|
436 | 39 | $this->writeAllXfs(); |
|
437 | 39 | $this->writeAllStyles(); |
|
438 | 39 | $this->writePalette(); |
|
439 | |||
440 | // Prepare part 3 of the workbook global stream, what goes after the SHEET records |
||
441 | 39 | $part3 = ''; |
|
442 | 39 | if ($this->countryCode != -1) { |
|
443 | $part3 .= $this->writeCountry(); |
||
444 | } |
||
445 | 39 | $part3 .= $this->writeRecalcId(); |
|
446 | |||
447 | 39 | $part3 .= $this->writeSupbookInternal(); |
|
448 | /* TODO: store external SUPBOOK records and XCT and CRN records |
||
449 | in case of external references for BIFF8 */ |
||
450 | 39 | $part3 .= $this->writeExternalsheetBiff8(); |
|
451 | 39 | $part3 .= $this->writeAllDefinedNamesBiff8(); |
|
452 | 39 | $part3 .= $this->writeMsoDrawingGroup(); |
|
453 | 39 | $part3 .= $this->writeSharedStringsTable(); |
|
454 | |||
455 | 39 | $part3 .= $this->writeEof(); |
|
456 | |||
457 | // Add part 2 of the Workbook globals, the SHEET records |
||
458 | 39 | $this->calcSheetOffsets(); |
|
459 | 39 | for ($i = 0; $i < $total_worksheets; ++$i) { |
|
460 | 39 | $this->writeBoundSheet($this->spreadsheet->getSheet($i), $this->worksheetOffsets[$i]); |
|
461 | } |
||
462 | |||
463 | // Add part 3 of the Workbook globals |
||
464 | 39 | $this->_data .= $part3; |
|
465 | |||
466 | 39 | return $this->_data; |
|
467 | } |
||
468 | |||
469 | /** |
||
470 | * Calculate offsets for Worksheet BOF records. |
||
471 | */ |
||
472 | 39 | private function calcSheetOffsets() |
|
473 | { |
||
474 | 39 | $boundsheet_length = 10; // fixed length for a BOUNDSHEET record |
|
475 | |||
476 | // size of Workbook globals part 1 + 3 |
||
477 | 39 | $offset = $this->_datasize; |
|
478 | |||
479 | // add size of Workbook globals part 2, the length of the SHEET records |
||
480 | 39 | $total_worksheets = count($this->spreadsheet->getAllSheets()); |
|
481 | 39 | foreach ($this->spreadsheet->getWorksheetIterator() as $sheet) { |
|
482 | 39 | $offset += $boundsheet_length + strlen(StringHelper::UTF8toBIFF8UnicodeShort($sheet->getTitle())); |
|
483 | } |
||
484 | |||
485 | // add the sizes of each of the Sheet substreams, respectively |
||
486 | 39 | for ($i = 0; $i < $total_worksheets; ++$i) { |
|
487 | 39 | $this->worksheetOffsets[$i] = $offset; |
|
488 | 39 | $offset += $this->worksheetSizes[$i]; |
|
489 | } |
||
490 | 39 | $this->biffSize = $offset; |
|
491 | 39 | } |
|
492 | |||
493 | /** |
||
494 | * Store the Excel FONT records. |
||
495 | */ |
||
496 | 39 | private function writeAllFonts() |
|
497 | { |
||
498 | 39 | foreach ($this->fontWriters as $fontWriter) { |
|
499 | 39 | $this->append($fontWriter->writeFont()); |
|
500 | } |
||
501 | 39 | } |
|
502 | |||
503 | /** |
||
504 | * Store user defined numerical formats i.e. FORMAT records. |
||
505 | */ |
||
506 | 39 | private function writeAllNumberFormats() |
|
507 | { |
||
508 | 39 | foreach ($this->numberFormats as $numberFormatIndex => $numberFormat) { |
|
509 | 14 | $this->writeNumberFormat($numberFormat->getFormatCode(), $numberFormatIndex); |
|
510 | } |
||
511 | 39 | } |
|
512 | |||
513 | /** |
||
514 | * Write all XF records. |
||
515 | */ |
||
516 | 39 | private function writeAllXfs() |
|
520 | } |
||
521 | 39 | } |
|
522 | |||
523 | /** |
||
524 | * Write all STYLE records. |
||
525 | */ |
||
526 | 39 | private function writeAllStyles() |
|
529 | 39 | } |
|
530 | |||
531 | /** |
||
532 | * Writes all the DEFINEDNAME records (BIFF8). |
||
533 | * So far this is only used for repeating rows/columns (print titles) and print areas. |
||
534 | */ |
||
535 | 39 | private function writeAllDefinedNamesBiff8() |
|
536 | { |
||
537 | 39 | $chunk = ''; |
|
538 | |||
539 | // Named ranges |
||
540 | 39 | if (count($this->spreadsheet->getNamedRanges()) > 0) { |
|
541 | // Loop named ranges |
||
542 | 4 | $namedRanges = $this->spreadsheet->getNamedRanges(); |
|
543 | 4 | foreach ($namedRanges as $namedRange) { |
|
544 | // Create absolute coordinate |
||
545 | 4 | $range = Coordinate::splitRange($namedRange->getRange()); |
|
546 | 4 | $iMax = count($range); |
|
547 | 4 | View Code Duplication | for ($i = 0; $i < $iMax; ++$i) { |
548 | 4 | $range[$i][0] = '\'' . str_replace("'", "''", $namedRange->getWorksheet()->getTitle()) . '\'!' . Coordinate::absoluteCoordinate($range[$i][0]); |
|
549 | 4 | if (isset($range[$i][1])) { |
|
550 | 3 | $range[$i][1] = Coordinate::absoluteCoordinate($range[$i][1]); |
|
551 | } |
||
552 | } |
||
553 | 4 | $range = Coordinate::buildRange($range); // e.g. Sheet1!$A$1:$B$2 |
|
554 | |||
555 | // parse formula |
||
556 | try { |
||
557 | 4 | $error = $this->parser->parse($range); |
|
558 | 4 | $formulaData = $this->parser->toReversePolish(); |
|
559 | |||
560 | // make sure tRef3d is of type tRef3dR (0x3A) |
||
561 | 4 | if (isset($formulaData[0]) and ($formulaData[0] == "\x7A" or $formulaData[0] == "\x5A")) { |
|
562 | 1 | $formulaData = "\x3A" . substr($formulaData, 1); |
|
563 | } |
||
564 | |||
565 | 4 | if ($namedRange->getLocalOnly()) { |
|
566 | // local scope |
||
567 | $scope = $this->spreadsheet->getIndex($namedRange->getScope()) + 1; |
||
568 | } else { |
||
569 | // global scope |
||
570 | 4 | $scope = 0; |
|
571 | } |
||
572 | 4 | $chunk .= $this->writeData($this->writeDefinedNameBiff8($namedRange->getName(), $formulaData, $scope, false)); |
|
573 | 4 | } catch (PhpSpreadsheetException $e) { |
|
574 | // do nothing |
||
575 | } |
||
576 | } |
||
577 | } |
||
578 | |||
579 | // total number of sheets |
||
580 | 39 | $total_worksheets = $this->spreadsheet->getSheetCount(); |
|
581 | |||
582 | // write the print titles (repeating rows, columns), if any |
||
583 | 39 | for ($i = 0; $i < $total_worksheets; ++$i) { |
|
584 | 39 | $sheetSetup = $this->spreadsheet->getSheet($i)->getPageSetup(); |
|
585 | // simultaneous repeatColumns repeatRows |
||
586 | 39 | if ($sheetSetup->isColumnsToRepeatAtLeftSet() && $sheetSetup->isRowsToRepeatAtTopSet()) { |
|
587 | $repeat = $sheetSetup->getColumnsToRepeatAtLeft(); |
||
588 | $colmin = Coordinate::columnIndexFromString($repeat[0]) - 1; |
||
589 | $colmax = Coordinate::columnIndexFromString($repeat[1]) - 1; |
||
590 | |||
591 | $repeat = $sheetSetup->getRowsToRepeatAtTop(); |
||
592 | $rowmin = $repeat[0] - 1; |
||
593 | $rowmax = $repeat[1] - 1; |
||
594 | |||
595 | // construct formula data manually |
||
596 | $formulaData = pack('Cv', 0x29, 0x17); // tMemFunc |
||
597 | $formulaData .= pack('Cvvvvv', 0x3B, $i, 0, 65535, $colmin, $colmax); // tArea3d |
||
598 | $formulaData .= pack('Cvvvvv', 0x3B, $i, $rowmin, $rowmax, 0, 255); // tArea3d |
||
599 | $formulaData .= pack('C', 0x10); // tList |
||
600 | |||
601 | // store the DEFINEDNAME record |
||
602 | $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x07), $formulaData, $i + 1, true)); |
||
603 | |||
604 | // (exclusive) either repeatColumns or repeatRows |
||
605 | 39 | } elseif ($sheetSetup->isColumnsToRepeatAtLeftSet() || $sheetSetup->isRowsToRepeatAtTopSet()) { |
|
606 | // Columns to repeat |
||
607 | 1 | if ($sheetSetup->isColumnsToRepeatAtLeftSet()) { |
|
608 | $repeat = $sheetSetup->getColumnsToRepeatAtLeft(); |
||
609 | $colmin = Coordinate::columnIndexFromString($repeat[0]) - 1; |
||
610 | $colmax = Coordinate::columnIndexFromString($repeat[1]) - 1; |
||
611 | } else { |
||
612 | 1 | $colmin = 0; |
|
613 | 1 | $colmax = 255; |
|
614 | } |
||
615 | // Rows to repeat |
||
616 | 1 | if ($sheetSetup->isRowsToRepeatAtTopSet()) { |
|
617 | 1 | $repeat = $sheetSetup->getRowsToRepeatAtTop(); |
|
618 | 1 | $rowmin = $repeat[0] - 1; |
|
619 | 1 | $rowmax = $repeat[1] - 1; |
|
620 | } else { |
||
621 | $rowmin = 0; |
||
622 | $rowmax = 65535; |
||
623 | } |
||
624 | |||
625 | // construct formula data manually because parser does not recognize absolute 3d cell references |
||
626 | 1 | $formulaData = pack('Cvvvvv', 0x3B, $i, $rowmin, $rowmax, $colmin, $colmax); |
|
627 | |||
628 | // store the DEFINEDNAME record |
||
629 | 1 | $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x07), $formulaData, $i + 1, true)); |
|
630 | } |
||
631 | } |
||
632 | |||
633 | // write the print areas, if any |
||
634 | 39 | for ($i = 0; $i < $total_worksheets; ++$i) { |
|
635 | 39 | $sheetSetup = $this->spreadsheet->getSheet($i)->getPageSetup(); |
|
636 | 39 | if ($sheetSetup->isPrintAreaSet()) { |
|
637 | // Print area, e.g. A3:J6,H1:X20 |
||
638 | $printArea = Coordinate::splitRange($sheetSetup->getPrintArea()); |
||
639 | $countPrintArea = count($printArea); |
||
640 | |||
641 | $formulaData = ''; |
||
642 | for ($j = 0; $j < $countPrintArea; ++$j) { |
||
643 | $printAreaRect = $printArea[$j]; // e.g. A3:J6 |
||
644 | $printAreaRect[0] = Coordinate::coordinateFromString($printAreaRect[0]); |
||
645 | $printAreaRect[1] = Coordinate::coordinateFromString($printAreaRect[1]); |
||
646 | |||
647 | $print_rowmin = $printAreaRect[0][1] - 1; |
||
648 | $print_rowmax = $printAreaRect[1][1] - 1; |
||
649 | $print_colmin = Coordinate::columnIndexFromString($printAreaRect[0][0]) - 1; |
||
650 | $print_colmax = Coordinate::columnIndexFromString($printAreaRect[1][0]) - 1; |
||
651 | |||
652 | // construct formula data manually because parser does not recognize absolute 3d cell references |
||
653 | $formulaData .= pack('Cvvvvv', 0x3B, $i, $print_rowmin, $print_rowmax, $print_colmin, $print_colmax); |
||
654 | |||
655 | if ($j > 0) { |
||
656 | $formulaData .= pack('C', 0x10); // list operator token ',' |
||
657 | } |
||
658 | } |
||
659 | |||
660 | // store the DEFINEDNAME record |
||
661 | $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x06), $formulaData, $i + 1, true)); |
||
662 | } |
||
663 | } |
||
664 | |||
665 | // write autofilters, if any |
||
666 | 39 | for ($i = 0; $i < $total_worksheets; ++$i) { |
|
667 | 39 | $sheetAutoFilter = $this->spreadsheet->getSheet($i)->getAutoFilter(); |
|
668 | 39 | $autoFilterRange = $sheetAutoFilter->getRange(); |
|
669 | 39 | if (!empty($autoFilterRange)) { |
|
670 | 3 | $rangeBounds = Coordinate::rangeBoundaries($autoFilterRange); |
|
671 | |||
672 | //Autofilter built in name |
||
673 | 3 | $name = pack('C', 0x0D); |
|
674 | |||
675 | 3 | $chunk .= $this->writeData($this->writeShortNameBiff8($name, $i + 1, $rangeBounds, true)); |
|
676 | } |
||
677 | } |
||
678 | |||
679 | 39 | return $chunk; |
|
680 | } |
||
681 | |||
682 | /** |
||
683 | * Write a DEFINEDNAME record for BIFF8 using explicit binary formula data. |
||
684 | * |
||
685 | * @param string $name The name in UTF-8 |
||
686 | * @param string $formulaData The binary formula data |
||
687 | * @param int $sheetIndex 1-based sheet index the defined name applies to. 0 = global |
||
688 | * @param bool $isBuiltIn Built-in name? |
||
689 | * |
||
690 | * @return string Complete binary record data |
||
691 | */ |
||
692 | 5 | private function writeDefinedNameBiff8($name, $formulaData, $sheetIndex = 0, $isBuiltIn = false) |
|
693 | { |
||
694 | 5 | $record = 0x0018; |
|
695 | |||
696 | // option flags |
||
697 | 5 | $options = $isBuiltIn ? 0x20 : 0x00; |
|
698 | |||
699 | // length of the name, character count |
||
700 | 5 | $nlen = StringHelper::countCharacters($name); |
|
701 | |||
702 | // name with stripped length field |
||
703 | 5 | $name = substr(StringHelper::UTF8toBIFF8UnicodeLong($name), 2); |
|
704 | |||
705 | // size of the formula (in bytes) |
||
706 | 5 | $sz = strlen($formulaData); |
|
707 | |||
708 | // combine the parts |
||
709 | 5 | $data = pack('vCCvvvCCCC', $options, 0, $nlen, $sz, 0, $sheetIndex, 0, 0, 0, 0) |
|
710 | 5 | . $name . $formulaData; |
|
711 | 5 | $length = strlen($data); |
|
712 | |||
713 | 5 | $header = pack('vv', $record, $length); |
|
714 | |||
715 | 5 | return $header . $data; |
|
716 | } |
||
717 | |||
718 | /** |
||
719 | * Write a short NAME record. |
||
720 | * |
||
721 | * @param string $name |
||
722 | * @param string $sheetIndex 1-based sheet index the defined name applies to. 0 = global |
||
723 | * @param integer[][] $rangeBounds range boundaries |
||
724 | * @param bool $isHidden |
||
725 | * |
||
726 | * @return string Complete binary record data |
||
727 | * */ |
||
728 | 3 | private function writeShortNameBiff8($name, $sheetIndex, $rangeBounds, $isHidden = false) |
|
729 | { |
||
730 | 3 | $record = 0x0018; |
|
731 | |||
732 | // option flags |
||
733 | 3 | $options = ($isHidden ? 0x21 : 0x00); |
|
734 | |||
735 | 3 | $extra = pack( |
|
736 | 3 | 'Cvvvvv', |
|
737 | 3 | 0x3B, |
|
738 | 3 | $sheetIndex - 1, |
|
739 | 3 | $rangeBounds[0][1] - 1, |
|
740 | 3 | $rangeBounds[1][1] - 1, |
|
741 | 3 | $rangeBounds[0][0] - 1, |
|
742 | 3 | $rangeBounds[1][0] - 1 |
|
743 | ); |
||
744 | |||
745 | // size of the formula (in bytes) |
||
746 | 3 | $sz = strlen($extra); |
|
747 | |||
748 | // combine the parts |
||
749 | 3 | $data = pack('vCCvvvCCCCC', $options, 0, 1, $sz, 0, $sheetIndex, 0, 0, 0, 0, 0) |
|
750 | 3 | . $name . $extra; |
|
751 | 3 | $length = strlen($data); |
|
752 | |||
753 | 3 | $header = pack('vv', $record, $length); |
|
754 | |||
755 | 3 | return $header . $data; |
|
756 | } |
||
757 | |||
758 | /** |
||
759 | * Stores the CODEPAGE biff record. |
||
760 | */ |
||
761 | 39 | View Code Duplication | private function writeCodepage() |
762 | { |
||
763 | 39 | $record = 0x0042; // Record identifier |
|
764 | 39 | $length = 0x0002; // Number of bytes to follow |
|
765 | 39 | $cv = $this->codepage; // The code page |
|
766 | |||
767 | 39 | $header = pack('vv', $record, $length); |
|
768 | 39 | $data = pack('v', $cv); |
|
769 | |||
770 | 39 | $this->append($header . $data); |
|
771 | 39 | } |
|
772 | |||
773 | /** |
||
774 | * Write Excel BIFF WINDOW1 record. |
||
775 | */ |
||
776 | 39 | private function writeWindow1() |
|
777 | { |
||
778 | 39 | $record = 0x003D; // Record identifier |
|
779 | 39 | $length = 0x0012; // Number of bytes to follow |
|
780 | |||
781 | 39 | $xWn = 0x0000; // Horizontal position of window |
|
782 | 39 | $yWn = 0x0000; // Vertical position of window |
|
783 | 39 | $dxWn = 0x25BC; // Width of window |
|
784 | 39 | $dyWn = 0x1572; // Height of window |
|
785 | |||
786 | 39 | $grbit = 0x0038; // Option flags |
|
787 | |||
788 | // not supported by PhpSpreadsheet, so there is only one selected sheet, the active |
||
789 | 39 | $ctabsel = 1; // Number of workbook tabs selected |
|
790 | |||
791 | 39 | $wTabRatio = 0x0258; // Tab to scrollbar ratio |
|
792 | |||
793 | // not supported by PhpSpreadsheet, set to 0 |
||
794 | 39 | $itabFirst = 0; // 1st displayed worksheet |
|
795 | 39 | $itabCur = $this->spreadsheet->getActiveSheetIndex(); // Active worksheet |
|
796 | |||
797 | 39 | $header = pack('vv', $record, $length); |
|
798 | 39 | $data = pack('vvvvvvvvv', $xWn, $yWn, $dxWn, $dyWn, $grbit, $itabCur, $itabFirst, $ctabsel, $wTabRatio); |
|
799 | 39 | $this->append($header . $data); |
|
800 | 39 | } |
|
801 | |||
802 | /** |
||
803 | * Writes Excel BIFF BOUNDSHEET record. |
||
804 | * |
||
805 | * @param Worksheet $sheet Worksheet name |
||
806 | * @param int $offset Location of worksheet BOF |
||
807 | */ |
||
808 | 39 | private function writeBoundSheet($sheet, $offset) |
|
809 | { |
||
810 | 39 | $sheetname = $sheet->getTitle(); |
|
811 | 39 | $record = 0x0085; // Record identifier |
|
812 | |||
813 | // sheet state |
||
814 | 39 | switch ($sheet->getSheetState()) { |
|
815 | 39 | case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_VISIBLE: |
|
816 | 39 | $ss = 0x00; |
|
817 | |||
818 | 39 | break; |
|
819 | case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_HIDDEN: |
||
820 | $ss = 0x01; |
||
821 | |||
822 | break; |
||
823 | case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_VERYHIDDEN: |
||
824 | $ss = 0x02; |
||
825 | |||
826 | break; |
||
827 | default: |
||
828 | $ss = 0x00; |
||
829 | |||
830 | break; |
||
831 | } |
||
832 | |||
833 | // sheet type |
||
834 | 39 | $st = 0x00; |
|
835 | |||
836 | 39 | $grbit = 0x0000; // Visibility and sheet type |
|
837 | |||
838 | 39 | $data = pack('VCC', $offset, $ss, $st); |
|
839 | 39 | $data .= StringHelper::UTF8toBIFF8UnicodeShort($sheetname); |
|
840 | |||
841 | 39 | $length = strlen($data); |
|
842 | 39 | $header = pack('vv', $record, $length); |
|
843 | 39 | $this->append($header . $data); |
|
844 | 39 | } |
|
845 | |||
846 | /** |
||
847 | * Write Internal SUPBOOK record. |
||
848 | */ |
||
849 | 39 | View Code Duplication | private function writeSupbookInternal() |
850 | { |
||
851 | 39 | $record = 0x01AE; // Record identifier |
|
852 | 39 | $length = 0x0004; // Bytes to follow |
|
853 | |||
854 | 39 | $header = pack('vv', $record, $length); |
|
855 | 39 | $data = pack('vv', $this->spreadsheet->getSheetCount(), 0x0401); |
|
856 | |||
857 | 39 | return $this->writeData($header . $data); |
|
858 | } |
||
859 | |||
860 | /** |
||
861 | * Writes the Excel BIFF EXTERNSHEET record. These references are used by |
||
862 | * formulas. |
||
863 | */ |
||
864 | 39 | private function writeExternalsheetBiff8() |
|
865 | { |
||
866 | 39 | $totalReferences = count($this->parser->references); |
|
867 | 39 | $record = 0x0017; // Record identifier |
|
868 | 39 | $length = 2 + 6 * $totalReferences; // Number of bytes to follow |
|
869 | |||
870 | 39 | $supbook_index = 0; // FIXME: only using internal SUPBOOK record |
|
871 | 39 | $header = pack('vv', $record, $length); |
|
872 | 39 | $data = pack('v', $totalReferences); |
|
873 | 39 | for ($i = 0; $i < $totalReferences; ++$i) { |
|
874 | 39 | $data .= $this->parser->references[$i]; |
|
875 | } |
||
876 | |||
877 | 39 | return $this->writeData($header . $data); |
|
878 | } |
||
879 | |||
880 | /** |
||
881 | * Write Excel BIFF STYLE records. |
||
882 | */ |
||
883 | 39 | private function writeStyle() |
|
884 | { |
||
885 | 39 | $record = 0x0293; // Record identifier |
|
886 | 39 | $length = 0x0004; // Bytes to follow |
|
887 | |||
888 | 39 | $ixfe = 0x8000; // Index to cell style XF |
|
889 | 39 | $BuiltIn = 0x00; // Built-in style |
|
890 | 39 | $iLevel = 0xff; // Outline style level |
|
891 | |||
892 | 39 | $header = pack('vv', $record, $length); |
|
893 | 39 | $data = pack('vCC', $ixfe, $BuiltIn, $iLevel); |
|
894 | 39 | $this->append($header . $data); |
|
895 | 39 | } |
|
896 | |||
897 | /** |
||
898 | * Writes Excel FORMAT record for non "built-in" numerical formats. |
||
899 | * |
||
900 | * @param string $format Custom format string |
||
901 | * @param int $ifmt Format index code |
||
902 | */ |
||
903 | 14 | private function writeNumberFormat($format, $ifmt) |
|
913 | 14 | } |
|
914 | |||
915 | /** |
||
916 | * Write DATEMODE record to indicate the date system in use (1904 or 1900). |
||
917 | */ |
||
918 | 39 | private function writeDateMode() |
|
919 | { |
||
920 | 39 | $record = 0x0022; // Record identifier |
|
921 | 39 | $length = 0x0002; // Bytes to follow |
|
922 | |||
923 | 39 | $f1904 = (Date::getExcelCalendar() == Date::CALENDAR_MAC_1904) |
|
924 | ? 1 |
||
925 | 39 | : 0; // Flag for 1904 date system |
|
926 | |||
927 | 39 | $header = pack('vv', $record, $length); |
|
928 | 39 | $data = pack('v', $f1904); |
|
929 | 39 | $this->append($header . $data); |
|
930 | 39 | } |
|
931 | |||
932 | /** |
||
933 | * Stores the COUNTRY record for localization. |
||
934 | * |
||
935 | * @return string |
||
936 | */ |
||
937 | View Code Duplication | private function writeCountry() |
|
947 | } |
||
948 | |||
949 | /** |
||
950 | * Write the RECALCID record. |
||
951 | * |
||
952 | * @return string |
||
953 | */ |
||
954 | 39 | View Code Duplication | private function writeRecalcId() |
955 | { |
||
956 | 39 | $record = 0x01C1; // Record identifier |
|
957 | 39 | $length = 8; // Number of bytes to follow |
|
958 | |||
959 | 39 | $header = pack('vv', $record, $length); |
|
960 | |||
961 | // by inspection of real Excel files, MS Office Excel 2007 writes this |
||
962 | 39 | $data = pack('VV', 0x000001C1, 0x00001E667); |
|
963 | |||
964 | 39 | return $this->writeData($header . $data); |
|
965 | } |
||
966 | |||
967 | /** |
||
968 | * Stores the PALETTE biff record. |
||
969 | */ |
||
970 | 39 | private function writePalette() |
|
988 | 39 | } |
|
989 | |||
990 | /** |
||
991 | * Handling of the SST continue blocks is complicated by the need to include an |
||
992 | * additional continuation byte depending on whether the string is split between |
||
993 | * blocks or whether it starts at the beginning of the block. (There are also |
||
994 | * additional complications that will arise later when/if Rich Strings are |
||
995 | * supported). |
||
996 | * |
||
997 | * The Excel documentation says that the SST record should be followed by an |
||
998 | * EXTSST record. The EXTSST record is a hash table that is used to optimise |
||
999 | * access to SST. However, despite the documentation it doesn't seem to be |
||
1000 | * required so we will ignore it. |
||
1001 | * |
||
1002 | * @return string Binary data |
||
1003 | */ |
||
1004 | 39 | private function writeSharedStringsTable() |
|
1005 | { |
||
1006 | // maximum size of record data (excluding record header) |
||
1007 | 39 | $continue_limit = 8224; |
|
1008 | |||
1009 | // initialize array of record data blocks |
||
1010 | 39 | $recordDatas = []; |
|
1011 | |||
1012 | // start SST record data block with total number of strings, total number of unique strings |
||
1013 | 39 | $recordData = pack('VV', $this->stringTotal, $this->stringUnique); |
|
1014 | |||
1015 | // loop through all (unique) strings in shared strings table |
||
1016 | 39 | foreach (array_keys($this->stringTable) as $string) { |
|
1017 | // here $string is a BIFF8 encoded string |
||
1018 | |||
1019 | // length = character count |
||
1020 | 35 | $headerinfo = unpack('vlength/Cencoding', $string); |
|
1021 | |||
1022 | // currently, this is always 1 = uncompressed |
||
1023 | 35 | $encoding = $headerinfo['encoding']; |
|
1024 | |||
1025 | // initialize finished writing current $string |
||
1026 | 35 | $finished = false; |
|
1027 | |||
1028 | 35 | while ($finished === false) { |
|
1029 | // normally, there will be only one cycle, but if string cannot immediately be written as is |
||
1030 | // there will be need for more than one cylcle, if string longer than one record data block, there |
||
1031 | // may be need for even more cycles |
||
1032 | |||
1033 | 35 | if (strlen($recordData) + strlen($string) <= $continue_limit) { |
|
1034 | // then we can write the string (or remainder of string) without any problems |
||
1035 | 35 | $recordData .= $string; |
|
1036 | |||
1037 | 35 | if (strlen($recordData) + strlen($string) == $continue_limit) { |
|
1038 | // we close the record data block, and initialize a new one |
||
1039 | $recordDatas[] = $recordData; |
||
1040 | $recordData = ''; |
||
1041 | } |
||
1042 | |||
1043 | // we are finished writing this string |
||
1044 | 35 | $finished = true; |
|
1045 | } else { |
||
1046 | // special treatment writing the string (or remainder of the string) |
||
1047 | // If the string is very long it may need to be written in more than one CONTINUE record. |
||
1048 | |||
1049 | // check how many bytes more there is room for in the current record |
||
1050 | $space_remaining = $continue_limit - strlen($recordData); |
||
1051 | |||
1052 | // minimum space needed |
||
1053 | // uncompressed: 2 byte string length length field + 1 byte option flags + 2 byte character |
||
1054 | // compressed: 2 byte string length length field + 1 byte option flags + 1 byte character |
||
1055 | $min_space_needed = ($encoding == 1) ? 5 : 4; |
||
1056 | |||
1057 | // We have two cases |
||
1058 | // 1. space remaining is less than minimum space needed |
||
1059 | // here we must waste the space remaining and move to next record data block |
||
1060 | // 2. space remaining is greater than or equal to minimum space needed |
||
1061 | // here we write as much as we can in the current block, then move to next record data block |
||
1062 | |||
1063 | // 1. space remaining is less than minimum space needed |
||
1064 | if ($space_remaining < $min_space_needed) { |
||
1065 | // we close the block, store the block data |
||
1066 | $recordDatas[] = $recordData; |
||
1067 | |||
1068 | // and start new record data block where we start writing the string |
||
1069 | $recordData = ''; |
||
1070 | |||
1071 | // 2. space remaining is greater than or equal to minimum space needed |
||
1072 | } else { |
||
1073 | // initialize effective remaining space, for Unicode strings this may need to be reduced by 1, see below |
||
1074 | $effective_space_remaining = $space_remaining; |
||
1075 | |||
1076 | // for uncompressed strings, sometimes effective space remaining is reduced by 1 |
||
1077 | if ($encoding == 1 && (strlen($string) - $space_remaining) % 2 == 1) { |
||
1078 | --$effective_space_remaining; |
||
1079 | } |
||
1080 | |||
1081 | // one block fininshed, store the block data |
||
1082 | $recordData .= substr($string, 0, $effective_space_remaining); |
||
1083 | |||
1084 | $string = substr($string, $effective_space_remaining); // for next cycle in while loop |
||
1085 | $recordDatas[] = $recordData; |
||
1086 | |||
1087 | // start new record data block with the repeated option flags |
||
1088 | $recordData = pack('C', $encoding); |
||
1089 | } |
||
1090 | } |
||
1091 | } |
||
1092 | } |
||
1093 | |||
1094 | // Store the last record data block unless it is empty |
||
1095 | // if there was no need for any continue records, this will be the for SST record data block itself |
||
1096 | 39 | if (strlen($recordData) > 0) { |
|
1097 | 39 | $recordDatas[] = $recordData; |
|
1098 | } |
||
1099 | |||
1100 | // combine into one chunk with all the blocks SST, CONTINUE,... |
||
1101 | 39 | $chunk = ''; |
|
1102 | 39 | foreach ($recordDatas as $i => $recordData) { |
|
1103 | // first block should have the SST record header, remaing should have CONTINUE header |
||
1104 | 39 | $record = ($i == 0) ? 0x00FC : 0x003C; |
|
1105 | |||
1106 | 39 | $header = pack('vv', $record, strlen($recordData)); |
|
1107 | 39 | $data = $header . $recordData; |
|
1108 | |||
1109 | 39 | $chunk .= $this->writeData($data); |
|
1110 | } |
||
1111 | |||
1112 | 39 | return $chunk; |
|
1113 | } |
||
1114 | |||
1115 | /** |
||
1116 | * Writes the MSODRAWINGGROUP record if needed. Possibly split using CONTINUE records. |
||
1117 | */ |
||
1118 | 39 | private function writeMsoDrawingGroup() |
|
1133 | } |
||
1134 | |||
1135 | /** |
||
1136 | * Get Escher object. |
||
1137 | * |
||
1138 | * @return \PhpOffice\PhpSpreadsheet\Shared\Escher |
||
1139 | */ |
||
1140 | public function getEscher() |
||
1143 | } |
||
1144 | |||
1145 | /** |
||
1146 | * Set Escher object. |
||
1147 | * |
||
1148 | * @param \PhpOffice\PhpSpreadsheet\Shared\Escher $pValue |
||
1149 | */ |
||
1150 | 10 | public function setEscher(\PhpOffice\PhpSpreadsheet\Shared\Escher $pValue = null) |
|
1151 | { |
||
1152 | 10 | $this->escher = $pValue; |
|
1153 | 10 | } |
|
1154 | } |
||
1155 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths