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

Spreadsheet::setTabRatio()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3.1406

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 2
nop 1
dl 0
loc 6
ccs 3
cts 4
cp 0.75
crap 3.1406
rs 10
c 0
b 0
f 0
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