Passed
Push — master ( 10b137...2bffcf )
by Adrien
10:25
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 160
    public function hasMacros()
208
    {
209 160
        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 160
    public function getUnparsedLoadedData()
350
    {
351 160
        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 176
    public function setUnparsedLoadedData(array $unparsedLoadedData): void
361
    {
362 176
        $this->unparsedLoadedData = $unparsedLoadedData;
363 176
    }
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 159
    public function hasRibbon()
425
    {
426 159
        return $this->ribbonXMLData !== null;
427
    }
428
429
    /**
430
     * This workbook have additionnal object for the ribbon ?
431
     *
432
     * @return bool
433
     */
434 159
    public function hasRibbonBinObjects()
435
    {
436 159
        return $this->ribbonBinObjects !== null;
437
    }
438
439
    /**
440
     * Check if a sheet with a specified code name already exists.
441
     *
442
     * @param string $codeName Name of the worksheet to check
443
     *
444
     * @return bool
445
     */
446 4816
    public function sheetCodeNameExists($codeName)
447
    {
448 4816
        return $this->getSheetByCodeName($codeName) !== null;
449
    }
450
451
    /**
452
     * Get sheet by code name. Warning : sheet don't have always a code name !
453
     *
454
     * @param string $codeName Sheet name
455
     *
456
     * @return null|Worksheet
457
     */
458 4816
    public function getSheetByCodeName($codeName)
459
    {
460 4816
        $worksheetCount = count($this->workSheetCollection);
461 4816
        for ($i = 0; $i < $worksheetCount; ++$i) {
462 307
            if ($this->workSheetCollection[$i]->getCodeName() == $codeName) {
463 197
                return $this->workSheetCollection[$i];
464
            }
465
        }
466
467 4816
        return null;
468
    }
469
470
    /**
471
     * Create a new PhpSpreadsheet with one Worksheet.
472
     */
473 4816
    public function __construct()
474
    {
475 4816
        $this->uniqueID = uniqid('', true);
476 4816
        $this->calculationEngine = new Calculation($this);
477
478
        // Initialise worksheet collection and add one worksheet
479 4816
        $this->workSheetCollection = [];
480 4816
        $this->workSheetCollection[] = new Worksheet($this);
481 4816
        $this->activeSheetIndex = 0;
482
483
        // Create document properties
484 4816
        $this->properties = new Document\Properties();
485
486
        // Create document security
487 4816
        $this->security = new Document\Security();
488
489
        // Set defined names
490 4816
        $this->definedNames = [];
491
492
        // Create the cellXf supervisor
493 4816
        $this->cellXfSupervisor = new Style(true);
494 4816
        $this->cellXfSupervisor->bindParent($this);
495
496
        // Create the default style
497 4816
        $this->addCellXf(new Style());
498 4816
        $this->addCellStyleXf(new Style());
499 4816
    }
500
501
    /**
502
     * Code to execute when this worksheet is unset().
503
     */
504 35
    public function __destruct()
505
    {
506 35
        $this->disconnectWorksheets();
507 35
        $this->calculationEngine = null;
508 35
        $this->cellXfCollection = [];
509 35
        $this->cellStyleXfCollection = [];
510 35
    }
511
512
    /**
513
     * Disconnect all worksheets from this PhpSpreadsheet workbook object,
514
     * typically so that the PhpSpreadsheet object can be unset.
515
     */
516 3107
    public function disconnectWorksheets(): void
517
    {
518 3107
        foreach ($this->workSheetCollection as $worksheet) {
519 3105
            $worksheet->disconnectCells();
520 3105
            unset($worksheet);
521
        }
522 3107
        $this->workSheetCollection = [];
523 3107
    }
524
525
    /**
526
     * Return the calculation engine for this worksheet.
527
     *
528
     * @return null|Calculation
529
     */
530 4816
    public function getCalculationEngine()
531
    {
532 4816
        return $this->calculationEngine;
533
    }
534
535
    /**
536
     * Get properties.
537
     *
538
     * @return Document\Properties
539
     */
540 526
    public function getProperties()
541
    {
542 526
        return $this->properties;
543
    }
544
545
    /**
546
     * Set properties.
547
     */
548
    public function setProperties(Document\Properties $documentProperties): void
549
    {
550
        $this->properties = $documentProperties;
551
    }
552
553
    /**
554
     * Get security.
555
     *
556
     * @return Document\Security
557
     */
558 160
    public function getSecurity()
559
    {
560 160
        return $this->security;
561
    }
562
563
    /**
564
     * Set security.
565
     */
566
    public function setSecurity(Document\Security $documentSecurity): void
567
    {
568
        $this->security = $documentSecurity;
569
    }
570
571
    /**
572
     * Get active sheet.
573
     *
574
     * @return Worksheet
575
     */
576 4778
    public function getActiveSheet()
577
    {
578 4778
        return $this->getSheet($this->activeSheetIndex);
579
    }
580
581
    /**
582
     * Create sheet and add it to this workbook.
583
     *
584
     * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
585
     *
586
     * @return Worksheet
587
     */
588 449
    public function createSheet($sheetIndex = null)
589
    {
590 449
        $newSheet = new Worksheet($this);
591 449
        $this->addSheet($newSheet, $sheetIndex);
592
593 449
        return $newSheet;
594
    }
595
596
    /**
597
     * Check if a sheet with a specified name already exists.
598
     *
599
     * @param string $worksheetName Name of the worksheet to check
600
     *
601
     * @return bool
602
     */
603 4816
    public function sheetNameExists($worksheetName)
604
    {
605 4816
        return $this->getSheetByName($worksheetName) !== null;
606
    }
607
608
    /**
609
     * Add sheet.
610
     *
611
     * @param Worksheet $worksheet The worskeet to add
612
     * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
613
     *
614
     * @return Worksheet
615
     */
616 522
    public function addSheet(Worksheet $worksheet, $sheetIndex = null)
617
    {
618 522
        if ($this->sheetNameExists($worksheet->getTitle())) {
619 1
            throw new Exception(
620 1
                "Workbook already contains a worksheet named '{$worksheet->getTitle()}'. Rename this worksheet first."
621
            );
622
        }
623
624 522
        if ($sheetIndex === null) {
625 493
            if ($this->activeSheetIndex < 0) {
626 254
                $this->activeSheetIndex = 0;
627
            }
628 493
            $this->workSheetCollection[] = $worksheet;
629
        } else {
630
            // Insert the sheet at the requested index
631 32
            array_splice(
632 32
                $this->workSheetCollection,
633
                $sheetIndex,
634 32
                0,
635 32
                [$worksheet]
636
            );
637
638
            // Adjust active sheet index if necessary
639 32
            if ($this->activeSheetIndex >= $sheetIndex) {
640 28
                ++$this->activeSheetIndex;
641
            }
642
        }
643
644 522
        if ($worksheet->getParent() === null) {
645 45
            $worksheet->rebindParent($this);
646
        }
647
648 522
        return $worksheet;
649
    }
650
651
    /**
652
     * Remove sheet by index.
653
     *
654
     * @param int $sheetIndex Index position of the worksheet to remove
655
     */
656 263
    public function removeSheetByIndex($sheetIndex): void
657
    {
658 263
        $numSheets = count($this->workSheetCollection);
659 263
        if ($sheetIndex > $numSheets - 1) {
660 1
            throw new Exception(
661 1
                "You tried to remove a sheet by the out of bounds index: {$sheetIndex}. The actual number of sheets is {$numSheets}."
662
            );
663
        }
664 262
        array_splice($this->workSheetCollection, $sheetIndex, 1);
665
666
        // Adjust active sheet index if necessary
667
        if (
668 262
            ($this->activeSheetIndex >= $sheetIndex) &&
669 262
            ($this->activeSheetIndex > 0 || $numSheets <= 1)
670
        ) {
671 260
            --$this->activeSheetIndex;
672
        }
673 262
    }
674
675
    /**
676
     * Get sheet by index.
677
     *
678
     * @param int $sheetIndex Sheet index
679
     *
680
     * @return Worksheet
681
     */
682 4794
    public function getSheet($sheetIndex)
683
    {
684 4794
        if (!isset($this->workSheetCollection[$sheetIndex])) {
685 1
            $numSheets = $this->getSheetCount();
686
687 1
            throw new Exception(
688 1
                "Your requested sheet index: {$sheetIndex} is out of bounds. The actual number of sheets is {$numSheets}."
689
            );
690
        }
691
692 4794
        return $this->workSheetCollection[$sheetIndex];
693
    }
694
695
    /**
696
     * Get all sheets.
697
     *
698
     * @return Worksheet[]
699
     */
700 94
    public function getAllSheets()
701
    {
702 94
        return $this->workSheetCollection;
703
    }
704
705
    /**
706
     * Get sheet by name.
707
     *
708
     * @param string $worksheetName Sheet name
709
     *
710
     * @return null|Worksheet
711
     */
712 4816
    public function getSheetByName($worksheetName)
713
    {
714 4816
        $worksheetCount = count($this->workSheetCollection);
715 4816
        for ($i = 0; $i < $worksheetCount; ++$i) {
716 4344
            if ($this->workSheetCollection[$i]->getTitle() === trim($worksheetName, "'")) {
717 4131
                return $this->workSheetCollection[$i];
718
            }
719
        }
720
721 4816
        return null;
722
    }
723
724
    /**
725
     * Get index for sheet.
726
     *
727
     * @return int index
728
     */
729 4459
    public function getIndex(Worksheet $worksheet)
730
    {
731 4459
        foreach ($this->workSheetCollection as $key => $value) {
732 4459
            if ($value->getHashCode() === $worksheet->getHashCode()) {
733 4458
                return $key;
734
            }
735
        }
736
737 2
        throw new Exception('Sheet does not exist.');
738
    }
739
740
    /**
741
     * Set index for sheet by sheet name.
742
     *
743
     * @param string $worksheetName Sheet name to modify index for
744
     * @param int $newIndexPosition New index for the sheet
745
     *
746
     * @return int New sheet index
747
     */
748 1
    public function setIndexByName($worksheetName, $newIndexPosition)
749
    {
750 1
        $oldIndex = $this->getIndex($this->getSheetByName($worksheetName));
1 ignored issue
show
Bug introduced by
It seems like $this->getSheetByName($worksheetName) can also be of type null; however, parameter $worksheet 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

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