Failed Conditions
Push — master ( a2bb82...a189d9 )
by Adrien
10:27 queued 01:00
created

Spreadsheet::createSheet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
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 NamedRange[]
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 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 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|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 128
    public function hasMacros()
208
    {
209 128
        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];
0 ignored issues
show
Documentation Bug introduced by
It seems like array('target' => $target, 'data' => $xmlData) of type array<string,mixed> is incompatible with the declared type null|string of property $ribbonXMLData.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
293
        } else {
294
            $this->ribbonXMLData = null;
295
        }
296
    }
297
298
    /**
299
     * retrieve ribbon XML Data.
300
     *
301
     * return string|null|array
302
     *
303
     * @param string $what
304
     *
305
     * @return string
306
     */
307
    public function getRibbonXMLData($what = 'all') //we need some constants here...
308
    {
309
        $returnData = null;
310
        $what = strtolower($what);
311
        switch ($what) {
312
            case 'all':
313
                $returnData = $this->ribbonXMLData;
314
315
                break;
316
            case 'target':
317
            case 'data':
318
                if (is_array($this->ribbonXMLData) && isset($this->ribbonXMLData[$what])) {
0 ignored issues
show
introduced by
The condition is_array($this->ribbonXMLData) is always false.
Loading history...
319
                    $returnData = $this->ribbonXMLData[$what];
320
                }
321
322
                break;
323
        }
324
325
        return $returnData;
326
    }
327
328
    /**
329
     * store binaries ribbon objects (pictures).
330
     *
331
     * @param null|mixed $BinObjectsNames
332
     * @param null|mixed $BinObjectsData
333
     */
334
    public function setRibbonBinObjects($BinObjectsNames, $BinObjectsData): void
335
    {
336
        if ($BinObjectsNames !== null && $BinObjectsData !== null) {
337
            $this->ribbonBinObjects = ['names' => $BinObjectsNames, 'data' => $BinObjectsData];
338
        } else {
339
            $this->ribbonBinObjects = null;
340
        }
341
    }
342
343
    /**
344
     * List of unparsed loaded data for export to same format with better compatibility.
345
     * It has to be minimized when the library start to support currently unparsed data.
346
     *
347
     * @internal
348
     *
349
     * @return array
350
     */
351 128
    public function getUnparsedLoadedData()
352
    {
353 128
        return $this->unparsedLoadedData;
354
    }
355
356
    /**
357
     * List of unparsed loaded data for export to same format with better compatibility.
358
     * It has to be minimized when the library start to support currently unparsed data.
359
     *
360
     * @internal
361
     */
362 123
    public function setUnparsedLoadedData(array $unparsedLoadedData): void
363
    {
364 123
        $this->unparsedLoadedData = $unparsedLoadedData;
365 123
    }
366
367
    /**
368
     * return the extension of a filename. Internal use for a array_map callback (php<5.3 don't like lambda function).
369
     *
370
     * @param mixed $path
371
     *
372
     * @return string
373
     */
374
    private function getExtensionOnly($path)
375
    {
376
        $extension = pathinfo($path, PATHINFO_EXTENSION);
377
378
        return is_array($extension) ? '' : $extension;
379
    }
380
381
    /**
382
     * retrieve Binaries Ribbon Objects.
383
     *
384
     * @param string $what
385
     *
386
     * @return null|array
387
     */
388
    public function getRibbonBinObjects($what = 'all')
389
    {
390
        $ReturnData = null;
391
        $what = strtolower($what);
392
        switch ($what) {
393
            case 'all':
394
                return $this->ribbonBinObjects;
395
396
                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...
397
            case 'names':
398
            case 'data':
399
                if (is_array($this->ribbonBinObjects) && isset($this->ribbonBinObjects[$what])) {
400
                    $ReturnData = $this->ribbonBinObjects[$what];
401
                }
402
403
                break;
404
            case 'types':
405
                if (
406
                    is_array($this->ribbonBinObjects) &&
407
                    isset($this->ribbonBinObjects['data']) && is_array($this->ribbonBinObjects['data'])
408
                ) {
409
                    $tmpTypes = array_keys($this->ribbonBinObjects['data']);
410
                    $ReturnData = array_unique(array_map([$this, 'getExtensionOnly'], $tmpTypes));
411
                } else {
412
                    $ReturnData = []; // the caller want an array... not null if empty
413
                }
414
415
                break;
416
        }
417
418
        return $ReturnData;
419
    }
420
421
    /**
422
     * This workbook have a custom UI ?
423
     *
424
     * @return bool
425
     */
426 128
    public function hasRibbon()
427
    {
428 128
        return $this->ribbonXMLData !== null;
429
    }
430
431
    /**
432
     * This workbook have additionnal object for the ribbon ?
433
     *
434
     * @return bool
435
     */
436 128
    public function hasRibbonBinObjects()
437
    {
438 128
        return $this->ribbonBinObjects !== null;
439
    }
440
441
    /**
442
     * Check if a sheet with a specified code name already exists.
443
     *
444
     * @param string $pSheetCodeName Name of the worksheet to check
445
     *
446
     * @return bool
447
     */
448 3865
    public function sheetCodeNameExists($pSheetCodeName)
449
    {
450 3865
        return $this->getSheetByCodeName($pSheetCodeName) !== null;
451
    }
452
453
    /**
454
     * Get sheet by code name. Warning : sheet don't have always a code name !
455
     *
456
     * @param string $pName Sheet name
457
     *
458
     * @return Worksheet
459
     */
460 3865
    public function getSheetByCodeName($pName)
461
    {
462 3865
        $worksheetCount = count($this->workSheetCollection);
463 3865
        for ($i = 0; $i < $worksheetCount; ++$i) {
464 139
            if ($this->workSheetCollection[$i]->getCodeName() == $pName) {
465 92
                return $this->workSheetCollection[$i];
466
            }
467
        }
468
469 3865
        return null;
470
    }
471
472
    /**
473
     * Create a new PhpSpreadsheet with one Worksheet.
474
     */
475 3865
    public function __construct()
476
    {
477 3865
        $this->uniqueID = uniqid('', true);
478 3865
        $this->calculationEngine = new Calculation($this);
479
480
        // Initialise worksheet collection and add one worksheet
481 3865
        $this->workSheetCollection = [];
482 3865
        $this->workSheetCollection[] = new Worksheet($this);
483 3865
        $this->activeSheetIndex = 0;
484
485
        // Create document properties
486 3865
        $this->properties = new Document\Properties();
487
488
        // Create document security
489 3865
        $this->security = new Document\Security();
490
491
        // Set defined names
492 3865
        $this->definedNames = [];
493
494
        // Create the cellXf supervisor
495 3865
        $this->cellXfSupervisor = new Style(true);
496 3865
        $this->cellXfSupervisor->bindParent($this);
497
498
        // Create the default style
499 3865
        $this->addCellXf(new Style());
500 3865
        $this->addCellStyleXf(new Style());
501 3865
    }
502
503
    /**
504
     * Code to execute when this worksheet is unset().
505
     */
506 30
    public function __destruct()
507
    {
508 30
        $this->disconnectWorksheets();
509 30
        $this->calculationEngine = null;
510 30
    }
511
512
    /**
513
     * Disconnect all worksheets from this PhpSpreadsheet workbook object,
514
     * typically so that the PhpSpreadsheet object can be unset.
515
     */
516 2290
    public function disconnectWorksheets(): void
517
    {
518 2290
        $worksheet = null;
519 2290
        foreach ($this->workSheetCollection as $k => &$worksheet) {
520 2290
            $worksheet->disconnectCells();
521 2290
            $this->workSheetCollection[$k] = null;
522
        }
523 2290
        unset($worksheet);
524 2290
        $this->workSheetCollection = [];
525 2290
    }
526
527
    /**
528
     * Return the calculation engine for this worksheet.
529
     *
530
     * @return null|Calculation
531
     */
532 3865
    public function getCalculationEngine()
533
    {
534 3865
        return $this->calculationEngine;
535
    }
536
537
    /**
538
     * Get properties.
539
     *
540
     * @return Document\Properties
541
     */
542 424
    public function getProperties()
543
    {
544 424
        return $this->properties;
545
    }
546
547
    /**
548
     * Set properties.
549
     */
550
    public function setProperties(Document\Properties $pValue): void
551
    {
552
        $this->properties = $pValue;
553
    }
554
555
    /**
556
     * Get security.
557
     *
558
     * @return Document\Security
559
     */
560 128
    public function getSecurity()
561
    {
562 128
        return $this->security;
563
    }
564
565
    /**
566
     * Set security.
567
     */
568
    public function setSecurity(Document\Security $pValue): void
569
    {
570
        $this->security = $pValue;
571
    }
572
573
    /**
574
     * Get active sheet.
575
     *
576
     * @return Worksheet
577
     */
578 3831
    public function getActiveSheet()
579
    {
580 3831
        return $this->getSheet($this->activeSheetIndex);
581
    }
582
583
    /**
584
     * Create sheet and add it to this workbook.
585
     *
586
     * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
587
     *
588
     * @return Worksheet
589
     */
590 255
    public function createSheet($sheetIndex = null)
591
    {
592 255
        $newSheet = new Worksheet($this);
593 255
        $this->addSheet($newSheet, $sheetIndex);
594
595 255
        return $newSheet;
596
    }
597
598
    /**
599
     * Check if a sheet with a specified name already exists.
600
     *
601
     * @param string $pSheetName Name of the worksheet to check
602
     *
603
     * @return bool
604
     */
605 3865
    public function sheetNameExists($pSheetName)
606
    {
607 3865
        return $this->getSheetByName($pSheetName) !== null;
608
    }
609
610
    /**
611
     * Add sheet.
612
     *
613
     * @param null|int $iSheetIndex Index where sheet should go (0,1,..., or null for last)
614
     *
615
     * @return Worksheet
616
     */
617 301
    public function addSheet(Worksheet $pSheet, $iSheetIndex = null)
618
    {
619 301
        if ($this->sheetNameExists($pSheet->getTitle())) {
620 1
            throw new Exception(
621 1
                "Workbook already contains a worksheet named '{$pSheet->getTitle()}'. Rename this worksheet first."
622
            );
623
        }
624
625 301
        if ($iSheetIndex === null) {
626 299
            if ($this->activeSheetIndex < 0) {
627 184
                $this->activeSheetIndex = 0;
628
            }
629 299
            $this->workSheetCollection[] = $pSheet;
630
        } else {
631
            // Insert the sheet at the requested index
632 5
            array_splice(
633 5
                $this->workSheetCollection,
634
                $iSheetIndex,
635 5
                0,
636 5
                [$pSheet]
637
            );
638
639
            // Adjust active sheet index if necessary
640 5
            if ($this->activeSheetIndex >= $iSheetIndex) {
641 1
                ++$this->activeSheetIndex;
642
            }
643
        }
644
645 301
        if ($pSheet->getParent() === null) {
646 45
            $pSheet->rebindParent($this);
647
        }
648
649 301
        return $pSheet;
650
    }
651
652
    /**
653
     * Remove sheet by index.
654
     *
655
     * @param int $pIndex Active sheet index
656
     */
657 191
    public function removeSheetByIndex($pIndex): void
658
    {
659 191
        $numSheets = count($this->workSheetCollection);
660 191
        if ($pIndex > $numSheets - 1) {
661 1
            throw new Exception(
662 1
                "You tried to remove a sheet by the out of bounds index: {$pIndex}. The actual number of sheets is {$numSheets}."
663
            );
664
        }
665 190
        array_splice($this->workSheetCollection, $pIndex, 1);
666
667
        // Adjust active sheet index if necessary
668
        if (
669 190
            ($this->activeSheetIndex >= $pIndex) &&
670 190
            ($this->activeSheetIndex > 0 || $numSheets <= 1)
671
        ) {
672 188
            --$this->activeSheetIndex;
673
        }
674 190
    }
675
676
    /**
677
     * Get sheet by index.
678
     *
679
     * @param int $pIndex Sheet index
680
     *
681
     * @return Worksheet
682
     */
683 3847
    public function getSheet($pIndex)
684
    {
685 3847
        if (!isset($this->workSheetCollection[$pIndex])) {
686 1
            $numSheets = $this->getSheetCount();
687
688 1
            throw new Exception(
689 1
                "Your requested sheet index: {$pIndex} is out of bounds. The actual number of sheets is {$numSheets}."
690
            );
691
        }
692
693 3847
        return $this->workSheetCollection[$pIndex];
694
    }
695
696
    /**
697
     * Get all sheets.
698
     *
699
     * @return Worksheet[]
700
     */
701 75
    public function getAllSheets()
702
    {
703 75
        return $this->workSheetCollection;
704
    }
705
706
    /**
707
     * Get sheet by name.
708
     *
709
     * @param string $pName Sheet name
710
     *
711
     * @return null|Worksheet
712
     */
713 3865
    public function getSheetByName($pName)
714
    {
715 3865
        $worksheetCount = count($this->workSheetCollection);
716 3865
        for ($i = 0; $i < $worksheetCount; ++$i) {
717 3532
            if ($this->workSheetCollection[$i]->getTitle() === trim($pName, "'")) {
718 3351
                return $this->workSheetCollection[$i];
719
            }
720
        }
721
722 3865
        return null;
723
    }
724
725
    /**
726
     * Get index for sheet.
727
     *
728
     * @return int index
729
     */
730 3617
    public function getIndex(Worksheet $pSheet)
731
    {
732 3617
        foreach ($this->workSheetCollection as $key => $value) {
733 3617
            if ($value->getHashCode() === $pSheet->getHashCode()) {
734 3616
                return $key;
735
            }
736
        }
737
738 1
        throw new Exception('Sheet does not exist.');
739
    }
740
741
    /**
742
     * Set index for sheet by sheet name.
743
     *
744
     * @param string $sheetName Sheet name to modify index for
745
     * @param int $newIndex New index for the sheet
746
     *
747
     * @return int New sheet index
748
     */
749 1
    public function setIndexByName($sheetName, $newIndex)
750
    {
751 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

751
        $oldIndex = $this->getIndex(/** @scrutinizer ignore-type */ $this->getSheetByName($sheetName));
Loading history...
752 1
        $pSheet = array_splice(
753 1
            $this->workSheetCollection,
754
            $oldIndex,
755 1
            1
756
        );
757 1
        array_splice(
758 1
            $this->workSheetCollection,
759
            $newIndex,
760 1
            0,
761
            $pSheet
762
        );
763
764 1
        return $newIndex;
765
    }
766
767
    /**
768
     * Get sheet count.
769
     *
770
     * @return int
771
     */
772 482
    public function getSheetCount()
773
    {
774 482
        return count($this->workSheetCollection);
775
    }
776
777
    /**
778
     * Get active sheet index.
779
     *
780
     * @return int Active sheet index
781
     */
782 3362
    public function getActiveSheetIndex()
783
    {
784 3362
        return $this->activeSheetIndex;
785
    }
786
787
    /**
788
     * Set active sheet index.
789
     *
790
     * @param int $pIndex Active sheet index
791
     *
792
     * @return Worksheet
793
     */
794 3696
    public function setActiveSheetIndex($pIndex)
795
    {
796 3696
        $numSheets = count($this->workSheetCollection);
797
798 3696
        if ($pIndex > $numSheets - 1) {
799 1
            throw new Exception(
800 1
                "You tried to set a sheet active by the out of bounds index: {$pIndex}. The actual number of sheets is {$numSheets}."
801
            );
802
        }
803 3695
        $this->activeSheetIndex = $pIndex;
804
805 3695
        return $this->getActiveSheet();
806
    }
807
808
    /**
809
     * Set active sheet index by name.
810
     *
811
     * @param string $pValue Sheet title
812
     *
813
     * @return Worksheet
814
     */
815 27
    public function setActiveSheetIndexByName($pValue)
816
    {
817 27
        if (($worksheet = $this->getSheetByName($pValue)) instanceof Worksheet) {
818 25
            $this->setActiveSheetIndex($this->getIndex($worksheet));
819
820 25
            return $worksheet;
821
        }
822
823 2
        throw new Exception('Workbook does not contain sheet:' . $pValue);
824
    }
825
826
    /**
827
     * Get sheet names.
828
     *
829
     * @return string[]
830
     */
831 9
    public function getSheetNames()
832
    {
833 9
        $returnValue = [];
834 9
        $worksheetCount = $this->getSheetCount();
835 9
        for ($i = 0; $i < $worksheetCount; ++$i) {
836 9
            $returnValue[] = $this->getSheet($i)->getTitle();
837
        }
838
839 9
        return $returnValue;
840
    }
841
842
    /**
843
     * Add external sheet.
844
     *
845
     * @param Worksheet $pSheet External sheet to add
846
     * @param null|int $iSheetIndex Index where sheet should go (0,1,..., or null for last)
847
     *
848
     * @return Worksheet
849
     */
850 3
    public function addExternalSheet(Worksheet $pSheet, $iSheetIndex = null)
851
    {
852 3
        if ($this->sheetNameExists($pSheet->getTitle())) {
853 1
            throw new Exception("Workbook already contains a worksheet named '{$pSheet->getTitle()}'. Rename the external sheet first.");
854
        }
855
856
        // count how many cellXfs there are in this workbook currently, we will need this below
857 2
        $countCellXfs = count($this->cellXfCollection);
858
859
        // copy all the shared cellXfs from the external workbook and append them to the current
860 2
        foreach ($pSheet->getParent()->getCellXfCollection() as $cellXf) {
861 2
            $this->addCellXf(clone $cellXf);
862
        }
863
864
        // move sheet to this workbook
865 2
        $pSheet->rebindParent($this);
866
867
        // update the cellXfs
868 2
        foreach ($pSheet->getCoordinates(false) as $coordinate) {
869 2
            $cell = $pSheet->getCell($coordinate);
870 2
            $cell->setXfIndex($cell->getXfIndex() + $countCellXfs);
871
        }
872
873 2
        return $this->addSheet($pSheet, $iSheetIndex);
874
    }
875
876
    /**
877
     * Get an array of all Named Ranges.
878
     *
879
     * @return NamedRange[]
880
     */
881 8
    public function getNamedRanges(): array
882
    {
883 8
        return array_filter(
884 8
            $this->definedNames,
885
            function (DefinedName $definedName) {
886 8
                return $definedName->isFormula() === self::DEFINED_NAME_IS_RANGE;
887 8
            }
888
        );
889
    }
890
891
    /**
892
     * Get an array of all Named Formulae.
893
     *
894
     * @return NamedFormula[]
895
     */
896 8
    public function getNamedFormulae(): array
897
    {
898 8
        return array_filter(
899 8
            $this->definedNames,
900
            function (DefinedName $definedName) {
901 8
                return $definedName->isFormula() === self::DEFINED_NAME_IS_FORMULA;
902 8
            }
903
        );
904
    }
905
906
    /**
907
     * Get an array of all Defined Names (both named ranges and named formulae).
908
     *
909
     * @return DefinedName[]
910
     */
911 185
    public function getDefinedNames(): array
912
    {
913 185
        return $this->definedNames;
914
    }
915
916
    /**
917
     * Add a named range.
918
     * If a named range with this name already exists, then this will replace the existing value.
919
     */
920 55
    public function addNamedRange(NamedRange $namedRange): void
921
    {
922 55
        $this->addDefinedName($namedRange);
923 55
    }
924
925
    /**
926
     * Add a named formula.
927
     * If a named formula with this name already exists, then this will replace the existing value.
928
     */
929 9
    public function addNamedFormula(NamedFormula $namedFormula): void
930
    {
931 9
        $this->addDefinedName($namedFormula);
932 9
    }
933
934
    /**
935
     * Add a defined name (either a named range or a named formula).
936
     * If a defined named with this name already exists, then this will replace the existing value.
937
     */
938 133
    public function addDefinedName(DefinedName $definedName): void
939
    {
940 133
        $upperCaseName = StringHelper::strToUpper($definedName->getName());
941 133
        if ($definedName->getScope() == null) {
942
            // global scope
943 128
            $this->definedNames[$upperCaseName] = $definedName;
944
        } else {
945
            // local scope
946 30
            $this->definedNames[$definedName->getScope()->getTitle() . '!' . $upperCaseName] = $definedName;
947
        }
948 133
    }
949
950
    /**
951
     * Get named range.
952
     *
953
     * @param null|Worksheet $pSheet Scope. Use null for global scope
954
     */
955 9
    public function getNamedRange(string $namedRange, ?Worksheet $pSheet = null): ?NamedRange
956
    {
957 9
        $returnValue = null;
958
959 9
        if ($namedRange !== '') {
960 9
            $namedRange = StringHelper::strToUpper($namedRange);
961
            // first look for global named range
962 9
            $returnValue = $this->getGlobalDefinedNameByType($namedRange, self::DEFINED_NAME_IS_RANGE);
963
            // then look for local named range (has priority over global named range if both names exist)
964 9
            $returnValue = $this->getLocalDefinedNameByType($namedRange, self::DEFINED_NAME_IS_RANGE, $pSheet) ?: $returnValue;
965
        }
966
967 9
        return $returnValue instanceof NamedRange ? $returnValue : null;
968
    }
969
970
    /**
971
     * Get named formula.
972
     *
973
     * @param null|Worksheet $pSheet Scope. Use null for global scope
974
     */
975 7
    public function getNamedFormula(string $namedFormula, ?Worksheet $pSheet = null): ?NamedFormula
976
    {
977 7
        $returnValue = null;
978
979 7
        if ($namedFormula !== '') {
980 7
            $namedFormula = StringHelper::strToUpper($namedFormula);
981
            // first look for global named formula
982 7
            $returnValue = $this->getGlobalDefinedNameByType($namedFormula, self::DEFINED_NAME_IS_FORMULA);
983
            // then look for local named formula (has priority over global named formula if both names exist)
984 7
            $returnValue = $this->getLocalDefinedNameByType($namedFormula, self::DEFINED_NAME_IS_FORMULA, $pSheet) ?: $returnValue;
985
        }
986
987 7
        return $returnValue instanceof NamedFormula ? $returnValue : null;
988
    }
989
990 16
    private function getGlobalDefinedNameByType(string $name, bool $type): ?DefinedName
991
    {
992 16
        if (isset($this->definedNames[$name]) && $this->definedNames[$name]->isFormula() === $type) {
993 16
            return $this->definedNames[$name];
994
        }
995
996 2
        return null;
997
    }
998
999 16
    private function getLocalDefinedNameByType(string $name, bool $type, ?Worksheet $pSheet = null): ?DefinedName
1000
    {
1001
        if (
1002 16
            ($pSheet !== null) && isset($this->definedNames[$pSheet->getTitle() . '!' . $name])
1003 16
            && $this->definedNames[$pSheet->getTitle() . '!' . $name]->isFormula() === $type
1004
        ) {
1005 8
            return $this->definedNames[$pSheet->getTitle() . '!' . $name];
1006
        }
1007
1008 14
        return null;
1009
    }
1010
1011
    /**
1012
     * Get named range.
1013
     *
1014
     * @param null|Worksheet $pSheet Scope. Use null for global scope
1015
     */
1016 97
    public function getDefinedName(string $definedName, ?Worksheet $pSheet = null): ?DefinedName
1017
    {
1018 97
        $returnValue = null;
1019
1020 97
        if ($definedName !== '') {
1021 97
            $definedName = StringHelper::strToUpper($definedName);
1022
            // first look for global defined name
1023 97
            if (isset($this->definedNames[$definedName])) {
1024 68
                $returnValue = $this->definedNames[$definedName];
1025
            }
1026
1027
            // then look for local defined name (has priority over global defined name if both names exist)
1028 97
            if (($pSheet !== null) && isset($this->definedNames[$pSheet->getTitle() . '!' . $definedName])) {
1029 7
                $returnValue = $this->definedNames[$pSheet->getTitle() . '!' . $definedName];
1030
            }
1031
        }
1032
1033 97
        return $returnValue;
1034
    }
1035
1036
    /**
1037
     * Remove named range.
1038
     *
1039
     * @param null|Worksheet $pSheet scope: use null for global scope
1040
     *
1041
     * @return $this
1042
     */
1043 4
    public function removeNamedRange(string $namedRange, ?Worksheet $pSheet = null): self
1044
    {
1045 4
        if ($this->getNamedRange($namedRange, $pSheet) === null) {
1046
            return $this;
1047
        }
1048
1049 4
        return $this->removeDefinedName($namedRange, $pSheet);
1050
    }
1051
1052
    /**
1053
     * Remove named formula.
1054
     *
1055
     * @param null|Worksheet $pSheet scope: use null for global scope
1056
     *
1057
     * @return $this
1058
     */
1059 3
    public function removeNamedFormula(string $namedFormula, ?Worksheet $pSheet = null): self
1060
    {
1061 3
        if ($this->getNamedFormula($namedFormula, $pSheet) === null) {
1062
            return $this;
1063
        }
1064
1065 3
        return $this->removeDefinedName($namedFormula, $pSheet);
1066
    }
1067
1068
    /**
1069
     * Remove defined name.
1070
     *
1071
     * @param null|Worksheet $pSheet scope: use null for global scope
1072
     *
1073
     * @return $this
1074
     */
1075 10
    public function removeDefinedName(string $definedName, ?Worksheet $pSheet = null): self
1076
    {
1077 10
        $definedName = StringHelper::strToUpper($definedName);
1078
1079 10
        if ($pSheet === null) {
1080
            if (isset($this->definedNames[$definedName])) {
1081
                unset($this->definedNames[$definedName]);
1082
            }
1083
        } else {
1084 10
            if (isset($this->definedNames[$pSheet->getTitle() . '!' . $definedName])) {
1085 3
                unset($this->definedNames[$pSheet->getTitle() . '!' . $definedName]);
1086 7
            } elseif (isset($this->definedNames[$definedName])) {
1087 7
                unset($this->definedNames[$definedName]);
1088
            }
1089
        }
1090
1091 10
        return $this;
1092
    }
1093
1094
    /**
1095
     * Get worksheet iterator.
1096
     *
1097
     * @return Iterator
1098
     */
1099 391
    public function getWorksheetIterator()
1100
    {
1101 391
        return new Iterator($this);
1102
    }
1103
1104
    /**
1105
     * Copy workbook (!= clone!).
1106
     *
1107
     * @return Spreadsheet
1108
     */
1109
    public function copy()
1110
    {
1111
        $copied = clone $this;
1112
1113
        $worksheetCount = count($this->workSheetCollection);
1114
        for ($i = 0; $i < $worksheetCount; ++$i) {
1115
            $this->workSheetCollection[$i] = $this->workSheetCollection[$i]->copy();
1116
            $this->workSheetCollection[$i]->rebindParent($this);
1117
        }
1118
1119
        return $copied;
1120
    }
1121
1122
    /**
1123
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
1124
     */
1125
    public function __clone()
1126
    {
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 333
    public function getCellXfCollection()
1140
    {
1141 333
        return $this->cellXfCollection;
1142
    }
1143
1144
    /**
1145
     * Get cellXf by index.
1146
     *
1147
     * @param int $pIndex
1148
     *
1149
     * @return Style
1150
     */
1151 3550
    public function getCellXfByIndex($pIndex)
1152
    {
1153 3550
        return $this->cellXfCollection[$pIndex];
1154
    }
1155
1156
    /**
1157
     * Get cellXf by hash code.
1158
     *
1159
     * @param string $pValue
1160
     *
1161
     * @return false|Style
1162
     */
1163 249
    public function getCellXfByHashCode($pValue)
1164
    {
1165 249
        foreach ($this->cellXfCollection as $cellXf) {
1166 249
            if ($cellXf->getHashCode() === $pValue) {
1167 102
                return $cellXf;
1168
            }
1169
        }
1170
1171 234
        return false;
1172
    }
1173
1174
    /**
1175
     * Check if style exists in style collection.
1176
     *
1177
     * @param Style $pCellStyle
1178
     *
1179
     * @return bool
1180
     */
1181
    public function cellXfExists($pCellStyle)
1182
    {
1183
        return in_array($pCellStyle, $this->cellXfCollection, true);
1184
    }
1185
1186
    /**
1187
     * Get default style.
1188
     *
1189
     * @return Style
1190
     */
1191 320
    public function getDefaultStyle()
1192
    {
1193 320
        if (isset($this->cellXfCollection[0])) {
1194 320
            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 3865
    public function addCellXf(Style $style): void
1204
    {
1205 3865
        $this->cellXfCollection[] = $style;
1206 3865
        $style->setIndex(count($this->cellXfCollection) - 1);
1207 3865
    }
1208
1209
    /**
1210
     * Remove cellXf by index. It is ensured that all cells get their xf index updated.
1211
     *
1212
     * @param int $pIndex Index to cellXf
1213
     */
1214 159
    public function removeCellXfByIndex($pIndex): void
1215
    {
1216 159
        if ($pIndex > count($this->cellXfCollection) - 1) {
1217
            throw new Exception('CellXf index is out of bounds.');
1218
        }
1219
1220
        // first remove the cellXf
1221 159
        array_splice($this->cellXfCollection, $pIndex, 1);
1222
1223
        // then update cellXf indexes for cells
1224 159
        foreach ($this->workSheetCollection as $worksheet) {
1225
            foreach ($worksheet->getCoordinates(false) as $coordinate) {
1226
                $cell = $worksheet->getCell($coordinate);
1227
                $xfIndex = $cell->getXfIndex();
1228
                if ($xfIndex > $pIndex) {
1229
                    // decrease xf index by 1
1230
                    $cell->setXfIndex($xfIndex - 1);
1231
                } elseif ($xfIndex == $pIndex) {
1232
                    // set to default xf index 0
1233
                    $cell->setXfIndex(0);
1234
                }
1235
            }
1236
        }
1237 159
    }
1238
1239
    /**
1240
     * Get the cellXf supervisor.
1241
     *
1242
     * @return Style
1243
     */
1244 3497
    public function getCellXfSupervisor()
1245
    {
1246 3497
        return $this->cellXfSupervisor;
1247
    }
1248
1249
    /**
1250
     * Get the workbook collection of cellStyleXfs.
1251
     *
1252
     * @return Style[]
1253
     */
1254 55
    public function getCellStyleXfCollection()
1255
    {
1256 55
        return $this->cellStyleXfCollection;
1257
    }
1258
1259
    /**
1260
     * Get cellStyleXf by index.
1261
     *
1262
     * @param int $pIndex Index to cellXf
1263
     *
1264
     * @return Style
1265
     */
1266
    public function getCellStyleXfByIndex($pIndex)
1267
    {
1268
        return $this->cellStyleXfCollection[$pIndex];
1269
    }
1270
1271
    /**
1272
     * Get cellStyleXf by hash code.
1273
     *
1274
     * @param string $pValue
1275
     *
1276
     * @return false|Style
1277
     */
1278
    public function getCellStyleXfByHashCode($pValue)
1279
    {
1280
        foreach ($this->cellStyleXfCollection as $cellStyleXf) {
1281
            if ($cellStyleXf->getHashCode() === $pValue) {
1282
                return $cellStyleXf;
1283
            }
1284
        }
1285
1286
        return false;
1287
    }
1288
1289
    /**
1290
     * Add a cellStyleXf to the workbook.
1291
     */
1292 3865
    public function addCellStyleXf(Style $pStyle): void
1293
    {
1294 3865
        $this->cellStyleXfCollection[] = $pStyle;
1295 3865
        $pStyle->setIndex(count($this->cellStyleXfCollection) - 1);
1296 3865
    }
1297
1298
    /**
1299
     * Remove cellStyleXf by index.
1300
     *
1301
     * @param int $pIndex Index to cellXf
1302
     */
1303 159
    public function removeCellStyleXfByIndex($pIndex): void
1304
    {
1305 159
        if ($pIndex > count($this->cellStyleXfCollection) - 1) {
1306
            throw new Exception('CellStyleXf index is out of bounds.');
1307
        }
1308 159
        array_splice($this->cellStyleXfCollection, $pIndex, 1);
1309 159
    }
1310
1311
    /**
1312
     * Eliminate all unneeded cellXf and afterwards update the xfIndex for all cells
1313
     * and columns in the workbook.
1314
     */
1315 313
    public function garbageCollect(): void
1316
    {
1317
        // how many references are there to each cellXf ?
1318 313
        $countReferencesCellXf = [];
1319 313
        foreach ($this->cellXfCollection as $index => $cellXf) {
1320 313
            $countReferencesCellXf[$index] = 0;
1321
        }
1322
1323 313
        foreach ($this->getWorksheetIterator() as $sheet) {
1324
            // from cells
1325 313
            foreach ($sheet->getCoordinates(false) as $coordinate) {
1326 303
                $cell = $sheet->getCell($coordinate);
1327 303
                ++$countReferencesCellXf[$cell->getXfIndex()];
1328
            }
1329
1330
            // from row dimensions
1331 313
            foreach ($sheet->getRowDimensions() as $rowDimension) {
1332 52
                if ($rowDimension->getXfIndex() !== null) {
1333
                    ++$countReferencesCellXf[$rowDimension->getXfIndex()];
1334
                }
1335
            }
1336
1337
            // from column dimensions
1338 313
            foreach ($sheet->getColumnDimensions() as $columnDimension) {
1339 42
                ++$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 313
        $countNeededCellXfs = 0;
1346 313
        $map = [];
1347 313
        foreach ($this->cellXfCollection as $index => $cellXf) {
1348 313
            if ($countReferencesCellXf[$index] > 0 || $index == 0) { // we must never remove the first cellXf
1349 313
                ++$countNeededCellXfs;
1350
            } else {
1351 20
                unset($this->cellXfCollection[$index]);
1352
            }
1353 313
            $map[$index] = $countNeededCellXfs - 1;
1354
        }
1355 313
        $this->cellXfCollection = array_values($this->cellXfCollection);
1356
1357
        // update the index for all cellXfs
1358 313
        foreach ($this->cellXfCollection as $i => $cellXf) {
1359 313
            $cellXf->setIndex($i);
1360
        }
1361
1362
        // make sure there is always at least one cellXf (there should be)
1363 313
        if (empty($this->cellXfCollection)) {
1364
            $this->cellXfCollection[] = new Style();
1365
        }
1366
1367
        // update the xfIndex for all cells, row dimensions, column dimensions
1368 313
        foreach ($this->getWorksheetIterator() as $sheet) {
1369
            // for all cells
1370 313
            foreach ($sheet->getCoordinates(false) as $coordinate) {
1371 303
                $cell = $sheet->getCell($coordinate);
1372 303
                $cell->setXfIndex($map[$cell->getXfIndex()]);
1373
            }
1374
1375
            // for all row dimensions
1376 313
            foreach ($sheet->getRowDimensions() as $rowDimension) {
1377 52
                if ($rowDimension->getXfIndex() !== null) {
1378
                    $rowDimension->setXfIndex($map[$rowDimension->getXfIndex()]);
1379
                }
1380
            }
1381
1382
            // for all column dimensions
1383 313
            foreach ($sheet->getColumnDimensions() as $columnDimension) {
1384 42
                $columnDimension->setXfIndex($map[$columnDimension->getXfIndex()]);
1385
            }
1386
1387
            // also do garbage collection for all the sheets
1388 313
            $sheet->garbageCollect();
1389
        }
1390 313
    }
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 128
    public function getShowHorizontalScroll()
1408
    {
1409 128
        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 61
    public function setShowHorizontalScroll($showHorizontalScroll): void
1418
    {
1419 61
        $this->showHorizontalScroll = (bool) $showHorizontalScroll;
1420 61
    }
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 128
    public function getShowVerticalScroll()
1428
    {
1429 128
        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 61
    public function setShowVerticalScroll($showVerticalScroll): void
1438
    {
1439 61
        $this->showVerticalScroll = (bool) $showVerticalScroll;
1440 61
    }
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 128
    public function getShowSheetTabs()
1448
    {
1449 128
        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 61
    public function setShowSheetTabs($showSheetTabs): void
1458
    {
1459 61
        $this->showSheetTabs = (bool) $showSheetTabs;
1460 61
    }
1461
1462
    /**
1463
     * Return whether the workbook window is minimized.
1464
     *
1465
     * @return bool true if workbook window is minimized
1466
     */
1467 128
    public function getMinimized()
1468
    {
1469 128
        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 61
    public function setMinimized($minimized): void
1478
    {
1479 61
        $this->minimized = (bool) $minimized;
1480 61
    }
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 128
    public function getAutoFilterDateGrouping()
1489
    {
1490 128
        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 61
    public function setAutoFilterDateGrouping($autoFilterDateGrouping): void
1500
    {
1501 61
        $this->autoFilterDateGrouping = (bool) $autoFilterDateGrouping;
1502 61
    }
1503
1504
    /**
1505
     * Return the first sheet in the book view.
1506
     *
1507
     * @return int First sheet in book view
1508
     */
1509 128
    public function getFirstSheetIndex()
1510
    {
1511 128
        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 61
    public function setFirstSheetIndex($firstSheetIndex): void
1520
    {
1521 61
        if ($firstSheetIndex >= 0) {
1522 61
            $this->firstSheetIndex = (int) $firstSheetIndex;
1523
        } else {
1524
            throw new Exception('First sheet index must be a positive integer.');
1525
        }
1526 61
    }
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 128
    public function getVisibility()
1537
    {
1538 128
        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 61
    public function setVisibility($visibility): void
1557
    {
1558 61
        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 61
        if (in_array($visibility, self::$workbookViewVisibilityValues)) {
1563 61
            $this->visibility = $visibility;
1564
        } else {
1565
            throw new Exception('Invalid visibility value.');
1566
        }
1567 61
    }
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 128
    public function getTabRatio()
1576
    {
1577 128
        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 61
    public function setTabRatio($tabRatio): void
1587
    {
1588 61
        if ($tabRatio >= 0 || $tabRatio <= 1000) {
1589 61
            $this->tabRatio = (int) $tabRatio;
1590
        } else {
1591
            throw new Exception('Tab ratio must be between 0 and 1000.');
1592
        }
1593 61
    }
1594
}
1595