Failed Conditions
Push — master ( 02f37d...f95322 )
by Adrien
09:26
created

Spreadsheet::garbageCollect()   F

Complexity

Conditions 17
Paths 4056

Size

Total Lines 74
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 17.1819

Importance

Changes 0
Metric Value
cc 17
eloc 35
c 0
b 0
f 0
nc 4056
nop 0
dl 0
loc 74
ccs 32
cts 35
cp 0.9143
crap 17.1819
rs 1.0499

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
6
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
7
use PhpOffice\PhpSpreadsheet\Style\Style;
8
use PhpOffice\PhpSpreadsheet\Worksheet\Iterator;
9
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
10
11
class Spreadsheet
12
{
13
    // Allowable values for workbook window visilbity
14
    const VISIBILITY_VISIBLE = 'visible';
15
    const VISIBILITY_HIDDEN = 'hidden';
16
    const VISIBILITY_VERY_HIDDEN = 'veryHidden';
17
18
    private const DEFINED_NAME_IS_RANGE = false;
19
    private const DEFINED_NAME_IS_FORMULA = true;
20
21
    private static $workbookViewVisibilityValues = [
22
        self::VISIBILITY_VISIBLE,
23
        self::VISIBILITY_HIDDEN,
24
        self::VISIBILITY_VERY_HIDDEN,
25
    ];
26
27
    /**
28
     * Unique ID.
29
     *
30
     * @var string
31
     */
32
    private $uniqueID;
33
34
    /**
35
     * Document properties.
36
     *
37
     * @var Document\Properties
38
     */
39
    private $properties;
40
41
    /**
42
     * Document security.
43
     *
44
     * @var Document\Security
45
     */
46
    private $security;
47
48
    /**
49
     * Collection of Worksheet objects.
50
     *
51
     * @var Worksheet[]
52
     */
53
    private $workSheetCollection = [];
54
55
    /**
56
     * Calculation Engine.
57
     *
58
     * @var null|Calculation
59
     */
60
    private $calculationEngine;
61
62
    /**
63
     * Active sheet index.
64
     *
65
     * @var int
66
     */
67
    private $activeSheetIndex = 0;
68
69
    /**
70
     * Named ranges.
71
     *
72
     * @var DefinedName[]
73
     */
74
    private $definedNames = [];
75
76
    /**
77
     * CellXf supervisor.
78
     *
79
     * @var Style
80
     */
81
    private $cellXfSupervisor;
82
83
    /**
84
     * CellXf collection.
85
     *
86
     * @var Style[]
87
     */
88
    private $cellXfCollection = [];
89
90
    /**
91
     * CellStyleXf collection.
92
     *
93
     * @var Style[]
94
     */
95
    private $cellStyleXfCollection = [];
96
97
    /**
98
     * hasMacros : this workbook have macros ?
99
     *
100
     * @var bool
101
     */
102
    private $hasMacros = false;
103
104
    /**
105
     * macrosCode : all macros code as binary data (the vbaProject.bin file, this include form, code,  etc.), null if no macro.
106
     *
107
     * @var null|string
108
     */
109
    private $macrosCode;
110
111
    /**
112
     * macrosCertificate : if macros are signed, contains binary data vbaProjectSignature.bin file, null if not signed.
113
     *
114
     * @var null|string
115
     */
116
    private $macrosCertificate;
117
118
    /**
119
     * ribbonXMLData : null if workbook is'nt Excel 2007 or not contain a customized UI.
120
     *
121
     * @var null|array{target: string, data: string}
122
     */
123
    private $ribbonXMLData;
124
125
    /**
126
     * ribbonBinObjects : null if workbook is'nt Excel 2007 or not contain embedded objects (picture(s)) for Ribbon Elements
127
     * ignored if $ribbonXMLData is null.
128
     *
129
     * @var null|array
130
     */
131
    private $ribbonBinObjects;
132
133
    /**
134
     * List of unparsed loaded data for export to same format with better compatibility.
135
     * It has to be minimized when the library start to support currently unparsed data.
136
     *
137
     * @var array
138
     */
139
    private $unparsedLoadedData = [];
140
141
    /**
142
     * Controls visibility of the horizonal scroll bar in the application.
143
     *
144
     * @var bool
145
     */
146
    private $showHorizontalScroll = true;
147
148
    /**
149
     * Controls visibility of the horizonal scroll bar in the application.
150
     *
151
     * @var bool
152
     */
153
    private $showVerticalScroll = true;
154
155
    /**
156
     * Controls visibility of the sheet tabs in the application.
157
     *
158
     * @var bool
159
     */
160
    private $showSheetTabs = true;
161
162
    /**
163
     * Specifies a boolean value that indicates whether the workbook window
164
     * is minimized.
165
     *
166
     * @var bool
167
     */
168
    private $minimized = false;
169
170
    /**
171
     * Specifies a boolean value that indicates whether to group dates
172
     * when presenting the user with filtering optiomd in the user
173
     * interface.
174
     *
175
     * @var bool
176
     */
177
    private $autoFilterDateGrouping = true;
178
179
    /**
180
     * Specifies the index to the first sheet in the book view.
181
     *
182
     * @var int
183
     */
184
    private $firstSheetIndex = 0;
185
186
    /**
187
     * Specifies the visible status of the workbook.
188
     *
189
     * @var string
190
     */
191
    private $visibility = self::VISIBILITY_VISIBLE;
192
193
    /**
194
     * Specifies the ratio between the workbook tabs bar and the horizontal
195
     * scroll bar.  TabRatio is assumed to be out of 1000 of the horizontal
196
     * window width.
197
     *
198
     * @var int
199
     */
200
    private $tabRatio = 600;
201
202
    /**
203
     * The workbook has macros ?
204
     *
205
     * @return bool
206
     */
207 129
    public function hasMacros()
208
    {
209 129
        return $this->hasMacros;
210
    }
211
212
    /**
213
     * Define if a workbook has macros.
214
     *
215
     * @param bool $hasMacros true|false
216
     */
217 1
    public function setHasMacros($hasMacros): void
218
    {
219 1
        $this->hasMacros = (bool) $hasMacros;
220 1
    }
221
222
    /**
223
     * Set the macros code.
224
     *
225
     * @param string $macroCode string|null
226
     */
227 1
    public function setMacrosCode($macroCode): void
228
    {
229 1
        $this->macrosCode = $macroCode;
230 1
        $this->setHasMacros($macroCode !== null);
231 1
    }
232
233
    /**
234
     * Return the macros code.
235
     *
236
     * @return null|string
237
     */
238 1
    public function getMacrosCode()
239
    {
240 1
        return $this->macrosCode;
241
    }
242
243
    /**
244
     * Set the macros certificate.
245
     *
246
     * @param null|string $certificate
247
     */
248
    public function setMacrosCertificate($certificate): void
249
    {
250
        $this->macrosCertificate = $certificate;
251
    }
252
253
    /**
254
     * Is the project signed ?
255
     *
256
     * @return bool true|false
257
     */
258 1
    public function hasMacrosCertificate()
259
    {
260 1
        return $this->macrosCertificate !== null;
261
    }
262
263
    /**
264
     * Return the macros certificate.
265
     *
266
     * @return null|string
267
     */
268
    public function getMacrosCertificate()
269
    {
270
        return $this->macrosCertificate;
271
    }
272
273
    /**
274
     * Remove all macros, certificate from spreadsheet.
275
     */
276
    public function discardMacros(): void
277
    {
278
        $this->hasMacros = false;
279
        $this->macrosCode = null;
280
        $this->macrosCertificate = null;
281
    }
282
283
    /**
284
     * set ribbon XML data.
285
     *
286
     * @param null|mixed $target
287
     * @param null|mixed $xmlData
288
     */
289
    public function setRibbonXMLData($target, $xmlData): void
290
    {
291
        if ($target !== null && $xmlData !== null) {
292
            $this->ribbonXMLData = ['target' => $target, 'data' => $xmlData];
293
        } else {
294
            $this->ribbonXMLData = null;
295
        }
296
    }
297
298
    /**
299
     * retrieve ribbon XML Data.
300
     *
301
     * @param string $what
302
     *
303
     * @return null|array|string
304
     */
305
    public function getRibbonXMLData($what = 'all') //we need some constants here...
306
    {
307
        $returnData = null;
308
        $what = strtolower($what);
309
        switch ($what) {
310
            case 'all':
311
                $returnData = $this->ribbonXMLData;
312
313
                break;
314
            case 'target':
315
            case 'data':
316
                if (is_array($this->ribbonXMLData) && isset($this->ribbonXMLData[$what])) {
317
                    $returnData = $this->ribbonXMLData[$what];
318
                }
319
320
                break;
321
        }
322
323
        return $returnData;
324
    }
325
326
    /**
327
     * store binaries ribbon objects (pictures).
328
     *
329
     * @param null|mixed $BinObjectsNames
330
     * @param null|mixed $BinObjectsData
331
     */
332
    public function setRibbonBinObjects($BinObjectsNames, $BinObjectsData): void
333
    {
334
        if ($BinObjectsNames !== null && $BinObjectsData !== null) {
335
            $this->ribbonBinObjects = ['names' => $BinObjectsNames, 'data' => $BinObjectsData];
336
        } else {
337
            $this->ribbonBinObjects = null;
338
        }
339
    }
340
341
    /**
342
     * List of unparsed loaded data for export to same format with better compatibility.
343
     * It has to be minimized when the library start to support currently unparsed data.
344
     *
345
     * @internal
346
     *
347
     * @return array
348
     */
349 129
    public function getUnparsedLoadedData()
350
    {
351 129
        return $this->unparsedLoadedData;
352
    }
353
354
    /**
355
     * List of unparsed loaded data for export to same format with better compatibility.
356
     * It has to be minimized when the library start to support currently unparsed data.
357
     *
358
     * @internal
359
     */
360 124
    public function setUnparsedLoadedData(array $unparsedLoadedData): void
361
    {
362 124
        $this->unparsedLoadedData = $unparsedLoadedData;
363 124
    }
364
365
    /**
366
     * return the extension of a filename. Internal use for a array_map callback (php<5.3 don't like lambda function).
367
     *
368
     * @param mixed $path
369
     *
370
     * @return string
371
     */
372
    private function getExtensionOnly($path)
373
    {
374
        $extension = pathinfo($path, PATHINFO_EXTENSION);
375
376
        return is_array($extension) ? '' : $extension;
377
    }
378
379
    /**
380
     * retrieve Binaries Ribbon Objects.
381
     *
382
     * @param string $what
383
     *
384
     * @return null|array
385
     */
386
    public function getRibbonBinObjects($what = 'all')
387
    {
388
        $ReturnData = null;
389
        $what = strtolower($what);
390
        switch ($what) {
391
            case 'all':
392
                return $this->ribbonBinObjects;
393
394
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
395
            case 'names':
396
            case 'data':
397
                if (is_array($this->ribbonBinObjects) && isset($this->ribbonBinObjects[$what])) {
398
                    $ReturnData = $this->ribbonBinObjects[$what];
399
                }
400
401
                break;
402
            case 'types':
403
                if (
404
                    is_array($this->ribbonBinObjects) &&
405
                    isset($this->ribbonBinObjects['data']) && is_array($this->ribbonBinObjects['data'])
406
                ) {
407
                    $tmpTypes = array_keys($this->ribbonBinObjects['data']);
408
                    $ReturnData = array_unique(array_map([$this, 'getExtensionOnly'], $tmpTypes));
409
                } else {
410
                    $ReturnData = []; // the caller want an array... not null if empty
411
                }
412
413
                break;
414
        }
415
416
        return $ReturnData;
417
    }
418
419
    /**
420
     * This workbook have a custom UI ?
421
     *
422
     * @return bool
423
     */
424 129
    public function hasRibbon()
425
    {
426 129
        return $this->ribbonXMLData !== null;
427
    }
428
429
    /**
430
     * This workbook have additionnal object for the ribbon ?
431
     *
432
     * @return bool
433
     */
434 129
    public function hasRibbonBinObjects()
435
    {
436 129
        return $this->ribbonBinObjects !== null;
437
    }
438
439
    /**
440
     * Check if a sheet with a specified code name already exists.
441
     *
442
     * @param string $pSheetCodeName Name of the worksheet to check
443
     *
444
     * @return bool
445
     */
446 3974
    public function sheetCodeNameExists($pSheetCodeName)
447
    {
448 3974
        return $this->getSheetByCodeName($pSheetCodeName) !== null;
449
    }
450
451
    /**
452
     * Get sheet by code name. Warning : sheet don't have always a code name !
453
     *
454
     * @param string $pName Sheet name
455
     *
456
     * @return null|Worksheet
457
     */
458 3974
    public function getSheetByCodeName($pName)
459
    {
460 3974
        $worksheetCount = count($this->workSheetCollection);
461 3974
        for ($i = 0; $i < $worksheetCount; ++$i) {
462 139
            if ($this->workSheetCollection[$i]->getCodeName() == $pName) {
463 92
                return $this->workSheetCollection[$i];
464
            }
465
        }
466
467 3974
        return null;
468
    }
469
470
    /**
471
     * Create a new PhpSpreadsheet with one Worksheet.
472
     */
473 3974
    public function __construct()
474
    {
475 3974
        $this->uniqueID = uniqid('', true);
476 3974
        $this->calculationEngine = new Calculation($this);
477
478
        // Initialise worksheet collection and add one worksheet
479 3974
        $this->workSheetCollection = [];
480 3974
        $this->workSheetCollection[] = new Worksheet($this);
481 3974
        $this->activeSheetIndex = 0;
482
483
        // Create document properties
484 3974
        $this->properties = new Document\Properties();
485
486
        // Create document security
487 3974
        $this->security = new Document\Security();
488
489
        // Set defined names
490 3974
        $this->definedNames = [];
491
492
        // Create the cellXf supervisor
493 3974
        $this->cellXfSupervisor = new Style(true);
494 3974
        $this->cellXfSupervisor->bindParent($this);
495
496
        // Create the default style
497 3974
        $this->addCellXf(new Style());
498 3974
        $this->addCellStyleXf(new Style());
499 3974
    }
500
501
    /**
502
     * Code to execute when this worksheet is unset().
503
     */
504 33
    public function __destruct()
505
    {
506 33
        $this->disconnectWorksheets();
507 33
        $this->calculationEngine = null;
508 33
    }
509
510
    /**
511
     * Disconnect all worksheets from this PhpSpreadsheet workbook object,
512
     * typically so that the PhpSpreadsheet object can be unset.
513
     */
514 2418
    public function disconnectWorksheets(): void
515
    {
516 2418
        foreach ($this->workSheetCollection as $worksheet) {
517 2418
            $worksheet->disconnectCells();
518 2418
            unset($worksheet);
519
        }
520 2418
        $this->workSheetCollection = [];
521 2418
    }
522
523
    /**
524
     * Return the calculation engine for this worksheet.
525
     *
526
     * @return null|Calculation
527
     */
528 3974
    public function getCalculationEngine()
529
    {
530 3974
        return $this->calculationEngine;
531
    }
532
533
    /**
534
     * Get properties.
535
     *
536
     * @return Document\Properties
537
     */
538 425
    public function getProperties()
539
    {
540 425
        return $this->properties;
541
    }
542
543
    /**
544
     * Set properties.
545
     */
546
    public function setProperties(Document\Properties $pValue): void
547
    {
548
        $this->properties = $pValue;
549
    }
550
551
    /**
552
     * Get security.
553
     *
554
     * @return Document\Security
555
     */
556 129
    public function getSecurity()
557
    {
558 129
        return $this->security;
559
    }
560
561
    /**
562
     * Set security.
563
     */
564
    public function setSecurity(Document\Security $pValue): void
565
    {
566
        $this->security = $pValue;
567
    }
568
569
    /**
570
     * Get active sheet.
571
     *
572
     * @return Worksheet
573
     */
574 3940
    public function getActiveSheet()
575
    {
576 3940
        return $this->getSheet($this->activeSheetIndex);
577
    }
578
579
    /**
580
     * Create sheet and add it to this workbook.
581
     *
582
     * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
583
     *
584
     * @return Worksheet
585
     */
586 256
    public function createSheet($sheetIndex = null)
587
    {
588 256
        $newSheet = new Worksheet($this);
589 256
        $this->addSheet($newSheet, $sheetIndex);
590
591 256
        return $newSheet;
592
    }
593
594
    /**
595
     * Check if a sheet with a specified name already exists.
596
     *
597
     * @param string $pSheetName Name of the worksheet to check
598
     *
599
     * @return bool
600
     */
601 3974
    public function sheetNameExists($pSheetName)
602
    {
603 3974
        return $this->getSheetByName($pSheetName) !== null;
604
    }
605
606
    /**
607
     * Add sheet.
608
     *
609
     * @param null|int $iSheetIndex Index where sheet should go (0,1,..., or null for last)
610
     *
611
     * @return Worksheet
612
     */
613 302
    public function addSheet(Worksheet $pSheet, $iSheetIndex = null)
614
    {
615 302
        if ($this->sheetNameExists($pSheet->getTitle())) {
616 1
            throw new Exception(
617 1
                "Workbook already contains a worksheet named '{$pSheet->getTitle()}'. Rename this worksheet first."
618
            );
619
        }
620
621 302
        if ($iSheetIndex === null) {
622 300
            if ($this->activeSheetIndex < 0) {
623 185
                $this->activeSheetIndex = 0;
624
            }
625 300
            $this->workSheetCollection[] = $pSheet;
626
        } else {
627
            // Insert the sheet at the requested index
628 5
            array_splice(
629 5
                $this->workSheetCollection,
630
                $iSheetIndex,
631 5
                0,
632 5
                [$pSheet]
633
            );
634
635
            // Adjust active sheet index if necessary
636 5
            if ($this->activeSheetIndex >= $iSheetIndex) {
637 1
                ++$this->activeSheetIndex;
638
            }
639
        }
640
641 302
        if ($pSheet->getParent() === null) {
642 45
            $pSheet->rebindParent($this);
643
        }
644
645 302
        return $pSheet;
646
    }
647
648
    /**
649
     * Remove sheet by index.
650
     *
651
     * @param int $pIndex Active sheet index
652
     */
653 192
    public function removeSheetByIndex($pIndex): void
654
    {
655 192
        $numSheets = count($this->workSheetCollection);
656 192
        if ($pIndex > $numSheets - 1) {
657 1
            throw new Exception(
658 1
                "You tried to remove a sheet by the out of bounds index: {$pIndex}. The actual number of sheets is {$numSheets}."
659
            );
660
        }
661 191
        array_splice($this->workSheetCollection, $pIndex, 1);
662
663
        // Adjust active sheet index if necessary
664
        if (
665 191
            ($this->activeSheetIndex >= $pIndex) &&
666 191
            ($this->activeSheetIndex > 0 || $numSheets <= 1)
667
        ) {
668 189
            --$this->activeSheetIndex;
669
        }
670 191
    }
671
672
    /**
673
     * Get sheet by index.
674
     *
675
     * @param int $pIndex Sheet index
676
     *
677
     * @return Worksheet
678
     */
679 3956
    public function getSheet($pIndex)
680
    {
681 3956
        if (!isset($this->workSheetCollection[$pIndex])) {
682 1
            $numSheets = $this->getSheetCount();
683
684 1
            throw new Exception(
685 1
                "Your requested sheet index: {$pIndex} is out of bounds. The actual number of sheets is {$numSheets}."
686
            );
687
        }
688
689 3956
        return $this->workSheetCollection[$pIndex];
690
    }
691
692
    /**
693
     * Get all sheets.
694
     *
695
     * @return Worksheet[]
696
     */
697 75
    public function getAllSheets()
698
    {
699 75
        return $this->workSheetCollection;
700
    }
701
702
    /**
703
     * Get sheet by name.
704
     *
705
     * @param string $pName Sheet name
706
     *
707
     * @return null|Worksheet
708
     */
709 3974
    public function getSheetByName($pName)
710
    {
711 3974
        $worksheetCount = count($this->workSheetCollection);
712 3974
        for ($i = 0; $i < $worksheetCount; ++$i) {
713 3634
            if ($this->workSheetCollection[$i]->getTitle() === trim($pName, "'")) {
714 3453
                return $this->workSheetCollection[$i];
715
            }
716
        }
717
718 3974
        return null;
719
    }
720
721
    /**
722
     * Get index for sheet.
723
     *
724
     * @return int index
725
     */
726 3720
    public function getIndex(Worksheet $pSheet)
727
    {
728 3720
        foreach ($this->workSheetCollection as $key => $value) {
729 3720
            if ($value->getHashCode() === $pSheet->getHashCode()) {
730 3719
                return $key;
731
            }
732
        }
733
734 1
        throw new Exception('Sheet does not exist.');
735
    }
736
737
    /**
738
     * Set index for sheet by sheet name.
739
     *
740
     * @param string $sheetName Sheet name to modify index for
741
     * @param int $newIndex New index for the sheet
742
     *
743
     * @return int New sheet index
744
     */
745 1
    public function setIndexByName($sheetName, $newIndex)
746
    {
747 1
        $oldIndex = $this->getIndex($this->getSheetByName($sheetName));
1 ignored issue
show
Bug introduced by
It seems like $this->getSheetByName($sheetName) can also be of type null; however, parameter $pSheet of PhpOffice\PhpSpreadsheet\Spreadsheet::getIndex() does only seem to accept PhpOffice\PhpSpreadsheet\Worksheet\Worksheet, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

747
        $oldIndex = $this->getIndex(/** @scrutinizer ignore-type */ $this->getSheetByName($sheetName));
Loading history...
748 1
        $pSheet = array_splice(
749 1
            $this->workSheetCollection,
750
            $oldIndex,
751 1
            1
752
        );
753 1
        array_splice(
754 1
            $this->workSheetCollection,
755
            $newIndex,
756 1
            0,
757
            $pSheet
758
        );
759
760 1
        return $newIndex;
761
    }
762
763
    /**
764
     * Get sheet count.
765
     *
766
     * @return int
767
     */
768 483
    public function getSheetCount()
769
    {
770 483
        return count($this->workSheetCollection);
771
    }
772
773
    /**
774
     * Get active sheet index.
775
     *
776
     * @return int Active sheet index
777
     */
778 3465
    public function getActiveSheetIndex()
779
    {
780 3465
        return $this->activeSheetIndex;
781
    }
782
783
    /**
784
     * Set active sheet index.
785
     *
786
     * @param int $pIndex Active sheet index
787
     *
788
     * @return Worksheet
789
     */
790 3799
    public function setActiveSheetIndex($pIndex)
791
    {
792 3799
        $numSheets = count($this->workSheetCollection);
793
794 3799
        if ($pIndex > $numSheets - 1) {
795 1
            throw new Exception(
796 1
                "You tried to set a sheet active by the out of bounds index: {$pIndex}. The actual number of sheets is {$numSheets}."
797
            );
798
        }
799 3798
        $this->activeSheetIndex = $pIndex;
800
801 3798
        return $this->getActiveSheet();
802
    }
803
804
    /**
805
     * Set active sheet index by name.
806
     *
807
     * @param string $pValue Sheet title
808
     *
809
     * @return Worksheet
810
     */
811 27
    public function setActiveSheetIndexByName($pValue)
812
    {
813 27
        if (($worksheet = $this->getSheetByName($pValue)) instanceof Worksheet) {
814 25
            $this->setActiveSheetIndex($this->getIndex($worksheet));
815
816 25
            return $worksheet;
817
        }
818
819 2
        throw new Exception('Workbook does not contain sheet:' . $pValue);
820
    }
821
822
    /**
823
     * Get sheet names.
824
     *
825
     * @return string[]
826
     */
827 9
    public function getSheetNames()
828
    {
829 9
        $returnValue = [];
830 9
        $worksheetCount = $this->getSheetCount();
831 9
        for ($i = 0; $i < $worksheetCount; ++$i) {
832 9
            $returnValue[] = $this->getSheet($i)->getTitle();
833
        }
834
835 9
        return $returnValue;
836
    }
837
838
    /**
839
     * Add external sheet.
840
     *
841
     * @param Worksheet $pSheet External sheet to add
842
     * @param null|int $iSheetIndex Index where sheet should go (0,1,..., or null for last)
843
     *
844
     * @return Worksheet
845
     */
846 3
    public function addExternalSheet(Worksheet $pSheet, $iSheetIndex = null)
847
    {
848 3
        if ($this->sheetNameExists($pSheet->getTitle())) {
849 1
            throw new Exception("Workbook already contains a worksheet named '{$pSheet->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 2
        $countCellXfs = count($this->cellXfCollection);
854
855
        // copy all the shared cellXfs from the external workbook and append them to the current
856 2
        foreach ($pSheet->getParent()->getCellXfCollection() as $cellXf) {
857 2
            $this->addCellXf(clone $cellXf);
858
        }
859
860
        // move sheet to this workbook
861 2
        $pSheet->rebindParent($this);
862
863
        // update the cellXfs
864 2
        foreach ($pSheet->getCoordinates(false) as $coordinate) {
865 2
            $cell = $pSheet->getCell($coordinate);
866 2
            $cell->setXfIndex($cell->getXfIndex() + $countCellXfs);
867
        }
868
869 2
        return $this->addSheet($pSheet, $iSheetIndex);
870
    }
871
872
    /**
873
     * Get an array of all Named Ranges.
874
     *
875
     * @return DefinedName[]
876
     */
877 8
    public function getNamedRanges(): array
878
    {
879 8
        return array_filter(
880 8
            $this->definedNames,
881
            function (DefinedName $definedName) {
882 8
                return $definedName->isFormula() === self::DEFINED_NAME_IS_RANGE;
883 8
            }
884
        );
885
    }
886
887
    /**
888
     * Get an array of all Named Formulae.
889
     *
890
     * @return DefinedName[]
891
     */
892 8
    public function getNamedFormulae(): array
893
    {
894 8
        return array_filter(
895 8
            $this->definedNames,
896
            function (DefinedName $definedName) {
897 8
                return $definedName->isFormula() === self::DEFINED_NAME_IS_FORMULA;
898 8
            }
899
        );
900
    }
901
902
    /**
903
     * Get an array of all Defined Names (both named ranges and named formulae).
904
     *
905
     * @return DefinedName[]
906
     */
907 186
    public function getDefinedNames(): array
908
    {
909 186
        return $this->definedNames;
910
    }
911
912
    /**
913
     * Add a named range.
914
     * If a named range with this name already exists, then this will replace the existing value.
915
     */
916 55
    public function addNamedRange(NamedRange $namedRange): void
917
    {
918 55
        $this->addDefinedName($namedRange);
919 55
    }
920
921
    /**
922
     * Add a named formula.
923
     * If a named formula with this name already exists, then this will replace the existing value.
924
     */
925 9
    public function addNamedFormula(NamedFormula $namedFormula): void
926
    {
927 9
        $this->addDefinedName($namedFormula);
928 9
    }
929
930
    /**
931
     * Add a defined name (either a named range or a named formula).
932
     * If a defined named with this name already exists, then this will replace the existing value.
933
     */
934 133
    public function addDefinedName(DefinedName $definedName): void
935
    {
936 133
        $upperCaseName = StringHelper::strToUpper($definedName->getName());
937 133
        if ($definedName->getScope() == null) {
938
            // global scope
939 128
            $this->definedNames[$upperCaseName] = $definedName;
940
        } else {
941
            // local scope
942 30
            $this->definedNames[$definedName->getScope()->getTitle() . '!' . $upperCaseName] = $definedName;
943
        }
944 133
    }
945
946
    /**
947
     * Get named range.
948
     *
949
     * @param null|Worksheet $pSheet Scope. Use null for global scope
950
     */
951 9
    public function getNamedRange(string $namedRange, ?Worksheet $pSheet = null): ?NamedRange
952
    {
953 9
        $returnValue = null;
954
955 9
        if ($namedRange !== '') {
956 9
            $namedRange = StringHelper::strToUpper($namedRange);
957
            // first look for global named range
958 9
            $returnValue = $this->getGlobalDefinedNameByType($namedRange, self::DEFINED_NAME_IS_RANGE);
959
            // then look for local named range (has priority over global named range if both names exist)
960 9
            $returnValue = $this->getLocalDefinedNameByType($namedRange, self::DEFINED_NAME_IS_RANGE, $pSheet) ?: $returnValue;
961
        }
962
963 9
        return $returnValue instanceof NamedRange ? $returnValue : null;
964
    }
965
966
    /**
967
     * Get named formula.
968
     *
969
     * @param null|Worksheet $pSheet Scope. Use null for global scope
970
     */
971 7
    public function getNamedFormula(string $namedFormula, ?Worksheet $pSheet = null): ?NamedFormula
972
    {
973 7
        $returnValue = null;
974
975 7
        if ($namedFormula !== '') {
976 7
            $namedFormula = StringHelper::strToUpper($namedFormula);
977
            // first look for global named formula
978 7
            $returnValue = $this->getGlobalDefinedNameByType($namedFormula, self::DEFINED_NAME_IS_FORMULA);
979
            // then look for local named formula (has priority over global named formula if both names exist)
980 7
            $returnValue = $this->getLocalDefinedNameByType($namedFormula, self::DEFINED_NAME_IS_FORMULA, $pSheet) ?: $returnValue;
981
        }
982
983 7
        return $returnValue instanceof NamedFormula ? $returnValue : null;
984
    }
985
986 16
    private function getGlobalDefinedNameByType(string $name, bool $type): ?DefinedName
987
    {
988 16
        if (isset($this->definedNames[$name]) && $this->definedNames[$name]->isFormula() === $type) {
989 16
            return $this->definedNames[$name];
990
        }
991
992 2
        return null;
993
    }
994
995 16
    private function getLocalDefinedNameByType(string $name, bool $type, ?Worksheet $pSheet = null): ?DefinedName
996
    {
997
        if (
998 16
            ($pSheet !== null) && isset($this->definedNames[$pSheet->getTitle() . '!' . $name])
999 16
            && $this->definedNames[$pSheet->getTitle() . '!' . $name]->isFormula() === $type
1000
        ) {
1001 8
            return $this->definedNames[$pSheet->getTitle() . '!' . $name];
1002
        }
1003
1004 14
        return null;
1005
    }
1006
1007
    /**
1008
     * Get named range.
1009
     *
1010
     * @param null|Worksheet $pSheet Scope. Use null for global scope
1011
     */
1012 97
    public function getDefinedName(string $definedName, ?Worksheet $pSheet = null): ?DefinedName
1013
    {
1014 97
        $returnValue = null;
1015
1016 97
        if ($definedName !== '') {
1017 97
            $definedName = StringHelper::strToUpper($definedName);
1018
            // first look for global defined name
1019 97
            if (isset($this->definedNames[$definedName])) {
1020 68
                $returnValue = $this->definedNames[$definedName];
1021
            }
1022
1023
            // then look for local defined name (has priority over global defined name if both names exist)
1024 97
            if (($pSheet !== null) && isset($this->definedNames[$pSheet->getTitle() . '!' . $definedName])) {
1025 7
                $returnValue = $this->definedNames[$pSheet->getTitle() . '!' . $definedName];
1026
            }
1027
        }
1028
1029 97
        return $returnValue;
1030
    }
1031
1032
    /**
1033
     * Remove named range.
1034
     *
1035
     * @param null|Worksheet $pSheet scope: use null for global scope
1036
     *
1037
     * @return $this
1038
     */
1039 4
    public function removeNamedRange(string $namedRange, ?Worksheet $pSheet = null): self
1040
    {
1041 4
        if ($this->getNamedRange($namedRange, $pSheet) === null) {
1042
            return $this;
1043
        }
1044
1045 4
        return $this->removeDefinedName($namedRange, $pSheet);
1046
    }
1047
1048
    /**
1049
     * Remove named formula.
1050
     *
1051
     * @param null|Worksheet $pSheet scope: use null for global scope
1052
     *
1053
     * @return $this
1054
     */
1055 3
    public function removeNamedFormula(string $namedFormula, ?Worksheet $pSheet = null): self
1056
    {
1057 3
        if ($this->getNamedFormula($namedFormula, $pSheet) === null) {
1058
            return $this;
1059
        }
1060
1061 3
        return $this->removeDefinedName($namedFormula, $pSheet);
1062
    }
1063
1064
    /**
1065
     * Remove defined name.
1066
     *
1067
     * @param null|Worksheet $pSheet scope: use null for global scope
1068
     *
1069
     * @return $this
1070
     */
1071 10
    public function removeDefinedName(string $definedName, ?Worksheet $pSheet = null): self
1072
    {
1073 10
        $definedName = StringHelper::strToUpper($definedName);
1074
1075 10
        if ($pSheet === null) {
1076
            if (isset($this->definedNames[$definedName])) {
1077
                unset($this->definedNames[$definedName]);
1078
            }
1079
        } else {
1080 10
            if (isset($this->definedNames[$pSheet->getTitle() . '!' . $definedName])) {
1081 3
                unset($this->definedNames[$pSheet->getTitle() . '!' . $definedName]);
1082 7
            } elseif (isset($this->definedNames[$definedName])) {
1083 7
                unset($this->definedNames[$definedName]);
1084
            }
1085
        }
1086
1087 10
        return $this;
1088
    }
1089
1090
    /**
1091
     * Get worksheet iterator.
1092
     *
1093
     * @return Iterator
1094
     */
1095 392
    public function getWorksheetIterator()
1096
    {
1097 392
        return new Iterator($this);
1098
    }
1099
1100
    /**
1101
     * Copy workbook (!= clone!).
1102
     *
1103
     * @return Spreadsheet
1104
     */
1105
    public function copy()
1106
    {
1107
        $copied = clone $this;
1108
1109
        $worksheetCount = count($this->workSheetCollection);
1110
        for ($i = 0; $i < $worksheetCount; ++$i) {
1111
            $this->workSheetCollection[$i] = $this->workSheetCollection[$i]->copy();
1112
            $this->workSheetCollection[$i]->rebindParent($this);
1113
        }
1114
1115
        return $copied;
1116
    }
1117
1118
    /**
1119
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
1120
     */
1121
    public function __clone()
1122
    {
1123
        // @phpstan-ignore-next-line
1124
        foreach ($this as $key => $val) {
1125
            if (is_object($val) || (is_array($val))) {
1126
                $this->{$key} = unserialize(serialize($val));
1127
            }
1128
        }
1129
    }
1130
1131
    /**
1132
     * Get the workbook collection of cellXfs.
1133
     *
1134
     * @return Style[]
1135
     */
1136 334
    public function getCellXfCollection()
1137
    {
1138 334
        return $this->cellXfCollection;
1139
    }
1140
1141
    /**
1142
     * Get cellXf by index.
1143
     *
1144
     * @param int $pIndex
1145
     *
1146
     * @return Style
1147
     */
1148 3653
    public function getCellXfByIndex($pIndex)
1149
    {
1150 3653
        return $this->cellXfCollection[$pIndex];
1151
    }
1152
1153
    /**
1154
     * Get cellXf by hash code.
1155
     *
1156
     * @param string $pValue
1157
     *
1158
     * @return false|Style
1159
     */
1160 249
    public function getCellXfByHashCode($pValue)
1161
    {
1162 249
        foreach ($this->cellXfCollection as $cellXf) {
1163 249
            if ($cellXf->getHashCode() === $pValue) {
1164 102
                return $cellXf;
1165
            }
1166
        }
1167
1168 234
        return false;
1169
    }
1170
1171
    /**
1172
     * Check if style exists in style collection.
1173
     *
1174
     * @param Style $pCellStyle
1175
     *
1176
     * @return bool
1177
     */
1178
    public function cellXfExists($pCellStyle)
1179
    {
1180
        return in_array($pCellStyle, $this->cellXfCollection, true);
1181
    }
1182
1183
    /**
1184
     * Get default style.
1185
     *
1186
     * @return Style
1187
     */
1188 321
    public function getDefaultStyle()
1189
    {
1190 321
        if (isset($this->cellXfCollection[0])) {
1191 321
            return $this->cellXfCollection[0];
1192
        }
1193
1194
        throw new Exception('No default style found for this workbook');
1195
    }
1196
1197
    /**
1198
     * Add a cellXf to the workbook.
1199
     */
1200 3974
    public function addCellXf(Style $style): void
1201
    {
1202 3974
        $this->cellXfCollection[] = $style;
1203 3974
        $style->setIndex(count($this->cellXfCollection) - 1);
1204 3974
    }
1205
1206
    /**
1207
     * Remove cellXf by index. It is ensured that all cells get their xf index updated.
1208
     *
1209
     * @param int $pIndex Index to cellXf
1210
     */
1211 160
    public function removeCellXfByIndex($pIndex): void
1212
    {
1213 160
        if ($pIndex > count($this->cellXfCollection) - 1) {
1214
            throw new Exception('CellXf index is out of bounds.');
1215
        }
1216
1217
        // first remove the cellXf
1218 160
        array_splice($this->cellXfCollection, $pIndex, 1);
1219
1220
        // then update cellXf indexes for cells
1221 160
        foreach ($this->workSheetCollection as $worksheet) {
1222
            foreach ($worksheet->getCoordinates(false) as $coordinate) {
1223
                $cell = $worksheet->getCell($coordinate);
1224
                $xfIndex = $cell->getXfIndex();
1225
                if ($xfIndex > $pIndex) {
1226
                    // decrease xf index by 1
1227
                    $cell->setXfIndex($xfIndex - 1);
1228
                } elseif ($xfIndex == $pIndex) {
1229
                    // set to default xf index 0
1230
                    $cell->setXfIndex(0);
1231
                }
1232
            }
1233
        }
1234 160
    }
1235
1236
    /**
1237
     * Get the cellXf supervisor.
1238
     *
1239
     * @return Style
1240
     */
1241 3599
    public function getCellXfSupervisor()
1242
    {
1243 3599
        return $this->cellXfSupervisor;
1244
    }
1245
1246
    /**
1247
     * Get the workbook collection of cellStyleXfs.
1248
     *
1249
     * @return Style[]
1250
     */
1251 55
    public function getCellStyleXfCollection()
1252
    {
1253 55
        return $this->cellStyleXfCollection;
1254
    }
1255
1256
    /**
1257
     * Get cellStyleXf by index.
1258
     *
1259
     * @param int $pIndex Index to cellXf
1260
     *
1261
     * @return Style
1262
     */
1263
    public function getCellStyleXfByIndex($pIndex)
1264
    {
1265
        return $this->cellStyleXfCollection[$pIndex];
1266
    }
1267
1268
    /**
1269
     * Get cellStyleXf by hash code.
1270
     *
1271
     * @param string $pValue
1272
     *
1273
     * @return false|Style
1274
     */
1275
    public function getCellStyleXfByHashCode($pValue)
1276
    {
1277
        foreach ($this->cellStyleXfCollection as $cellStyleXf) {
1278
            if ($cellStyleXf->getHashCode() === $pValue) {
1279
                return $cellStyleXf;
1280
            }
1281
        }
1282
1283
        return false;
1284
    }
1285
1286
    /**
1287
     * Add a cellStyleXf to the workbook.
1288
     */
1289 3974
    public function addCellStyleXf(Style $pStyle): void
1290
    {
1291 3974
        $this->cellStyleXfCollection[] = $pStyle;
1292 3974
        $pStyle->setIndex(count($this->cellStyleXfCollection) - 1);
1293 3974
    }
1294
1295
    /**
1296
     * Remove cellStyleXf by index.
1297
     *
1298
     * @param int $pIndex Index to cellXf
1299
     */
1300 160
    public function removeCellStyleXfByIndex($pIndex): void
1301
    {
1302 160
        if ($pIndex > count($this->cellStyleXfCollection) - 1) {
1303
            throw new Exception('CellStyleXf index is out of bounds.');
1304
        }
1305 160
        array_splice($this->cellStyleXfCollection, $pIndex, 1);
1306 160
    }
1307
1308
    /**
1309
     * Eliminate all unneeded cellXf and afterwards update the xfIndex for all cells
1310
     * and columns in the workbook.
1311
     */
1312 314
    public function garbageCollect(): void
1313
    {
1314
        // how many references are there to each cellXf ?
1315 314
        $countReferencesCellXf = [];
1316 314
        foreach ($this->cellXfCollection as $index => $cellXf) {
1317 314
            $countReferencesCellXf[$index] = 0;
1318
        }
1319
1320 314
        foreach ($this->getWorksheetIterator() as $sheet) {
1321
            // from cells
1322 314
            foreach ($sheet->getCoordinates(false) as $coordinate) {
1323 304
                $cell = $sheet->getCell($coordinate);
1324 304
                ++$countReferencesCellXf[$cell->getXfIndex()];
1325
            }
1326
1327
            // from row dimensions
1328 314
            foreach ($sheet->getRowDimensions() as $rowDimension) {
1329 52
                if ($rowDimension->getXfIndex() !== null) {
1330
                    ++$countReferencesCellXf[$rowDimension->getXfIndex()];
1331
                }
1332
            }
1333
1334
            // from column dimensions
1335 314
            foreach ($sheet->getColumnDimensions() as $columnDimension) {
1336 42
                ++$countReferencesCellXf[$columnDimension->getXfIndex()];
1337
            }
1338
        }
1339
1340
        // remove cellXfs without references and create mapping so we can update xfIndex
1341
        // for all cells and columns
1342 314
        $countNeededCellXfs = 0;
1343 314
        $map = [];
1344 314
        foreach ($this->cellXfCollection as $index => $cellXf) {
1345 314
            if ($countReferencesCellXf[$index] > 0 || $index == 0) { // we must never remove the first cellXf
1346 314
                ++$countNeededCellXfs;
1347
            } else {
1348 20
                unset($this->cellXfCollection[$index]);
1349
            }
1350 314
            $map[$index] = $countNeededCellXfs - 1;
1351
        }
1352 314
        $this->cellXfCollection = array_values($this->cellXfCollection);
1353
1354
        // update the index for all cellXfs
1355 314
        foreach ($this->cellXfCollection as $i => $cellXf) {
1356 314
            $cellXf->setIndex($i);
1357
        }
1358
1359
        // make sure there is always at least one cellXf (there should be)
1360 314
        if (empty($this->cellXfCollection)) {
1361
            $this->cellXfCollection[] = new Style();
1362
        }
1363
1364
        // update the xfIndex for all cells, row dimensions, column dimensions
1365 314
        foreach ($this->getWorksheetIterator() as $sheet) {
1366
            // for all cells
1367 314
            foreach ($sheet->getCoordinates(false) as $coordinate) {
1368 304
                $cell = $sheet->getCell($coordinate);
1369 304
                $cell->setXfIndex($map[$cell->getXfIndex()]);
1370
            }
1371
1372
            // for all row dimensions
1373 314
            foreach ($sheet->getRowDimensions() as $rowDimension) {
1374 52
                if ($rowDimension->getXfIndex() !== null) {
1375
                    $rowDimension->setXfIndex($map[$rowDimension->getXfIndex()]);
1376
                }
1377
            }
1378
1379
            // for all column dimensions
1380 314
            foreach ($sheet->getColumnDimensions() as $columnDimension) {
1381 42
                $columnDimension->setXfIndex($map[$columnDimension->getXfIndex()]);
1382
            }
1383
1384
            // also do garbage collection for all the sheets
1385 314
            $sheet->garbageCollect();
1386
        }
1387 314
    }
1388
1389
    /**
1390
     * Return the unique ID value assigned to this spreadsheet workbook.
1391
     *
1392
     * @return string
1393
     */
1394
    public function getID()
1395
    {
1396
        return $this->uniqueID;
1397
    }
1398
1399
    /**
1400
     * Get the visibility of the horizonal scroll bar in the application.
1401
     *
1402
     * @return bool True if horizonal scroll bar is visible
1403
     */
1404 129
    public function getShowHorizontalScroll()
1405
    {
1406 129
        return $this->showHorizontalScroll;
1407
    }
1408
1409
    /**
1410
     * Set the visibility of the horizonal scroll bar in the application.
1411
     *
1412
     * @param bool $showHorizontalScroll True if horizonal scroll bar is visible
1413
     */
1414 62
    public function setShowHorizontalScroll($showHorizontalScroll): void
1415
    {
1416 62
        $this->showHorizontalScroll = (bool) $showHorizontalScroll;
1417 62
    }
1418
1419
    /**
1420
     * Get the visibility of the vertical scroll bar in the application.
1421
     *
1422
     * @return bool True if vertical scroll bar is visible
1423
     */
1424 129
    public function getShowVerticalScroll()
1425
    {
1426 129
        return $this->showVerticalScroll;
1427
    }
1428
1429
    /**
1430
     * Set the visibility of the vertical scroll bar in the application.
1431
     *
1432
     * @param bool $showVerticalScroll True if vertical scroll bar is visible
1433
     */
1434 62
    public function setShowVerticalScroll($showVerticalScroll): void
1435
    {
1436 62
        $this->showVerticalScroll = (bool) $showVerticalScroll;
1437 62
    }
1438
1439
    /**
1440
     * Get the visibility of the sheet tabs in the application.
1441
     *
1442
     * @return bool True if the sheet tabs are visible
1443
     */
1444 129
    public function getShowSheetTabs()
1445
    {
1446 129
        return $this->showSheetTabs;
1447
    }
1448
1449
    /**
1450
     * Set the visibility of the sheet tabs  in the application.
1451
     *
1452
     * @param bool $showSheetTabs True if sheet tabs are visible
1453
     */
1454 62
    public function setShowSheetTabs($showSheetTabs): void
1455
    {
1456 62
        $this->showSheetTabs = (bool) $showSheetTabs;
1457 62
    }
1458
1459
    /**
1460
     * Return whether the workbook window is minimized.
1461
     *
1462
     * @return bool true if workbook window is minimized
1463
     */
1464 129
    public function getMinimized()
1465
    {
1466 129
        return $this->minimized;
1467
    }
1468
1469
    /**
1470
     * Set whether the workbook window is minimized.
1471
     *
1472
     * @param bool $minimized true if workbook window is minimized
1473
     */
1474 62
    public function setMinimized($minimized): void
1475
    {
1476 62
        $this->minimized = (bool) $minimized;
1477 62
    }
1478
1479
    /**
1480
     * Return whether to group dates when presenting the user with
1481
     * filtering optiomd in the user interface.
1482
     *
1483
     * @return bool true if workbook window is minimized
1484
     */
1485 129
    public function getAutoFilterDateGrouping()
1486
    {
1487 129
        return $this->autoFilterDateGrouping;
1488
    }
1489
1490
    /**
1491
     * Set whether to group dates when presenting the user with
1492
     * filtering optiomd in the user interface.
1493
     *
1494
     * @param bool $autoFilterDateGrouping true if workbook window is minimized
1495
     */
1496 62
    public function setAutoFilterDateGrouping($autoFilterDateGrouping): void
1497
    {
1498 62
        $this->autoFilterDateGrouping = (bool) $autoFilterDateGrouping;
1499 62
    }
1500
1501
    /**
1502
     * Return the first sheet in the book view.
1503
     *
1504
     * @return int First sheet in book view
1505
     */
1506 129
    public function getFirstSheetIndex()
1507
    {
1508 129
        return $this->firstSheetIndex;
1509
    }
1510
1511
    /**
1512
     * Set the first sheet in the book view.
1513
     *
1514
     * @param int $firstSheetIndex First sheet in book view
1515
     */
1516 62
    public function setFirstSheetIndex($firstSheetIndex): void
1517
    {
1518 62
        if ($firstSheetIndex >= 0) {
1519 62
            $this->firstSheetIndex = (int) $firstSheetIndex;
1520
        } else {
1521
            throw new Exception('First sheet index must be a positive integer.');
1522
        }
1523 62
    }
1524
1525
    /**
1526
     * Return the visibility status of the workbook.
1527
     *
1528
     * This may be one of the following three values:
1529
     * - visibile
1530
     *
1531
     * @return string Visible status
1532
     */
1533 129
    public function getVisibility()
1534
    {
1535 129
        return $this->visibility;
1536
    }
1537
1538
    /**
1539
     * Set the visibility status of the workbook.
1540
     *
1541
     * Valid values are:
1542
     *  - 'visible' (self::VISIBILITY_VISIBLE):
1543
     *       Workbook window is visible
1544
     *  - 'hidden' (self::VISIBILITY_HIDDEN):
1545
     *       Workbook window is hidden, but can be shown by the user
1546
     *       via the user interface
1547
     *  - 'veryHidden' (self::VISIBILITY_VERY_HIDDEN):
1548
     *       Workbook window is hidden and cannot be shown in the
1549
     *       user interface.
1550
     *
1551
     * @param string $visibility visibility status of the workbook
1552
     */
1553 62
    public function setVisibility($visibility): void
1554
    {
1555 62
        if ($visibility === null) {
0 ignored issues
show
introduced by
The condition $visibility === null is always false.
Loading history...
1556
            $visibility = self::VISIBILITY_VISIBLE;
1557
        }
1558
1559 62
        if (in_array($visibility, self::$workbookViewVisibilityValues)) {
1560 62
            $this->visibility = $visibility;
1561
        } else {
1562
            throw new Exception('Invalid visibility value.');
1563
        }
1564 62
    }
1565
1566
    /**
1567
     * Get the ratio between the workbook tabs bar and the horizontal scroll bar.
1568
     * TabRatio is assumed to be out of 1000 of the horizontal window width.
1569
     *
1570
     * @return int Ratio between the workbook tabs bar and the horizontal scroll bar
1571
     */
1572 129
    public function getTabRatio()
1573
    {
1574 129
        return $this->tabRatio;
1575
    }
1576
1577
    /**
1578
     * Set the ratio between the workbook tabs bar and the horizontal scroll bar
1579
     * TabRatio is assumed to be out of 1000 of the horizontal window width.
1580
     *
1581
     * @param int $tabRatio Ratio between the tabs bar and the horizontal scroll bar
1582
     */
1583 62
    public function setTabRatio($tabRatio): void
1584
    {
1585 62
        if ($tabRatio >= 0 || $tabRatio <= 1000) {
1586 62
            $this->tabRatio = (int) $tabRatio;
1587
        } else {
1588
            throw new Exception('Tab ratio must be between 0 and 1000.');
1589
        }
1590 62
    }
1591
}
1592