Failed Conditions
Pull Request — master (#3743)
by Adrien
14:08
created

Spreadsheet::getSharedComponent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 0
cts 0
cp 0
rs 10
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet;
4
5
use JsonSerializable;
6
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
7
use PhpOffice\PhpSpreadsheet\Document\Properties;
8
use PhpOffice\PhpSpreadsheet\Document\Security;
9
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
10
use PhpOffice\PhpSpreadsheet\Shared\File;
11
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
12
use PhpOffice\PhpSpreadsheet\Style\Style;
13
use PhpOffice\PhpSpreadsheet\Worksheet\Iterator;
14
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
15
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
16
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
17
18
class Spreadsheet implements JsonSerializable
19
{
20
    // Allowable values for workbook window visilbity
21
    const VISIBILITY_VISIBLE = 'visible';
22
    const VISIBILITY_HIDDEN = 'hidden';
23
    const VISIBILITY_VERY_HIDDEN = 'veryHidden';
24
25
    private const DEFINED_NAME_IS_RANGE = false;
26
    private const DEFINED_NAME_IS_FORMULA = true;
27
28
    private const WORKBOOK_VIEW_VISIBILITY_VALUES = [
29
        self::VISIBILITY_VISIBLE,
30
        self::VISIBILITY_HIDDEN,
31
        self::VISIBILITY_VERY_HIDDEN,
32
    ];
33
34
    /**
35
     * Unique ID.
36
     */
37
    private string $uniqueID;
38
39
    /**
40
     * Document properties.
41
     */
42
    private Properties $properties;
43
44
    /**
45
     * Document security.
46
     */
47
    private Security $security;
48
49
    /**
50
     * Collection of Worksheet objects.
51
     *
52
     * @var Worksheet[]
53
     */
54
    private $workSheetCollection = [];
55
56
    /**
57
     * Calculation Engine.
58
     *
59
     * @var null|Calculation
60
     */
61
    private $calculationEngine;
62
63
    /**
64
     * Active sheet index.
65
     */
66
    private int $activeSheetIndex;
67
68
    /**
69
     * Named ranges.
70
     *
71
     * @var DefinedName[]
72
     */
73
    private $definedNames = [];
74
75
    /**
76
     * CellXf supervisor.
77
     */
78
    private Style $cellXfSupervisor;
79
80
    /**
81
     * CellXf collection.
82
     *
83
     * @var Style[]
84
     */
85
    private $cellXfCollection = [];
86
87
    /**
88
     * CellStyleXf collection.
89
     *
90
     * @var Style[]
91
     */
92
    private $cellStyleXfCollection = [];
93
94
    /**
95
     * hasMacros : this workbook have macros ?
96
     *
97
     * @var bool
98
     */
99
    private $hasMacros = false;
100
101
    /**
102
     * macrosCode : all macros code as binary data (the vbaProject.bin file, this include form, code,  etc.), null if no macro.
103
     *
104
     * @var null|string
105
     */
106
    private $macrosCode;
107
108
    /**
109
     * macrosCertificate : if macros are signed, contains binary data vbaProjectSignature.bin file, null if not signed.
110
     *
111
     * @var null|string
112
     */
113
    private $macrosCertificate;
114
115
    /**
116
     * ribbonXMLData : null if workbook is'nt Excel 2007 or not contain a customized UI.
117
     *
118
     * @var null|array{target: string, data: string}
119
     */
120
    private $ribbonXMLData;
121
122
    /**
123
     * ribbonBinObjects : null if workbook is'nt Excel 2007 or not contain embedded objects (picture(s)) for Ribbon Elements
124
     * ignored if $ribbonXMLData is null.
125
     *
126
     * @var null|array
127
     */
128
    private $ribbonBinObjects;
129
130
    /**
131
     * List of unparsed loaded data for export to same format with better compatibility.
132
     * It has to be minimized when the library start to support currently unparsed data.
133
     *
134
     * @var array
135
     */
136
    private $unparsedLoadedData = [];
137
138
    /**
139
     * Controls visibility of the horizonal scroll bar in the application.
140
     *
141
     * @var bool
142
     */
143
    private $showHorizontalScroll = true;
144
145
    /**
146
     * Controls visibility of the horizonal scroll bar in the application.
147
     *
148
     * @var bool
149
     */
150
    private $showVerticalScroll = true;
151
152
    /**
153
     * Controls visibility of the sheet tabs in the application.
154
     *
155
     * @var bool
156
     */
157
    private $showSheetTabs = true;
158
159
    /**
160
     * Specifies a boolean value that indicates whether the workbook window
161
     * is minimized.
162
     *
163
     * @var bool
164
     */
165
    private $minimized = false;
166
167
    /**
168
     * Specifies a boolean value that indicates whether to group dates
169
     * when presenting the user with filtering optiomd in the user
170
     * interface.
171
     *
172
     * @var bool
173
     */
174
    private $autoFilterDateGrouping = true;
175
176
    /**
177
     * Specifies the index to the first sheet in the book view.
178
     *
179
     * @var int
180
     */
181
    private $firstSheetIndex = 0;
182
183
    /**
184
     * Specifies the visible status of the workbook.
185
     *
186
     * @var string
187
     */
188
    private $visibility = self::VISIBILITY_VISIBLE;
189
190
    /**
191
     * Specifies the ratio between the workbook tabs bar and the horizontal
192
     * scroll bar.  TabRatio is assumed to be out of 1000 of the horizontal
193
     * window width.
194
     *
195
     * @var int
196
     */
197
    private $tabRatio = 600;
198
199
    private Theme $theme;
200
201 649
    public function getTheme(): Theme
202
    {
203 649
        return $this->theme;
204
    }
205
206
    /**
207
     * The workbook has macros ?
208
     *
209
     * @return bool
210
     */
211 342
    public function hasMacros()
212
    {
213 342
        return $this->hasMacros;
214
    }
215
216
    /**
217
     * Define if a workbook has macros.
218
     *
219
     * @param bool $hasMacros true|false
220
     */
221 3
    public function setHasMacros($hasMacros): void
222
    {
223 3
        $this->hasMacros = (bool) $hasMacros;
224
    }
225
226
    /**
227
     * Set the macros code.
228
     *
229
     * @param string $macroCode string|null
230
     */
231 3
    public function setMacrosCode($macroCode): void
232
    {
233 3
        $this->macrosCode = $macroCode;
234 3
        $this->setHasMacros($macroCode !== null);
235
    }
236
237
    /**
238
     * Return the macros code.
239
     *
240
     * @return null|string
241
     */
242 3
    public function getMacrosCode()
243
    {
244 3
        return $this->macrosCode;
245
    }
246
247
    /**
248
     * Set the macros certificate.
249
     *
250
     * @param null|string $certificate
251
     */
252 3
    public function setMacrosCertificate($certificate): void
253
    {
254 3
        $this->macrosCertificate = $certificate;
255
    }
256
257
    /**
258
     * Is the project signed ?
259
     *
260
     * @return bool true|false
261
     */
262 2
    public function hasMacrosCertificate(): bool
263
    {
264 2
        return $this->macrosCertificate !== null;
265
    }
266
267
    /**
268
     * Return the macros certificate.
269
     *
270
     * @return null|string
271
     */
272 2
    public function getMacrosCertificate()
273
    {
274 2
        return $this->macrosCertificate;
275
    }
276
277
    /**
278
     * Remove all macros, certificate from spreadsheet.
279
     */
280 1
    public function discardMacros(): void
281
    {
282 1
        $this->hasMacros = false;
283 1
        $this->macrosCode = null;
284 1
        $this->macrosCertificate = null;
285
    }
286
287
    /**
288
     * set ribbon XML data.
289
     *
290
     * @param null|mixed $target
291
     * @param null|mixed $xmlData
292
     */
293 2
    public function setRibbonXMLData($target, $xmlData): void
294
    {
295 2
        if ($target !== null && $xmlData !== null) {
296 2
            $this->ribbonXMLData = ['target' => $target, 'data' => $xmlData];
297
        } else {
298
            $this->ribbonXMLData = null;
299
        }
300
    }
301
302
    /**
303
     * retrieve ribbon XML Data.
304
     *
305
     * @param string $what
306
     *
307
     * @return null|array|string
308
     */
309 304
    public function getRibbonXMLData($what = 'all') //we need some constants here...
310
    {
311 304
        $returnData = null;
312 304
        $what = strtolower($what);
313
        switch ($what) {
314 304
            case 'all':
315 2
                $returnData = $this->ribbonXMLData;
316
317 2
                break;
318 304
            case 'target':
319 2
            case 'data':
320 304
                if (is_array($this->ribbonXMLData)) {
321 2
                    $returnData = $this->ribbonXMLData[$what];
322
                }
323
324 304
                break;
325
        }
326
327 304
        return $returnData;
328
    }
329
330
    /**
331
     * store binaries ribbon objects (pictures).
332
     *
333
     * @param null|mixed $BinObjectsNames
334
     * @param null|mixed $BinObjectsData
335
     */
336 2
    public function setRibbonBinObjects($BinObjectsNames, $BinObjectsData): void
337
    {
338 2
        if ($BinObjectsNames !== null && $BinObjectsData !== null) {
339
            $this->ribbonBinObjects = ['names' => $BinObjectsNames, 'data' => $BinObjectsData];
340
        } else {
341 2
            $this->ribbonBinObjects = null;
342
        }
343
    }
344
345
    /**
346
     * List of unparsed loaded data for export to same format with better compatibility.
347
     * It has to be minimized when the library start to support currently unparsed data.
348
     *
349
     * @internal
350
     *
351
     * @return array
352
     */
353 343
    public function getUnparsedLoadedData()
354
    {
355 343
        return $this->unparsedLoadedData;
356
    }
357
358
    /**
359
     * List of unparsed loaded data for export to same format with better compatibility.
360
     * It has to be minimized when the library start to support currently unparsed data.
361
     *
362
     * @internal
363
     */
364 553
    public function setUnparsedLoadedData(array $unparsedLoadedData): void
365
    {
366 553
        $this->unparsedLoadedData = $unparsedLoadedData;
367
    }
368
369
    /**
370
     * return the extension of a filename. Internal use for a array_map callback (php<5.3 don't like lambda function).
371
     */
372
    private function getExtensionOnly(mixed $path): string
373
    {
374
        $extension = pathinfo($path, PATHINFO_EXTENSION);
375
376
        return substr($extension, 0);
377
    }
378
379
    /**
380
     * retrieve Binaries Ribbon Objects.
381
     *
382
     * @param string $what
383
     *
384
     * @return null|array
385
     */
386 2
    public function getRibbonBinObjects($what = 'all')
387
    {
388 2
        $ReturnData = null;
389 2
        $what = strtolower($what);
390
        switch ($what) {
391 2
            case 'all':
392 2
                return $this->ribbonBinObjects;
393 1
            case 'names':
394 1
            case 'data':
395 1
                if (is_array($this->ribbonBinObjects) && isset($this->ribbonBinObjects[$what])) {
396
                    $ReturnData = $this->ribbonBinObjects[$what];
397
                }
398
399 1
                break;
400 1
            case 'types':
401
                if (
402 1
                    is_array($this->ribbonBinObjects)
403 1
                    && isset($this->ribbonBinObjects['data']) && is_array($this->ribbonBinObjects['data'])
404
                ) {
405
                    $tmpTypes = array_keys($this->ribbonBinObjects['data']);
406
                    $ReturnData = array_unique(array_map([$this, 'getExtensionOnly'], $tmpTypes));
407
                } else {
408 1
                    $ReturnData = []; // the caller want an array... not null if empty
409
                }
410
411 1
                break;
412
        }
413
414 1
        return $ReturnData;
415
    }
416
417
    /**
418
     * This workbook have a custom UI ?
419
     */
420 304
    public function hasRibbon(): bool
421
    {
422 304
        return $this->ribbonXMLData !== null;
423
    }
424
425
    /**
426
     * This workbook have additionnal object for the ribbon ?
427
     */
428 304
    public function hasRibbonBinObjects(): bool
429
    {
430 304
        return $this->ribbonBinObjects !== null;
431
    }
432
433
    /**
434
     * Check if a sheet with a specified code name already exists.
435
     *
436
     * @param string $codeName Name of the worksheet to check
437
     */
438 9859
    public function sheetCodeNameExists($codeName): bool
439
    {
440 9859
        return $this->getSheetByCodeName($codeName) !== null;
441
    }
442
443
    /**
444
     * Get sheet by code name. Warning : sheet don't have always a code name !
445
     *
446
     * @param string $codeName Sheet name
447
     *
448
     * @return null|Worksheet
449
     */
450 9859
    public function getSheetByCodeName($codeName)
451
    {
452 9859
        $worksheetCount = count($this->workSheetCollection);
453 9859
        for ($i = 0; $i < $worksheetCount; ++$i) {
454 655
            if ($this->workSheetCollection[$i]->getCodeName() == $codeName) {
455 483
                return $this->workSheetCollection[$i];
456
            }
457
        }
458
459 9859
        return null;
460
    }
461
462
    /**
463
     * Create a new PhpSpreadsheet with one Worksheet.
464
     */
465 9859
    public function __construct()
466
    {
467 9859
        $this->uniqueID = uniqid('', true);
468 9859
        $this->calculationEngine = new Calculation($this);
469 9859
        $this->theme = new Theme();
470
471
        // Initialise worksheet collection and add one worksheet
472 9859
        $this->workSheetCollection = [];
473 9859
        $this->workSheetCollection[] = new Worksheet($this);
474 9859
        $this->activeSheetIndex = 0;
475
476
        // Create document properties
477 9859
        $this->properties = new Document\Properties();
478
479
        // Create document security
480 9859
        $this->security = new Document\Security();
481
482
        // Set defined names
483 9859
        $this->definedNames = [];
484
485
        // Create the cellXf supervisor
486 9859
        $this->cellXfSupervisor = new Style(true);
487 9859
        $this->cellXfSupervisor->bindParent($this);
488
489
        // Create the default style
490 9859
        $this->addCellXf(new Style());
491 9859
        $this->addCellStyleXf(new Style());
492
    }
493
494
    /**
495
     * Code to execute when this worksheet is unset().
496
     */
497 113
    public function __destruct()
498
    {
499 113
        $this->disconnectWorksheets();
500 113
        $this->calculationEngine = null;
501 113
        $this->cellXfCollection = [];
502 113
        $this->cellStyleXfCollection = [];
503 113
        $this->definedNames = [];
504
    }
505
506
    /**
507
     * Disconnect all worksheets from this PhpSpreadsheet workbook object,
508
     * typically so that the PhpSpreadsheet object can be unset.
509
     */
510 8432
    public function disconnectWorksheets(): void
511
    {
512 8432
        foreach ($this->workSheetCollection as $worksheet) {
513 8429
            $worksheet->disconnectCells();
514 8429
            unset($worksheet);
515
        }
516 8432
        $this->workSheetCollection = [];
517
    }
518
519
    /**
520
     * Return the calculation engine for this worksheet.
521
     *
522
     * @return null|Calculation
523
     */
524 9859
    public function getCalculationEngine()
525
    {
526 9859
        return $this->calculationEngine;
527
    }
528
529
    /**
530
     * Get properties.
531
     *
532
     * @return Document\Properties
533
     */
534 1327
    public function getProperties()
535
    {
536 1327
        return $this->properties;
537
    }
538
539
    /**
540
     * Set properties.
541
     */
542 1
    public function setProperties(Document\Properties $documentProperties): void
543
    {
544 1
        $this->properties = $documentProperties;
545
    }
546
547
    /**
548
     * Get security.
549
     *
550
     * @return Document\Security
551
     */
552 314
    public function getSecurity()
553
    {
554 314
        return $this->security;
555
    }
556
557
    /**
558
     * Set security.
559
     */
560 1
    public function setSecurity(Document\Security $documentSecurity): void
561
    {
562 1
        $this->security = $documentSecurity;
563
    }
564
565
    /**
566
     * Get active sheet.
567
     *
568
     * @return Worksheet
569
     */
570 9804
    public function getActiveSheet()
571
    {
572 9804
        return $this->getSheet($this->activeSheetIndex);
573
    }
574
575
    /**
576
     * Create sheet and add it to this workbook.
577
     *
578
     * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
579
     */
580 1008
    public function createSheet($sheetIndex = null): Worksheet
581
    {
582 1008
        $newSheet = new Worksheet($this);
583 1008
        $this->addSheet($newSheet, $sheetIndex);
584
585 1008
        return $newSheet;
586
    }
587
588
    /**
589
     * Check if a sheet with a specified name already exists.
590
     *
591
     * @param string $worksheetName Name of the worksheet to check
592
     */
593 9859
    public function sheetNameExists($worksheetName): bool
594
    {
595 9859
        return $this->getSheetByName($worksheetName) !== null;
596
    }
597
598
    /**
599
     * Add sheet.
600
     *
601
     * @param Worksheet $worksheet The worksheet to add
602
     * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
603
     */
604 1089
    public function addSheet(Worksheet $worksheet, $sheetIndex = null): Worksheet
605
    {
606 1089
        if ($this->sheetNameExists($worksheet->getTitle())) {
607 1
            throw new Exception(
608 1
                "Workbook already contains a worksheet named '{$worksheet->getTitle()}'. Rename this worksheet first."
609 1
            );
610
        }
611
612 1089
        if ($sheetIndex === null) {
613 1060
            if ($this->activeSheetIndex < 0) {
614 721
                $this->activeSheetIndex = 0;
615
            }
616 1060
            $this->workSheetCollection[] = $worksheet;
617
        } else {
618
            // Insert the sheet at the requested index
619 32
            array_splice(
620 32
                $this->workSheetCollection,
621 32
                $sheetIndex,
622 32
                0,
623 32
                [$worksheet]
624 32
            );
625
626
            // Adjust active sheet index if necessary
627 32
            if ($this->activeSheetIndex >= $sheetIndex) {
628 28
                ++$this->activeSheetIndex;
629
            }
630
        }
631
632 1089
        if ($worksheet->getParent() === null) {
633 50
            $worksheet->rebindParent($this);
634
        }
635
636 1089
        return $worksheet;
637
    }
638
639
    /**
640
     * Remove sheet by index.
641
     *
642
     * @param int $sheetIndex Index position of the worksheet to remove
643
     */
644 745
    public function removeSheetByIndex($sheetIndex): void
645
    {
646 745
        $numSheets = count($this->workSheetCollection);
647 745
        if ($sheetIndex > $numSheets - 1) {
648 1
            throw new Exception(
649 1
                "You tried to remove a sheet by the out of bounds index: {$sheetIndex}. The actual number of sheets is {$numSheets}."
650 1
            );
651
        }
652 744
        array_splice($this->workSheetCollection, $sheetIndex, 1);
653
654
        // Adjust active sheet index if necessary
655
        if (
656 744
            ($this->activeSheetIndex >= $sheetIndex)
657 744
            && ($this->activeSheetIndex > 0 || $numSheets <= 1)
658
        ) {
659 742
            --$this->activeSheetIndex;
660
        }
661
    }
662
663
    /**
664
     * Get sheet by index.
665
     *
666
     * @param int $sheetIndex Sheet index
667
     *
668
     * @return Worksheet
669
     */
670 9812
    public function getSheet($sheetIndex)
671
    {
672 9812
        if (!isset($this->workSheetCollection[$sheetIndex])) {
673 1
            $numSheets = $this->getSheetCount();
674
675 1
            throw new Exception(
676 1
                "Your requested sheet index: {$sheetIndex} is out of bounds. The actual number of sheets is {$numSheets}."
677 1
            );
678
        }
679
680 9812
        return $this->workSheetCollection[$sheetIndex];
681
    }
682
683
    /**
684
     * Get all sheets.
685
     *
686
     * @return Worksheet[]
687
     */
688 137
    public function getAllSheets()
689
    {
690 137
        return $this->workSheetCollection;
691
    }
692
693
    /**
694
     * Get sheet by name.
695
     *
696
     * @param string $worksheetName Sheet name
697
     *
698
     * @return null|Worksheet
699
     */
700 9859
    public function getSheetByName($worksheetName)
701
    {
702 9859
        $worksheetCount = count($this->workSheetCollection);
703 9859
        for ($i = 0; $i < $worksheetCount; ++$i) {
704 8668
            if ($this->workSheetCollection[$i]->getTitle() === trim($worksheetName, "'")) {
705 8141
                return $this->workSheetCollection[$i];
706
            }
707
        }
708
709 9859
        return null;
710
    }
711
712
    /**
713
     * Get sheet by name, throwing exception if not found.
714
     */
715 213
    public function getSheetByNameOrThrow(string $worksheetName): Worksheet
716
    {
717 213
        $worksheet = $this->getSheetByName($worksheetName);
718 213
        if ($worksheet === null) {
719 1
            throw new Exception("Sheet $worksheetName does not exist.");
720
        }
721
722 212
        return $worksheet;
723
    }
724
725
    /**
726
     * Get index for sheet.
727
     *
728
     * @return int index
729
     */
730 9101
    public function getIndex(Worksheet $worksheet)
731
    {
732 9101
        foreach ($this->workSheetCollection as $key => $value) {
733 9101
            if ($value->getHashCode() === $worksheet->getHashCode()) {
734 9100
                return $key;
735
            }
736
        }
737
738 2
        throw new Exception('Sheet does not exist.');
739
    }
740
741
    /**
742
     * Set index for sheet by sheet name.
743
     *
744
     * @param string $worksheetName Sheet name to modify index for
745
     * @param int $newIndexPosition New index for the sheet
746
     *
747
     * @return int New sheet index
748
     */
749 1
    public function setIndexByName(string $worksheetName, $newIndexPosition)
750
    {
751 1
        $oldIndex = $this->getIndex($this->getSheetByNameOrThrow($worksheetName));
752 1
        $worksheet = array_splice(
753 1
            $this->workSheetCollection,
754 1
            $oldIndex,
755 1
            1
756 1
        );
757 1
        array_splice(
758 1
            $this->workSheetCollection,
759 1
            $newIndexPosition,
760 1
            0,
761 1
            $worksheet
762 1
        );
763
764 1
        return $newIndexPosition;
765
    }
766
767
    /**
768
     * Get sheet count.
769
     */
770 1328
    public function getSheetCount(): int
771
    {
772 1328
        return count($this->workSheetCollection);
773
    }
774
775
    /**
776
     * Get active sheet index.
777
     *
778
     * @return int Active sheet index
779
     */
780 8069
    public function getActiveSheetIndex()
781
    {
782 8069
        return $this->activeSheetIndex;
783
    }
784
785
    /**
786
     * Set active sheet index.
787
     *
788
     * @param int $worksheetIndex Active sheet index
789
     *
790
     * @return Worksheet
791
     */
792 9153
    public function setActiveSheetIndex($worksheetIndex)
793
    {
794 9153
        $numSheets = count($this->workSheetCollection);
795
796 9153
        if ($worksheetIndex > $numSheets - 1) {
797 6
            throw new Exception(
798 6
                "You tried to set a sheet active by the out of bounds index: {$worksheetIndex}. The actual number of sheets is {$numSheets}."
799 6
            );
800
        }
801 9147
        $this->activeSheetIndex = $worksheetIndex;
802
803 9147
        return $this->getActiveSheet();
804
    }
805
806
    /**
807
     * Set active sheet index by name.
808
     *
809
     * @param string $worksheetName Sheet title
810
     *
811
     * @return Worksheet
812
     */
813 78
    public function setActiveSheetIndexByName(string $worksheetName)
814
    {
815 78
        if (($worksheet = $this->getSheetByName($worksheetName)) instanceof Worksheet) {
816 76
            $this->setActiveSheetIndex($this->getIndex($worksheet));
817
818 76
            return $worksheet;
819
        }
820
821 2
        throw new Exception('Workbook does not contain sheet:' . $worksheetName);
822
    }
823
824
    /**
825
     * Get sheet names.
826
     *
827
     * @return string[]
828
     */
829 10
    public function getSheetNames(): array
830
    {
831 10
        $returnValue = [];
832 10
        $worksheetCount = $this->getSheetCount();
833 10
        for ($i = 0; $i < $worksheetCount; ++$i) {
834 10
            $returnValue[] = $this->getSheet($i)->getTitle();
835
        }
836
837 10
        return $returnValue;
838
    }
839
840
    /**
841
     * Add external sheet.
842
     *
843
     * @param Worksheet $worksheet External sheet to add
844
     * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
845
     */
846 5
    public function addExternalSheet(Worksheet $worksheet, $sheetIndex = null): Worksheet
847
    {
848 5
        if ($this->sheetNameExists($worksheet->getTitle())) {
849 1
            throw new Exception("Workbook already contains a worksheet named '{$worksheet->getTitle()}'. Rename the external sheet first.");
850
        }
851
852
        // count how many cellXfs there are in this workbook currently, we will need this below
853 4
        $countCellXfs = count($this->cellXfCollection);
854
855
        // copy all the shared cellXfs from the external workbook and append them to the current
856 4
        foreach ($worksheet->getParentOrThrow()->getCellXfCollection() as $cellXf) {
857 4
            $this->addCellXf(clone $cellXf);
858
        }
859
860
        // move sheet to this workbook
861 4
        $worksheet->rebindParent($this);
862
863
        // update the cellXfs
864 4
        foreach ($worksheet->getCoordinates(false) as $coordinate) {
865 4
            $cell = $worksheet->getCell($coordinate);
866 4
            $cell->setXfIndex($cell->getXfIndex() + $countCellXfs);
867
        }
868
869
        // update the column dimensions Xfs
870 4
        foreach ($worksheet->getColumnDimensions() as $columnDimension) {
871 1
            $columnDimension->setXfIndex($columnDimension->getXfIndex() + $countCellXfs);
872
        }
873
874
        // update the row dimensions Xfs
875 4
        foreach ($worksheet->getRowDimensions() as $rowDimension) {
876 1
            $xfIndex = $rowDimension->getXfIndex();
877 1
            if ($xfIndex !== null) {
878 1
                $rowDimension->setXfIndex($xfIndex + $countCellXfs);
879
            }
880
        }
881
882 4
        return $this->addSheet($worksheet, $sheetIndex);
883
    }
884
885
    /**
886
     * Get an array of all Named Ranges.
887
     *
888
     * @return DefinedName[]
889
     */
890 9
    public function getNamedRanges(): array
891
    {
892 9
        return array_filter(
893 9
            $this->definedNames,
894 9
            fn (DefinedName $definedName): bool => $definedName->isFormula() === self::DEFINED_NAME_IS_RANGE
895 9
        );
896
    }
897
898
    /**
899
     * Get an array of all Named Formulae.
900
     *
901
     * @return DefinedName[]
902
     */
903 15
    public function getNamedFormulae(): array
904
    {
905 15
        return array_filter(
906 15
            $this->definedNames,
907 15
            fn (DefinedName $definedName): bool => $definedName->isFormula() === self::DEFINED_NAME_IS_FORMULA
908 15
        );
909
    }
910
911
    /**
912
     * Get an array of all Defined Names (both named ranges and named formulae).
913
     *
914
     * @return DefinedName[]
915
     */
916 450
    public function getDefinedNames(): array
917
    {
918 450
        return $this->definedNames;
919
    }
920
921
    /**
922
     * Add a named range.
923
     * If a named range with this name already exists, then this will replace the existing value.
924
     */
925 218
    public function addNamedRange(NamedRange $namedRange): void
926
    {
927 218
        $this->addDefinedName($namedRange);
928
    }
929
930
    /**
931
     * Add a named formula.
932
     * If a named formula with this name already exists, then this will replace the existing value.
933
     */
934 12
    public function addNamedFormula(NamedFormula $namedFormula): void
935
    {
936 12
        $this->addDefinedName($namedFormula);
937
    }
938
939
    /**
940
     * Add a defined name (either a named range or a named formula).
941
     * If a defined named with this name already exists, then this will replace the existing value.
942
     */
943 336
    public function addDefinedName(DefinedName $definedName): void
944
    {
945 336
        $upperCaseName = StringHelper::strToUpper($definedName->getName());
946 336
        if ($definedName->getScope() == null) {
947
            // global scope
948 323
            $this->definedNames[$upperCaseName] = $definedName;
949
        } else {
950
            // local scope
951 120
            $this->definedNames[$definedName->getScope()->getTitle() . '!' . $upperCaseName] = $definedName;
952
        }
953
    }
954
955
    /**
956
     * Get named range.
957
     *
958
     * @param null|Worksheet $worksheet Scope. Use null for global scope
959
     */
960 27
    public function getNamedRange(string $namedRange, ?Worksheet $worksheet = null): ?NamedRange
961
    {
962 27
        $returnValue = null;
963
964 27
        if ($namedRange !== '') {
965 27
            $namedRange = StringHelper::strToUpper($namedRange);
966
            // first look for global named range
967 27
            $returnValue = $this->getGlobalDefinedNameByType($namedRange, self::DEFINED_NAME_IS_RANGE);
968
            // then look for local named range (has priority over global named range if both names exist)
969 27
            $returnValue = $this->getLocalDefinedNameByType($namedRange, self::DEFINED_NAME_IS_RANGE, $worksheet) ?: $returnValue;
970
        }
971
972 27
        return $returnValue instanceof NamedRange ? $returnValue : null;
973
    }
974
975
    /**
976
     * Get named formula.
977
     *
978
     * @param null|Worksheet $worksheet Scope. Use null for global scope
979
     */
980 11
    public function getNamedFormula(string $namedFormula, ?Worksheet $worksheet = null): ?NamedFormula
981
    {
982 11
        $returnValue = null;
983
984 11
        if ($namedFormula !== '') {
985 11
            $namedFormula = StringHelper::strToUpper($namedFormula);
986
            // first look for global named formula
987 11
            $returnValue = $this->getGlobalDefinedNameByType($namedFormula, self::DEFINED_NAME_IS_FORMULA);
988
            // then look for local named formula (has priority over global named formula if both names exist)
989 11
            $returnValue = $this->getLocalDefinedNameByType($namedFormula, self::DEFINED_NAME_IS_FORMULA, $worksheet) ?: $returnValue;
990
        }
991
992 11
        return $returnValue instanceof NamedFormula ? $returnValue : null;
993
    }
994
995 38
    private function getGlobalDefinedNameByType(string $name, bool $type): ?DefinedName
996
    {
997 38
        if (isset($this->definedNames[$name]) && $this->definedNames[$name]->isFormula() === $type) {
998 28
            return $this->definedNames[$name];
999
        }
1000
1001 12
        return null;
1002
    }
1003
1004 38
    private function getLocalDefinedNameByType(string $name, bool $type, ?Worksheet $worksheet = null): ?DefinedName
1005
    {
1006
        if (
1007 38
            ($worksheet !== null) && isset($this->definedNames[$worksheet->getTitle() . '!' . $name])
1008 38
            && $this->definedNames[$worksheet->getTitle() . '!' . $name]->isFormula() === $type
1009
        ) {
1010 8
            return $this->definedNames[$worksheet->getTitle() . '!' . $name];
1011
        }
1012
1013 36
        return null;
1014
    }
1015
1016
    /**
1017
     * Get named range.
1018
     *
1019
     * @param null|Worksheet $worksheet Scope. Use null for global scope
1020
     */
1021 9077
    public function getDefinedName(string $definedName, ?Worksheet $worksheet = null): ?DefinedName
1022
    {
1023 9077
        $returnValue = null;
1024
1025 9077
        if ($definedName !== '') {
1026 9077
            $definedName = StringHelper::strToUpper($definedName);
1027
            // first look for global defined name
1028 9077
            if (isset($this->definedNames[$definedName])) {
1029 118
                $returnValue = $this->definedNames[$definedName];
1030
            }
1031
1032
            // then look for local defined name (has priority over global defined name if both names exist)
1033 9077
            if (($worksheet !== null) && isset($this->definedNames[$worksheet->getTitle() . '!' . $definedName])) {
1034 20
                $returnValue = $this->definedNames[$worksheet->getTitle() . '!' . $definedName];
1035
            }
1036
        }
1037
1038 9077
        return $returnValue;
1039
    }
1040
1041
    /**
1042
     * Remove named range.
1043
     *
1044
     * @param null|Worksheet $worksheet scope: use null for global scope
1045
     *
1046
     * @return $this
1047
     */
1048 5
    public function removeNamedRange(string $namedRange, ?Worksheet $worksheet = null): self
1049
    {
1050 5
        if ($this->getNamedRange($namedRange, $worksheet) === null) {
1051 1
            return $this;
1052
        }
1053
1054 4
        return $this->removeDefinedName($namedRange, $worksheet);
1055
    }
1056
1057
    /**
1058
     * Remove named formula.
1059
     *
1060
     * @param null|Worksheet $worksheet scope: use null for global scope
1061
     *
1062
     * @return $this
1063
     */
1064 4
    public function removeNamedFormula(string $namedFormula, ?Worksheet $worksheet = null): self
1065
    {
1066 4
        if ($this->getNamedFormula($namedFormula, $worksheet) === null) {
1067 1
            return $this;
1068
        }
1069
1070 3
        return $this->removeDefinedName($namedFormula, $worksheet);
1071
    }
1072
1073
    /**
1074
     * Remove defined name.
1075
     *
1076
     * @param null|Worksheet $worksheet scope: use null for global scope
1077
     *
1078
     * @return $this
1079
     */
1080 11
    public function removeDefinedName(string $definedName, ?Worksheet $worksheet = null): self
1081
    {
1082 11
        $definedName = StringHelper::strToUpper($definedName);
1083
1084 11
        if ($worksheet === null) {
1085 1
            if (isset($this->definedNames[$definedName])) {
1086 1
                unset($this->definedNames[$definedName]);
1087
            }
1088
        } else {
1089 10
            if (isset($this->definedNames[$worksheet->getTitle() . '!' . $definedName])) {
1090 3
                unset($this->definedNames[$worksheet->getTitle() . '!' . $definedName]);
1091 7
            } elseif (isset($this->definedNames[$definedName])) {
1092 7
                unset($this->definedNames[$definedName]);
1093
            }
1094
        }
1095
1096 11
        return $this;
1097
    }
1098
1099
    /**
1100
     * Get worksheet iterator.
1101
     */
1102 1170
    public function getWorksheetIterator(): Iterator
1103
    {
1104 1170
        return new Iterator($this);
1105
    }
1106
1107
    /**
1108
     * Copy workbook (!= clone!).
1109
     */
1110 1
    public function copy(): self
1111
    {
1112 1
        $filename = File::temporaryFilename();
1113 1
        $writer = new XlsxWriter($this);
1114 1
        $writer->setIncludeCharts(true);
1115 1
        $writer->save($filename);
1116
1117 1
        $reader = new XlsxReader();
1118 1
        $reader->setIncludeCharts(true);
1119 1
        $reloadedSpreadsheet = $reader->load($filename);
1120 1
        unlink($filename);
1121
1122 1
        return $reloadedSpreadsheet;
1123
    }
1124
1125 1
    public function __clone()
1126
    {
1127 1
        throw new Exception(
1128 1
            'Do not use clone on spreadsheet. Use spreadsheet->copy() instead.'
1129 1
        );
1130
    }
1131
1132
    /**
1133
     * Get the workbook collection of cellXfs.
1134
     *
1135
     * @return Style[]
1136
     */
1137 911
    public function getCellXfCollection()
1138
    {
1139 911
        return $this->cellXfCollection;
1140
    }
1141
1142
    /**
1143
     * Get cellXf by index.
1144
     *
1145
     * @param int $cellStyleIndex
1146
     *
1147
     * @return Style
1148
     */
1149 8857
    public function getCellXfByIndex($cellStyleIndex)
1150
    {
1151 8857
        return $this->cellXfCollection[$cellStyleIndex];
1152
    }
1153
1154
    /**
1155
     * Get cellXf by hash code.
1156
     *
1157
     * @param string $hashcode
1158
     *
1159
     * @return false|Style
1160
     */
1161 1168
    public function getCellXfByHashCode($hashcode)
1162
    {
1163 1168
        foreach ($this->cellXfCollection as $cellXf) {
1164 1168
            if ($cellXf->getHashCode() === $hashcode) {
1165 597
                return $cellXf;
1166
            }
1167
        }
1168
1169 723
        return false;
1170
    }
1171
1172
    /**
1173
     * Check if style exists in style collection.
1174
     */
1175 1
    public function cellXfExists(Style $cellStyleIndex): bool
1176
    {
1177 1
        return in_array($cellStyleIndex, $this->cellXfCollection, true);
1178
    }
1179
1180
    /**
1181
     * Get default style.
1182
     *
1183
     * @return Style
1184
     */
1185 839
    public function getDefaultStyle()
1186
    {
1187 839
        if (isset($this->cellXfCollection[0])) {
1188 838
            return $this->cellXfCollection[0];
1189
        }
1190
1191 1
        throw new Exception('No default style found for this workbook');
1192
    }
1193
1194
    /**
1195
     * Add a cellXf to the workbook.
1196
     */
1197 9859
    public function addCellXf(Style $style): void
1198
    {
1199 9859
        $this->cellXfCollection[] = $style;
1200 9859
        $style->setIndex(count($this->cellXfCollection) - 1);
1201
    }
1202
1203
    /**
1204
     * Remove cellXf by index. It is ensured that all cells get their xf index updated.
1205
     *
1206
     * @param int $cellStyleIndex Index to cellXf
1207
     */
1208 644
    public function removeCellXfByIndex($cellStyleIndex): void
1209
    {
1210 644
        if ($cellStyleIndex > count($this->cellXfCollection) - 1) {
1211 1
            throw new Exception('CellXf index is out of bounds.');
1212
        }
1213
1214
        // first remove the cellXf
1215 643
        array_splice($this->cellXfCollection, $cellStyleIndex, 1);
1216
1217
        // then update cellXf indexes for cells
1218 643
        foreach ($this->workSheetCollection as $worksheet) {
1219 2
            foreach ($worksheet->getCoordinates(false) as $coordinate) {
1220 1
                $cell = $worksheet->getCell($coordinate);
1221 1
                $xfIndex = $cell->getXfIndex();
1222 1
                if ($xfIndex > $cellStyleIndex) {
1223
                    // decrease xf index by 1
1224 1
                    $cell->setXfIndex($xfIndex - 1);
1225 1
                } elseif ($xfIndex == $cellStyleIndex) {
1226
                    // set to default xf index 0
1227 1
                    $cell->setXfIndex(0);
1228
                }
1229
            }
1230
        }
1231
    }
1232
1233
    /**
1234
     * Get the cellXf supervisor.
1235
     *
1236
     * @return Style
1237
     */
1238 8831
    public function getCellXfSupervisor()
1239
    {
1240 8831
        return $this->cellXfSupervisor;
1241
    }
1242
1243
    /**
1244
     * Get the workbook collection of cellStyleXfs.
1245
     *
1246
     * @return Style[]
1247
     */
1248 1
    public function getCellStyleXfCollection()
1249
    {
1250 1
        return $this->cellStyleXfCollection;
1251
    }
1252
1253
    /**
1254
     * Get cellStyleXf by index.
1255
     *
1256
     * @param int $cellStyleIndex Index to cellXf
1257
     *
1258
     * @return Style
1259
     */
1260 1
    public function getCellStyleXfByIndex($cellStyleIndex)
1261
    {
1262 1
        return $this->cellStyleXfCollection[$cellStyleIndex];
1263
    }
1264
1265
    /**
1266
     * Get cellStyleXf by hash code.
1267
     *
1268
     * @param string $hashcode
1269
     *
1270
     * @return false|Style
1271
     */
1272 1
    public function getCellStyleXfByHashCode($hashcode)
1273
    {
1274 1
        foreach ($this->cellStyleXfCollection as $cellStyleXf) {
1275 1
            if ($cellStyleXf->getHashCode() === $hashcode) {
1276 1
                return $cellStyleXf;
1277
            }
1278
        }
1279
1280 1
        return false;
1281
    }
1282
1283
    /**
1284
     * Add a cellStyleXf to the workbook.
1285
     */
1286 9859
    public function addCellStyleXf(Style $style): void
1287
    {
1288 9859
        $this->cellStyleXfCollection[] = $style;
1289 9859
        $style->setIndex(count($this->cellStyleXfCollection) - 1);
1290
    }
1291
1292
    /**
1293
     * Remove cellStyleXf by index.
1294
     *
1295
     * @param int $cellStyleIndex Index to cellXf
1296
     */
1297 642
    public function removeCellStyleXfByIndex($cellStyleIndex): void
1298
    {
1299 642
        if ($cellStyleIndex > count($this->cellStyleXfCollection) - 1) {
1300 1
            throw new Exception('CellStyleXf index is out of bounds.');
1301
        }
1302 641
        array_splice($this->cellStyleXfCollection, $cellStyleIndex, 1);
1303
    }
1304
1305
    /**
1306
     * Eliminate all unneeded cellXf and afterwards update the xfIndex for all cells
1307
     * and columns in the workbook.
1308
     */
1309 839
    public function garbageCollect(): void
1310
    {
1311
        // how many references are there to each cellXf ?
1312 839
        $countReferencesCellXf = [];
1313 839
        foreach ($this->cellXfCollection as $index => $cellXf) {
1314 839
            $countReferencesCellXf[$index] = 0;
1315
        }
1316
1317 839
        foreach ($this->getWorksheetIterator() as $sheet) {
1318
            // from cells
1319 839
            foreach ($sheet->getCoordinates(false) as $coordinate) {
1320 814
                $cell = $sheet->getCell($coordinate);
1321 814
                ++$countReferencesCellXf[$cell->getXfIndex()];
1322
            }
1323
1324
            // from row dimensions
1325 839
            foreach ($sheet->getRowDimensions() as $rowDimension) {
1326 67
                if ($rowDimension->getXfIndex() !== null) {
1327 8
                    ++$countReferencesCellXf[$rowDimension->getXfIndex()];
1328
                }
1329
            }
1330
1331
            // from column dimensions
1332 839
            foreach ($sheet->getColumnDimensions() as $columnDimension) {
1333 109
                ++$countReferencesCellXf[$columnDimension->getXfIndex()];
1334
            }
1335
        }
1336
1337
        // remove cellXfs without references and create mapping so we can update xfIndex
1338
        // for all cells and columns
1339 839
        $countNeededCellXfs = 0;
1340 839
        $map = [];
1341 839
        foreach ($this->cellXfCollection as $index => $cellXf) {
1342 839
            if ($countReferencesCellXf[$index] > 0 || $index == 0) { // we must never remove the first cellXf
1343 839
                ++$countNeededCellXfs;
1344
            } else {
1345 28
                unset($this->cellXfCollection[$index]);
1346
            }
1347 839
            $map[$index] = $countNeededCellXfs - 1;
1348
        }
1349 839
        $this->cellXfCollection = array_values($this->cellXfCollection);
1350
1351
        // update the index for all cellXfs
1352 839
        foreach ($this->cellXfCollection as $i => $cellXf) {
1353 839
            $cellXf->setIndex($i);
1354
        }
1355
1356
        // make sure there is always at least one cellXf (there should be)
1357 839
        if (empty($this->cellXfCollection)) {
1358
            $this->cellXfCollection[] = new Style();
1359
        }
1360
1361
        // update the xfIndex for all cells, row dimensions, column dimensions
1362 839
        foreach ($this->getWorksheetIterator() as $sheet) {
1363
            // for all cells
1364 839
            foreach ($sheet->getCoordinates(false) as $coordinate) {
1365 814
                $cell = $sheet->getCell($coordinate);
1366 814
                $cell->setXfIndex($map[$cell->getXfIndex()]);
1367
            }
1368
1369
            // for all row dimensions
1370 839
            foreach ($sheet->getRowDimensions() as $rowDimension) {
1371 67
                if ($rowDimension->getXfIndex() !== null) {
1372 8
                    $rowDimension->setXfIndex($map[$rowDimension->getXfIndex()]);
1373
                }
1374
            }
1375
1376
            // for all column dimensions
1377 839
            foreach ($sheet->getColumnDimensions() as $columnDimension) {
1378 109
                $columnDimension->setXfIndex($map[$columnDimension->getXfIndex()]);
1379
            }
1380
1381
            // also do garbage collection for all the sheets
1382 839
            $sheet->garbageCollect();
1383
        }
1384
    }
1385
1386
    /**
1387
     * Return the unique ID value assigned to this spreadsheet workbook.
1388
     *
1389
     * @return string
1390
     */
1391
    public function getID()
1392
    {
1393
        return $this->uniqueID;
1394
    }
1395
1396
    /**
1397
     * Get the visibility of the horizonal scroll bar in the application.
1398
     *
1399
     * @return bool True if horizonal scroll bar is visible
1400
     */
1401 304
    public function getShowHorizontalScroll()
1402
    {
1403 304
        return $this->showHorizontalScroll;
1404
    }
1405
1406
    /**
1407
     * Set the visibility of the horizonal scroll bar in the application.
1408
     *
1409
     * @param bool $showHorizontalScroll True if horizonal scroll bar is visible
1410
     */
1411 201
    public function setShowHorizontalScroll($showHorizontalScroll): void
1412
    {
1413 201
        $this->showHorizontalScroll = (bool) $showHorizontalScroll;
1414
    }
1415
1416
    /**
1417
     * Get the visibility of the vertical scroll bar in the application.
1418
     *
1419
     * @return bool True if vertical scroll bar is visible
1420
     */
1421 304
    public function getShowVerticalScroll()
1422
    {
1423 304
        return $this->showVerticalScroll;
1424
    }
1425
1426
    /**
1427
     * Set the visibility of the vertical scroll bar in the application.
1428
     *
1429
     * @param bool $showVerticalScroll True if vertical scroll bar is visible
1430
     */
1431 201
    public function setShowVerticalScroll($showVerticalScroll): void
1432
    {
1433 201
        $this->showVerticalScroll = (bool) $showVerticalScroll;
1434
    }
1435
1436
    /**
1437
     * Get the visibility of the sheet tabs in the application.
1438
     *
1439
     * @return bool True if the sheet tabs are visible
1440
     */
1441 304
    public function getShowSheetTabs()
1442
    {
1443 304
        return $this->showSheetTabs;
1444
    }
1445
1446
    /**
1447
     * Set the visibility of the sheet tabs  in the application.
1448
     *
1449
     * @param bool $showSheetTabs True if sheet tabs are visible
1450
     */
1451 201
    public function setShowSheetTabs($showSheetTabs): void
1452
    {
1453 201
        $this->showSheetTabs = (bool) $showSheetTabs;
1454
    }
1455
1456
    /**
1457
     * Return whether the workbook window is minimized.
1458
     *
1459
     * @return bool true if workbook window is minimized
1460
     */
1461 304
    public function getMinimized()
1462
    {
1463 304
        return $this->minimized;
1464
    }
1465
1466
    /**
1467
     * Set whether the workbook window is minimized.
1468
     *
1469
     * @param bool $minimized true if workbook window is minimized
1470
     */
1471 197
    public function setMinimized($minimized): void
1472
    {
1473 197
        $this->minimized = (bool) $minimized;
1474
    }
1475
1476
    /**
1477
     * Return whether to group dates when presenting the user with
1478
     * filtering optiomd in the user interface.
1479
     *
1480
     * @return bool true if workbook window is minimized
1481
     */
1482 304
    public function getAutoFilterDateGrouping()
1483
    {
1484 304
        return $this->autoFilterDateGrouping;
1485
    }
1486
1487
    /**
1488
     * Set whether to group dates when presenting the user with
1489
     * filtering optiomd in the user interface.
1490
     *
1491
     * @param bool $autoFilterDateGrouping true if workbook window is minimized
1492
     */
1493 197
    public function setAutoFilterDateGrouping($autoFilterDateGrouping): void
1494
    {
1495 197
        $this->autoFilterDateGrouping = (bool) $autoFilterDateGrouping;
1496
    }
1497
1498
    /**
1499
     * Return the first sheet in the book view.
1500
     *
1501
     * @return int First sheet in book view
1502
     */
1503 304
    public function getFirstSheetIndex()
1504
    {
1505 304
        return $this->firstSheetIndex;
1506
    }
1507
1508
    /**
1509
     * Set the first sheet in the book view.
1510
     *
1511
     * @param int $firstSheetIndex First sheet in book view
1512
     */
1513 203
    public function setFirstSheetIndex($firstSheetIndex): void
1514
    {
1515 203
        if ($firstSheetIndex >= 0) {
1516 202
            $this->firstSheetIndex = (int) $firstSheetIndex;
1517
        } else {
1518 1
            throw new Exception('First sheet index must be a positive integer.');
1519
        }
1520
    }
1521
1522
    /**
1523
     * Return the visibility status of the workbook.
1524
     *
1525
     * This may be one of the following three values:
1526
     * - visibile
1527
     *
1528
     * @return string Visible status
1529
     */
1530 305
    public function getVisibility()
1531
    {
1532 305
        return $this->visibility;
1533
    }
1534
1535
    /**
1536
     * Set the visibility status of the workbook.
1537
     *
1538
     * Valid values are:
1539
     *  - 'visible' (self::VISIBILITY_VISIBLE):
1540
     *       Workbook window is visible
1541
     *  - 'hidden' (self::VISIBILITY_HIDDEN):
1542
     *       Workbook window is hidden, but can be shown by the user
1543
     *       via the user interface
1544
     *  - 'veryHidden' (self::VISIBILITY_VERY_HIDDEN):
1545
     *       Workbook window is hidden and cannot be shown in the
1546
     *       user interface.
1547
     *
1548
     * @param null|string $visibility visibility status of the workbook
1549
     */
1550 198
    public function setVisibility($visibility): void
1551
    {
1552 198
        if ($visibility === null) {
1553 1
            $visibility = self::VISIBILITY_VISIBLE;
1554
        }
1555
1556 198
        if (in_array($visibility, self::WORKBOOK_VIEW_VISIBILITY_VALUES)) {
1557 198
            $this->visibility = $visibility;
1558
        } else {
1559 1
            throw new Exception('Invalid visibility value.');
1560
        }
1561
    }
1562
1563
    /**
1564
     * Get the ratio between the workbook tabs bar and the horizontal scroll bar.
1565
     * TabRatio is assumed to be out of 1000 of the horizontal window width.
1566
     *
1567
     * @return int Ratio between the workbook tabs bar and the horizontal scroll bar
1568
     */
1569 304
    public function getTabRatio()
1570
    {
1571 304
        return $this->tabRatio;
1572
    }
1573
1574
    /**
1575
     * Set the ratio between the workbook tabs bar and the horizontal scroll bar
1576
     * TabRatio is assumed to be out of 1000 of the horizontal window width.
1577
     *
1578
     * @param int $tabRatio Ratio between the tabs bar and the horizontal scroll bar
1579
     */
1580 205
    public function setTabRatio($tabRatio): void
1581
    {
1582 205
        if ($tabRatio >= 0 && $tabRatio <= 1000) {
1583 204
            $this->tabRatio = (int) $tabRatio;
1584
        } else {
1585 1
            throw new Exception('Tab ratio must be between 0 and 1000.');
1586
        }
1587
    }
1588
1589 2
    public function reevaluateAutoFilters(bool $resetToMax): void
1590
    {
1591 2
        foreach ($this->workSheetCollection as $sheet) {
1592 2
            $filter = $sheet->getAutoFilter();
1593 2
            if (!empty($filter->getRange())) {
1594 2
                if ($resetToMax) {
1595 1
                    $filter->setRangeToMaxRow();
1596
                }
1597 2
                $filter->showHideRows();
1598
            }
1599
        }
1600
    }
1601
1602
    /**
1603
     * @throws Exception
1604
     *
1605
     * @return mixed
1606
     */
1607
    public function __serialize()
1608
    {
1609
        throw new Exception('Spreadsheet objects cannot be serialized');
1610
    }
1611
1612
    /**
1613
     * @throws Exception
1614
     */
1615
    public function jsonSerialize(): mixed
1616
    {
1617 1
        throw new Exception('Spreadsheet objects cannot be json encoded');
1618
    }
1619 1
1620
    public function resetThemeFonts(): void
1621
    {
1622
        $majorFontLatin = $this->theme->getMajorFontLatin();
1623
        $minorFontLatin = $this->theme->getMinorFontLatin();
1624
        foreach ($this->cellXfCollection as $cellStyleXf) {
1625 1
            $scheme = $cellStyleXf->getFont()->getScheme();
1626
            if ($scheme === 'major') {
1627 1
                $cellStyleXf->getFont()->setName($majorFontLatin)->setScheme($scheme);
1628
            } elseif ($scheme === 'minor') {
1629
                $cellStyleXf->getFont()->setName($minorFontLatin)->setScheme($scheme);
1630 1
            }
1631
        }
1632 1
        foreach ($this->cellStyleXfCollection as $cellStyleXf) {
1633 1
            $scheme = $cellStyleXf->getFont()->getScheme();
1634 1
            if ($scheme === 'major') {
1635 1
                $cellStyleXf->getFont()->setName($majorFontLatin)->setScheme($scheme);
1636 1
            } elseif ($scheme === 'minor') {
1637 1
                $cellStyleXf->getFont()->setName($minorFontLatin)->setScheme($scheme);
1638 1
            }
1639 1
        }
1640
    }
1641
1642 1
    public function getTableByName(string $tableName): ?Table
1643 1
    {
1644 1
        $table = null;
1645
        foreach ($this->workSheetCollection as $sheet) {
1646 1
            $table = $sheet->getTableByName($tableName);
1 ignored issue
show
Bug introduced by
Are you sure the assignment to $table is correct as $sheet->getTableByName($tableName) targeting PhpOffice\PhpSpreadsheet...sheet::getTableByName() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1647 1
            if ($table !== null) {
1648
                break;
1649
            }
1650
        }
1651
1652 33
        return $table;
1653
    }
1654
}
1655