Failed Conditions
Push — develop ( 064076...83c759 )
by Adrien
63:09
created

Spreadsheet::getCalculationEngine()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
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\Style\Style;
7
use PhpOffice\PhpSpreadsheet\Worksheet\Iterator;
8
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
9
10
class Spreadsheet
11
{
12
    /**
13
     * Unique ID.
14
     *
15
     * @var string
16
     */
17
    private $uniqueID;
18
19
    /**
20
     * Document properties.
21
     *
22
     * @var Document\Properties
23
     */
24
    private $properties;
25
26
    /**
27
     * Document security.
28
     *
29
     * @var Document\Security
30
     */
31
    private $security;
32
33
    /**
34
     * Collection of Worksheet objects.
35
     *
36
     * @var Worksheet[]
37
     */
38
    private $workSheetCollection = [];
39
40
    /**
41
     * Calculation Engine.
42
     *
43
     * @var Calculation
44
     */
45
    private $calculationEngine;
46
47
    /**
48
     * Active sheet index.
49
     *
50
     * @var int
51
     */
52
    private $activeSheetIndex = 0;
53
54
    /**
55
     * Named ranges.
56
     *
57
     * @var NamedRange[]
58
     */
59
    private $namedRanges = [];
60
61
    /**
62
     * CellXf supervisor.
63
     *
64
     * @var Style
65
     */
66
    private $cellXfSupervisor;
67
68
    /**
69
     * CellXf collection.
70
     *
71
     * @var Style[]
72
     */
73
    private $cellXfCollection = [];
74
75
    /**
76
     * CellStyleXf collection.
77
     *
78
     * @var Style[]
79
     */
80
    private $cellStyleXfCollection = [];
81
82
    /**
83
     * hasMacros : this workbook have macros ?
84
     *
85
     * @var bool
86
     */
87
    private $hasMacros = false;
88
89
    /**
90
     * macrosCode : all macros code as binary data (the vbaProject.bin file, this include form, code,  etc.), null if no macro.
91
     *
92
     * @var string
93
     */
94
    private $macrosCode;
95
96
    /**
97
     * macrosCertificate : if macros are signed, contains binary data vbaProjectSignature.bin file, null if not signed.
98
     *
99
     * @var string
100
     */
101
    private $macrosCertificate;
102
103
    /**
104
     * ribbonXMLData : null if workbook is'nt Excel 2007 or not contain a customized UI.
105
     *
106
     * @var null|string
107
     */
108
    private $ribbonXMLData;
109
110
    /**
111
     * ribbonBinObjects : null if workbook is'nt Excel 2007 or not contain embedded objects (picture(s)) for Ribbon Elements
112
     * ignored if $ribbonXMLData is null.
113
     *
114
     * @var null|array
115
     */
116
    private $ribbonBinObjects;
117
118
    /**
119
     * List of unparsed loaded data for export to same format with better compatibility.
120
     * It has to be minimized when the library start to support currently unparsed data.
121
     *
122
     * @var array
123 73
     */
124
    private $unparsedLoadedData = [];
125 73
126
    /**
127
     * The workbook has macros ?
128
     *
129
     * @return bool
130
     */
131
    public function hasMacros()
132
    {
133
        return $this->hasMacros;
134
    }
135
136
    /**
137
     * Define if a workbook has macros.
138
     *
139
     * @param bool $hasMacros true|false
140
     */
141
    public function setHasMacros($hasMacros)
142
    {
143
        $this->hasMacros = (bool) $hasMacros;
144
    }
145
146
    /**
147
     * Set the macros code.
148
     *
149
     * @param string $macroCode string|null
150
     */
151
    public function setMacrosCode($macroCode)
152
    {
153
        $this->macrosCode = $macroCode;
154
        $this->setHasMacros($macroCode !== null);
155
    }
156
157
    /**
158
     * Return the macros code.
159
     *
160
     * @return null|string
161
     */
162
    public function getMacrosCode()
163
    {
164
        return $this->macrosCode;
165
    }
166
167
    /**
168
     * Set the macros certificate.
169
     *
170
     * @param null|string $certificate
171
     */
172
    public function setMacrosCertificate($certificate)
173
    {
174
        $this->macrosCertificate = $certificate;
175
    }
176
177
    /**
178
     * Is the project signed ?
179
     *
180
     * @return bool true|false
181
     */
182
    public function hasMacrosCertificate()
183
    {
184
        return $this->macrosCertificate !== null;
185
    }
186
187
    /**
188
     * Return the macros certificate.
189
     *
190
     * @return null|string
191
     */
192
    public function getMacrosCertificate()
193
    {
194
        return $this->macrosCertificate;
195
    }
196
197
    /**
198
     * Remove all macros, certificate from spreadsheet.
199
     */
200
    public function discardMacros()
201
    {
202
        $this->hasMacros = false;
203
        $this->macrosCode = null;
204
        $this->macrosCertificate = null;
205
    }
206
207
    /**
208
     * set ribbon XML data.
209
     *
210
     * @param null|mixed $target
211
     * @param null|mixed $xmlData
212
     */
213
    public function setRibbonXMLData($target, $xmlData)
214
    {
215
        if ($target !== null && $xmlData !== null) {
216
            $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...
217
        } else {
218
            $this->ribbonXMLData = null;
219
        }
220
    }
221
222
    /**
223
     * retrieve ribbon XML Data.
224
     *
225
     * return string|null|array
226
     *
227
     * @param string $what
228
     *
229
     * @return string
230
     */
231
    public function getRibbonXMLData($what = 'all') //we need some constants here...
232
    {
233
        $returnData = null;
234
        $what = strtolower($what);
235
        switch ($what) {
236
            case 'all':
237
                $returnData = $this->ribbonXMLData;
238
239
                break;
240
            case 'target':
241
            case 'data':
242
                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...
243
                    $returnData = $this->ribbonXMLData[$what];
244
                }
245
246
                break;
247
        }
248
249
        return $returnData;
250
    }
251
252
    /**
253
     * store binaries ribbon objects (pictures).
254
     *
255
     * @param null|mixed $BinObjectsNames
256
     * @param null|mixed $BinObjectsData
257
     */
258
    public function setRibbonBinObjects($BinObjectsNames, $BinObjectsData)
259
    {
260
        if ($BinObjectsNames !== null && $BinObjectsData !== null) {
261
            $this->ribbonBinObjects = ['names' => $BinObjectsNames, 'data' => $BinObjectsData];
262
        } else {
263
            $this->ribbonBinObjects = null;
264
        }
265
    }
266
267
    /**
268
     * List of unparsed loaded data for export to same format with better compatibility.
269
     * It has to be minimized when the library start to support currently unparsed data.
270
     *
271
     * @internal
272
     *
273
     * @return array
274
     */
275
    public function getUnparsedLoadedData()
276
    {
277
        return $this->unparsedLoadedData;
278
    }
279
280
    /**
281
     * List of unparsed loaded data for export to same format with better compatibility.
282
     * It has to be minimized when the library start to support currently unparsed data.
283
     *
284
     * @internal
285
     *
286
     * @param array $unparsedLoadedData
287
     */
288
    public function setUnparsedLoadedData(array $unparsedLoadedData)
289
    {
290
        $this->unparsedLoadedData = $unparsedLoadedData;
291
    }
292
293
    /**
294
     * return the extension of a filename. Internal use for a array_map callback (php<5.3 don't like lambda function).
295
     *
296
     * @param mixed $path
297
     *
298
     * @return string
299
     */
300
    private function getExtensionOnly($path)
301
    {
302
        return pathinfo($path, PATHINFO_EXTENSION);
303
    }
304
305
    /**
306
     * retrieve Binaries Ribbon Objects.
307
     *
308
     * @param string $what
309
     *
310
     * @return null|array
311
     */
312
    public function getRibbonBinObjects($what = 'all')
313
    {
314 73
        $ReturnData = null;
315
        $what = strtolower($what);
316 73
        switch ($what) {
317
            case 'all':
318
                return $this->ribbonBinObjects;
319
320
                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...
321
            case 'names':
322
            case 'data':
323
                if (is_array($this->ribbonBinObjects) && isset($this->ribbonBinObjects[$what])) {
324 73
                    $ReturnData = $this->ribbonBinObjects[$what];
325
                }
326 73
327
                break;
328
            case 'types':
329
                if (is_array($this->ribbonBinObjects) &&
330
                    isset($this->ribbonBinObjects['data']) && is_array($this->ribbonBinObjects['data'])) {
331
                    $tmpTypes = array_keys($this->ribbonBinObjects['data']);
332
                    $ReturnData = array_unique(array_map([$this, 'getExtensionOnly'], $tmpTypes));
333
                } else {
334
                    $ReturnData = []; // the caller want an array... not null if empty
335
                }
336 172
337
                break;
338 172
        }
339
340
        return $ReturnData;
341
    }
342
343
    /**
344
     * This workbook have a custom UI ?
345
     *
346
     * @return bool
347
     */
348 172
    public function hasRibbon()
349
    {
350 172
        return $this->ribbonXMLData !== null;
351 172
    }
352 44
353 41
    /**
354
     * This workbook have additionnal object for the ribbon ?
355
     *
356
     * @return bool
357 172
     */
358
    public function hasRibbonBinObjects()
359
    {
360
        return $this->ribbonBinObjects !== null;
361
    }
362
363 172
    /**
364
     * Check if a sheet with a specified code name already exists.
365 172
     *
366 172
     * @param string $pSheetCodeName Name of the worksheet to check
367
     *
368
     * @return bool
369 172
     */
370 172
    public function sheetCodeNameExists($pSheetCodeName)
371 172
    {
372
        return $this->getSheetByCodeName($pSheetCodeName) !== null;
373
    }
374 172
375
    /**
376
     * Get sheet by code name. Warning : sheet don't have always a code name !
377 172
     *
378
     * @param string $pName Sheet name
379
     *
380 172
     * @return Worksheet
381
     */
382
    public function getSheetByCodeName($pName)
383 172
    {
384 172
        $worksheetCount = count($this->workSheetCollection);
385
        for ($i = 0; $i < $worksheetCount; ++$i) {
386
            if ($this->workSheetCollection[$i]->getCodeName() == $pName) {
387 172
                return $this->workSheetCollection[$i];
388 172
            }
389 172
        }
390
391
        return null;
392
    }
393
394 4
    /**
395
     * Create a new PhpSpreadsheet with one Worksheet.
396 4
     */
397 4
    public function __construct()
398 4
    {
399
        $this->uniqueID = uniqid('', true);
400
        $this->calculationEngine = new Calculation($this);
401
402
        // Initialise worksheet collection and add one worksheet
403
        $this->workSheetCollection = [];
404 4
        $this->workSheetCollection[] = new Worksheet($this);
405
        $this->activeSheetIndex = 0;
406 4
407 4
        // Create document properties
408 4
        $this->properties = new Document\Properties();
409 4
410
        // Create document security
411 4
        $this->security = new Document\Security();
412 4
413 4
        // Set named ranges
414
        $this->namedRanges = [];
415
416
        // Create the cellXf supervisor
417
        $this->cellXfSupervisor = new Style(true);
418
        $this->cellXfSupervisor->bindParent($this);
419
420 172
        // Create the default style
421
        $this->addCellXf(new Style());
422 172
        $this->addCellStyleXf(new Style());
423
    }
424
425
    /**
426
     * Code to execute when this worksheet is unset().
427
     */
428
    public function __destruct()
429
    {
430 118
        $this->calculationEngine = null;
431
        $this->disconnectWorksheets();
432 118
    }
433
434
    /**
435
     * Disconnect all worksheets from this PhpSpreadsheet workbook object,
436
     * typically so that the PhpSpreadsheet object can be unset.
437
     */
438
    public function disconnectWorksheets()
439
    {
440
        $worksheet = null;
441
        foreach ($this->workSheetCollection as $k => &$worksheet) {
442
            $worksheet->disconnectCells();
443
            $this->workSheetCollection[$k] = null;
444
        }
445
        unset($worksheet);
446
        $this->workSheetCollection = [];
447
    }
448
449
    /**
450 73
     * Return the calculation engine for this worksheet.
451
     *
452 73
     * @return Calculation
453
     */
454
    public function getCalculationEngine()
455
    {
456
        return $this->calculationEngine;
457
    }
458
459
    /**
460
     * Get properties.
461
     *
462
     * @return Document\Properties
463
     */
464
    public function getProperties()
465
    {
466
        return $this->properties;
467
    }
468
469
    /**
470
     * Set properties.
471
     *
472 150
     * @param Document\Properties $pValue
473
     */
474 150
    public function setProperties(Document\Properties $pValue)
475
    {
476
        $this->properties = $pValue;
477
    }
478
479
    /**
480
     * Get security.
481
     *
482
     * @return Document\Security
483
     */
484
    public function getSecurity()
485
    {
486 78
        return $this->security;
487
    }
488 78
489 78
    /**
490
     * Set security.
491 78
     *
492
     * @param Document\Security $pValue
493
     */
494
    public function setSecurity(Document\Security $pValue)
495
    {
496
        $this->security = $pValue;
497
    }
498
499
    /**
500
     * Get active sheet.
501 172
     *
502
     * @throws Exception
503 172
     *
504
     * @return Worksheet
505
     */
506
    public function getActiveSheet()
507
    {
508
        return $this->getSheet($this->activeSheetIndex);
509
    }
510
511
    /**
512
     * Create sheet and add it to this workbook.
513
     *
514
     * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
515
     *
516 79
     * @throws Exception
517
     *
518 79
     * @return Worksheet
519
     */
520
    public function createSheet($sheetIndex = null)
521
    {
522
        $newSheet = new Worksheet($this);
523
        $this->addSheet($newSheet, $sheetIndex);
524 79
525 79
        return $newSheet;
526 54
    }
527
528 79
    /**
529
     * Check if a sheet with a specified name already exists.
530
     *
531
     * @param string $pSheetName Name of the worksheet to check
532
     *
533
     * @return bool
534
     */
535
    public function sheetNameExists($pSheetName)
536
    {
537
        return $this->getSheetByName($pSheetName) !== null;
538
    }
539
540
    /**
541
     * Add sheet.
542
     *
543
     * @param Worksheet $pSheet
544 79
     * @param null|int $iSheetIndex Index where sheet should go (0,1,..., or null for last)
545
     *
546
     * @throws Exception
547
     *
548 79
     * @return Worksheet
549
     */
550
    public function addSheet(Worksheet $pSheet, $iSheetIndex = null)
551
    {
552
        if ($this->sheetNameExists($pSheet->getTitle())) {
553
            throw new Exception(
554
                "Workbook already contains a worksheet named '{$pSheet->getTitle()}'. Rename this worksheet first."
555
            );
556
        }
557
558 54
        if ($iSheetIndex === null) {
559
            if ($this->activeSheetIndex < 0) {
560 54
                $this->activeSheetIndex = 0;
561 54
            }
562
            $this->workSheetCollection[] = $pSheet;
563
        } else {
564
            // Insert the sheet at the requested index
565
            array_splice(
566 54
                $this->workSheetCollection,
567
                $iSheetIndex,
568
                0,
569 54
                [$pSheet]
570 54
            );
571 54
572
            // Adjust active sheet index if necessary
573 54
            if ($this->activeSheetIndex >= $iSheetIndex) {
574
                ++$this->activeSheetIndex;
575
            }
576
        }
577
578
        if ($pSheet->getParent() === null) {
579
            $pSheet->rebindParent($this);
580
        }
581
582
        return $pSheet;
583
    }
584 163
585
    /**
586 163
     * Remove sheet by index.
587
     *
588
     * @param int $pIndex Active sheet index
589
     *
590
     * @throws Exception
591
     */
592
    public function removeSheetByIndex($pIndex)
593
    {
594 163
        $numSheets = count($this->workSheetCollection);
595
        if ($pIndex > $numSheets - 1) {
596
            throw new Exception(
597
                "You tried to remove a sheet by the out of bounds index: {$pIndex}. The actual number of sheets is {$numSheets}."
598
            );
599
        }
600
        array_splice($this->workSheetCollection, $pIndex, 1);
601
602 43
        // Adjust active sheet index if necessary
603
        if (($this->activeSheetIndex >= $pIndex) &&
604 43
            ($pIndex > count($this->workSheetCollection) - 1)) {
605
            --$this->activeSheetIndex;
606
        }
607
    }
608
609
    /**
610
     * Get sheet by index.
611
     *
612
     * @param int $pIndex Sheet index
613
     *
614 172
     * @throws Exception
615
     *
616 172
     * @return Worksheet
617 172
     */
618 89
    public function getSheet($pIndex)
619 71
    {
620
        if (!isset($this->workSheetCollection[$pIndex])) {
621
            $numSheets = $this->getSheetCount();
622
623 172
            throw new Exception(
624
                "Your requested sheet index: {$pIndex} is out of bounds. The actual number of sheets is {$numSheets}."
625
            );
626
        }
627
628
        return $this->workSheetCollection[$pIndex];
629
    }
630
631
    /**
632
     * Get all sheets.
633
     *
634
     * @return Worksheet[]
635 120
     */
636
    public function getAllSheets()
637 120
    {
638 120
        return $this->workSheetCollection;
639 120
    }
640
641
    /**
642
     * Get sheet by name.
643
     *
644
     * @param string $pName Sheet name
645
     *
646
     * @return Worksheet
647
     */
648
    public function getSheetByName($pName)
649
    {
650
        $worksheetCount = count($this->workSheetCollection);
651
        for ($i = 0; $i < $worksheetCount; ++$i) {
652
            if ($this->workSheetCollection[$i]->getTitle() === $pName) {
653
                return $this->workSheetCollection[$i];
654
            }
655
        }
656
657
        return null;
658
    }
659
660
    /**
661
     * Get index for sheet.
662
     *
663
     * @param Worksheet $pSheet
664
     *
665
     * @throws Exception
666
     *
667
     * @return int index
668
     */
669
    public function getIndex(Worksheet $pSheet)
670
    {
671
        foreach ($this->workSheetCollection as $key => $value) {
672
            if ($value->getHashCode() == $pSheet->getHashCode()) {
673
                return $key;
674
            }
675
        }
676
677
        throw new Exception('Sheet does not exist.');
678
    }
679 119
680
    /**
681 119
     * Set index for sheet by sheet name.
682
     *
683
     * @param string $sheetName Sheet name to modify index for
684
     * @param int $newIndex New index for the sheet
685
     *
686
     * @throws Exception
687
     *
688
     * @return int New sheet index
689 79
     */
690
    public function setIndexByName($sheetName, $newIndex)
691 79
    {
692
        $oldIndex = $this->getIndex($this->getSheetByName($sheetName));
693
        $pSheet = array_splice(
694
            $this->workSheetCollection,
695
            $oldIndex,
696
            1
697
        );
698
        array_splice(
699
            $this->workSheetCollection,
700
            $newIndex,
701
            0,
702
            $pSheet
703 120
        );
704
705 120
        return $newIndex;
706
    }
707 120
708
    /**
709
     * Get sheet count.
710
     *
711
     * @return int
712 120
     */
713
    public function getSheetCount()
714 120
    {
715
        return count($this->workSheetCollection);
716
    }
717
718
    /**
719
     * Get active sheet index.
720
     *
721
     * @return int Active sheet index
722
     */
723
    public function getActiveSheetIndex()
724
    {
725
        return $this->activeSheetIndex;
726 3
    }
727
728 3
    /**
729 3
     * Set active sheet index.
730
     *
731 3
     * @param int $pIndex Active sheet index
732
     *
733
     * @throws Exception
734
     *
735
     * @return Worksheet
736
     */
737
    public function setActiveSheetIndex($pIndex)
738
    {
739
        $numSheets = count($this->workSheetCollection);
740
741
        if ($pIndex > $numSheets - 1) {
742 8
            throw new Exception(
743
                "You tried to set a sheet active by the out of bounds index: {$pIndex}. The actual number of sheets is {$numSheets}."
744 8
            );
745 8
        }
746 8
        $this->activeSheetIndex = $pIndex;
747 8
748
        return $this->getActiveSheet();
749
    }
750 8
751
    /**
752
     * Set active sheet index by name.
753
     *
754
     * @param string $pValue Sheet title
755
     *
756
     * @throws Exception
757
     *
758
     * @return Worksheet
759
     */
760
    public function setActiveSheetIndexByName($pValue)
761
    {
762
        if (($worksheet = $this->getSheetByName($pValue)) instanceof Worksheet) {
0 ignored issues
show
introduced by
$worksheet = $this->getSheetByName($pValue) is always a sub-type of PhpOffice\PhpSpreadsheet\Worksheet\Worksheet.
Loading history...
763 1
            $this->setActiveSheetIndex($this->getIndex($worksheet));
764
765 1
            return $worksheet;
766
        }
767
768
        throw new Exception('Workbook does not contain sheet:' . $pValue);
769
    }
770 1
771
    /**
772
     * Get sheet names.
773 1
     *
774 1
     * @return string[]
775
     */
776
    public function getSheetNames()
777
    {
778 1
        $returnValue = [];
779
        $worksheetCount = $this->getSheetCount();
780
        for ($i = 0; $i < $worksheetCount; ++$i) {
781 1
            $returnValue[] = $this->getSheet($i)->getTitle();
782 1
        }
783 1
784
        return $returnValue;
785
    }
786 1
787
    /**
788
     * Add external sheet.
789
     *
790
     * @param Worksheet $pSheet External sheet to add
791
     * @param null|int $iSheetIndex Index where sheet should go (0,1,..., or null for last)
792
     *
793
     * @throws Exception
794 83
     *
795
     * @return Worksheet
796 83
     */
797
    public function addExternalSheet(Worksheet $pSheet, $iSheetIndex = null)
798
    {
799
        if ($this->sheetNameExists($pSheet->getTitle())) {
800
            throw new Exception("Workbook already contains a worksheet named '{$pSheet->getTitle()}'. Rename the external sheet first.");
801
        }
802
803
        // count how many cellXfs there are in this workbook currently, we will need this below
804
        $countCellXfs = count($this->cellXfCollection);
805
806 4
        // copy all the shared cellXfs from the external workbook and append them to the current
807
        foreach ($pSheet->getParent()->getCellXfCollection() as $cellXf) {
808 4
            $this->addCellXf(clone $cellXf);
809
        }
810 4
811
        // move sheet to this workbook
812
        $pSheet->rebindParent($this);
813
814
        // update the cellXfs
815
        foreach ($pSheet->getCoordinates(false) as $coordinate) {
816 4
            $cell = $pSheet->getCell($coordinate);
817
            $cell->setXfIndex($cell->getXfIndex() + $countCellXfs);
818
        }
819
820
        return $this->addSheet($pSheet, $iSheetIndex);
821
    }
822
823
    /**
824
     * Get named ranges.
825
     *
826
     * @return NamedRange[]
827 5
     */
828
    public function getNamedRanges()
829 5
    {
830
        return $this->namedRanges;
831 5
    }
832
833 5
    /**
834 2
     * Add named range.
835
     *
836
     * @param NamedRange $namedRange
837
     *
838 5
     * @return bool
839
     */
840
    public function addNamedRange(NamedRange $namedRange)
841
    {
842
        if ($namedRange->getScope() == null) {
843 5
            // global scope
844
            $this->namedRanges[$namedRange->getName()] = $namedRange;
845
        } else {
846
            // local scope
847
            $this->namedRanges[$namedRange->getScope()->getTitle() . '!' . $namedRange->getName()] = $namedRange;
848
        }
849
850
        return true;
851
    }
852
853
    /**
854 1
     * Get named range.
855
     *
856 1
     * @param string $namedRange
857
     * @param null|Worksheet $pSheet Scope. Use null for global scope
858
     *
859
     * @return null|NamedRange
860
     */
861 1
    public function getNamedRange($namedRange, Worksheet $pSheet = null)
862
    {
863
        $returnValue = null;
864
865
        if ($namedRange != '' && ($namedRange !== null)) {
866 1
            // first look for global defined name
867
            if (isset($this->namedRanges[$namedRange])) {
868
                $returnValue = $this->namedRanges[$namedRange];
869
            }
870
871
            // then look for local defined name (has priority over global defined name if both names exist)
872
            if (($pSheet !== null) && isset($this->namedRanges[$pSheet->getTitle() . '!' . $namedRange])) {
873
                $returnValue = $this->namedRanges[$pSheet->getTitle() . '!' . $namedRange];
874 98
            }
875
        }
876 98
877
        return $returnValue;
878
    }
879
880
    /**
881
     * Remove named range.
882
     *
883
     * @param string $namedRange
884
     * @param null|Worksheet $pSheet scope: use null for global scope
885
     *
886
     * @return Spreadsheet
887
     */
888
    public function removeNamedRange($namedRange, Worksheet $pSheet = null)
889
    {
890
        if ($pSheet === null) {
891
            if (isset($this->namedRanges[$namedRange])) {
892
                unset($this->namedRanges[$namedRange]);
893
            }
894
        } else {
895
            if (isset($this->namedRanges[$pSheet->getTitle() . '!' . $namedRange])) {
896
                unset($this->namedRanges[$pSheet->getTitle() . '!' . $namedRange]);
897
            }
898
        }
899
900
        return $this;
901
    }
902
903
    /**
904
     * Get worksheet iterator.
905
     *
906
     * @return Iterator
907
     */
908
    public function getWorksheetIterator()
909
    {
910
        return new Iterator($this);
911
    }
912
913
    /**
914 107
     * Copy workbook (!= clone!).
915
     *
916 107
     * @return Spreadsheet
917
     */
918
    public function copy()
919
    {
920
        $copied = clone $this;
921
922
        $worksheetCount = count($this->workSheetCollection);
923
        for ($i = 0; $i < $worksheetCount; ++$i) {
924
            $this->workSheetCollection[$i] = $this->workSheetCollection[$i]->copy();
925
            $this->workSheetCollection[$i]->rebindParent($this);
926 80
        }
927
928 80
        return $copied;
929
    }
930
931
    /**
932
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
933
     */
934
    public function __clone()
935
    {
936
        foreach ($this as $key => $val) {
937
            if (is_object($val) || (is_array($val))) {
938 50
                $this->{$key} = unserialize(serialize($val));
939
            }
940 50
        }
941 50
    }
942 50
943
    /**
944
     * Get the workbook collection of cellXfs.
945
     *
946 48
     * @return Style[]
947
     */
948
    public function getCellXfCollection()
949
    {
950
        return $this->cellXfCollection;
951
    }
952
953
    /**
954
     * Get cellXf by index.
955
     *
956
     * @param int $pIndex
957
     *
958
     * @return Style
959
     */
960
    public function getCellXfByIndex($pIndex)
961
    {
962
        return $this->cellXfCollection[$pIndex];
963
    }
964
965
    /**
966
     * Get cellXf by hash code.
967
     *
968 95
     * @param string $pValue
969
     *
970 95
     * @return false|Style
971 95
     */
972
    public function getCellXfByHashCode($pValue)
973
    {
974
        foreach ($this->cellXfCollection as $cellXf) {
975
            if ($cellXf->getHashCode() == $pValue) {
976
                return $cellXf;
977
            }
978
        }
979
980
        return false;
981
    }
982 172
983
    /**
984 172
     * Check if style exists in style collection.
985 172
     *
986 172
     * @param Style $pCellStyle
987
     *
988
     * @return bool
989
     */
990
    public function cellXfExists($pCellStyle)
991
    {
992
        return in_array($pCellStyle, $this->cellXfCollection, true);
993
    }
994
995 50
    /**
996
     * Get default style.
997 50
     *
998
     * @throws Exception
999
     *
1000
     * @return Style
1001
     */
1002 50
    public function getDefaultStyle()
1003
    {
1004
        if (isset($this->cellXfCollection[0])) {
1005 50
            return $this->cellXfCollection[0];
1006
        }
1007
1008
        throw new Exception('No default style found for this workbook');
1009
    }
1010
1011
    /**
1012
     * Add a cellXf to the workbook.
1013
     *
1014
     * @param Style $style
1015
     */
1016
    public function addCellXf(Style $style)
1017
    {
1018 50
        $this->cellXfCollection[] = $style;
1019
        $style->setIndex(count($this->cellXfCollection) - 1);
1020
    }
1021
1022
    /**
1023
     * Remove cellXf by index. It is ensured that all cells get their xf index updated.
1024
     *
1025 49
     * @param int $pIndex Index to cellXf
1026
     *
1027 49
     * @throws Exception
1028
     */
1029
    public function removeCellXfByIndex($pIndex)
1030
    {
1031
        if ($pIndex > count($this->cellXfCollection) - 1) {
1032
            throw new Exception('CellXf index is out of bounds.');
1033
        }
1034
1035 43
        // first remove the cellXf
1036
        array_splice($this->cellXfCollection, $pIndex, 1);
1037 43
1038
        // then update cellXf indexes for cells
1039
        foreach ($this->workSheetCollection as $worksheet) {
1040
            foreach ($worksheet->getCoordinates(false) as $coordinate) {
1041
                $cell = $worksheet->getCell($coordinate);
1042
                $xfIndex = $cell->getXfIndex();
1043
                if ($xfIndex > $pIndex) {
1044
                    // decrease xf index by 1
1045
                    $cell->setXfIndex($xfIndex - 1);
1046
                } elseif ($xfIndex == $pIndex) {
1047
                    // set to default xf index 0
1048
                    $cell->setXfIndex(0);
1049
                }
1050
            }
1051
        }
1052
    }
1053
1054
    /**
1055
     * Get the cellXf supervisor.
1056
     *
1057
     * @return Style
1058
     */
1059
    public function getCellXfSupervisor()
1060
    {
1061
        return $this->cellXfSupervisor;
1062
    }
1063
1064
    /**
1065
     * Get the workbook collection of cellStyleXfs.
1066
     *
1067
     * @return Style[]
1068
     */
1069
    public function getCellStyleXfCollection()
1070
    {
1071
        return $this->cellStyleXfCollection;
1072
    }
1073
1074
    /**
1075 172
     * Get cellStyleXf by index.
1076
     *
1077 172
     * @param int $pIndex Index to cellXf
1078 172
     *
1079 172
     * @return Style
1080
     */
1081
    public function getCellStyleXfByIndex($pIndex)
1082
    {
1083
        return $this->cellStyleXfCollection[$pIndex];
1084
    }
1085
1086
    /**
1087
     * Get cellStyleXf by hash code.
1088 50
     *
1089
     * @param string $pValue
1090 50
     *
1091
     * @return false|Style
1092
     */
1093 50
    public function getCellStyleXfByHashCode($pValue)
1094 50
    {
1095
        foreach ($this->cellStyleXfCollection as $cellStyleXf) {
1096
            if ($cellStyleXf->getHashCode() == $pValue) {
1097
                return $cellStyleXf;
1098
            }
1099
        }
1100 92
1101
        return false;
1102
    }
1103 92
1104 92
    /**
1105 92
     * Add a cellStyleXf to the workbook.
1106
     *
1107
     * @param Style $pStyle
1108 92
     */
1109
    public function addCellStyleXf(Style $pStyle)
1110 92
    {
1111 85
        $this->cellStyleXfCollection[] = $pStyle;
1112 85
        $pStyle->setIndex(count($this->cellStyleXfCollection) - 1);
1113
    }
1114
1115
    /**
1116 92
     * Remove cellStyleXf by index.
1117 39
     *
1118 39
     * @param int $pIndex Index to cellXf
1119
     *
1120
     * @throws Exception
1121
     */
1122
    public function removeCellStyleXfByIndex($pIndex)
1123 92
    {
1124 92
        if ($pIndex > count($this->cellStyleXfCollection) - 1) {
1125
            throw new Exception('CellStyleXf index is out of bounds.');
1126
        }
1127
        array_splice($this->cellStyleXfCollection, $pIndex, 1);
1128
    }
1129
1130 92
    /**
1131 92
     * Eliminate all unneeded cellXf and afterwards update the xfIndex for all cells
1132 92
     * and columns in the workbook.
1133 92
     */
1134
    public function garbageCollect()
1135 15
    {
1136
        // how many references are there to each cellXf ?
1137 92
        $countReferencesCellXf = [];
1138
        foreach ($this->cellXfCollection as $index => $cellXf) {
1139 92
            $countReferencesCellXf[$index] = 0;
1140
        }
1141
1142 92
        foreach ($this->getWorksheetIterator() as $sheet) {
1143 92
            // from cells
1144
            foreach ($sheet->getCoordinates(false) as $coordinate) {
1145
                $cell = $sheet->getCell($coordinate);
1146
                ++$countReferencesCellXf[$cell->getXfIndex()];
1147 92
            }
1148
1149
            // from row dimensions
1150
            foreach ($sheet->getRowDimensions() as $rowDimension) {
1151
                if ($rowDimension->getXfIndex() !== null) {
1152 92
                    ++$countReferencesCellXf[$rowDimension->getXfIndex()];
1153
                }
1154 92
            }
1155 85
1156 85
            // from column dimensions
1157
            foreach ($sheet->getColumnDimensions() as $columnDimension) {
1158
                ++$countReferencesCellXf[$columnDimension->getXfIndex()];
1159
            }
1160 92
        }
1161 39
1162 39
        // remove cellXfs without references and create mapping so we can update xfIndex
1163
        // for all cells and columns
1164
        $countNeededCellXfs = 0;
1165
        foreach ($this->cellXfCollection as $index => $cellXf) {
1166
            if ($countReferencesCellXf[$index] > 0 || $index == 0) { // we must never remove the first cellXf
1167 92
                ++$countNeededCellXfs;
1168 26
            } else {
1169
                unset($this->cellXfCollection[$index]);
1170
            }
1171
            $map[$index] = $countNeededCellXfs - 1;
1172 92
        }
1173
        $this->cellXfCollection = array_values($this->cellXfCollection);
1174 92
1175
        // update the index for all cellXfs
1176
        foreach ($this->cellXfCollection as $i => $cellXf) {
1177
            $cellXf->setIndex($i);
1178
        }
1179
1180
        // make sure there is always at least one cellXf (there should be)
1181
        if (empty($this->cellXfCollection)) {
1182
            $this->cellXfCollection[] = new Style();
1183
        }
1184
1185
        // update the xfIndex for all cells, row dimensions, column dimensions
1186
        foreach ($this->getWorksheetIterator() as $sheet) {
1187
            // for all cells
1188
            foreach ($sheet->getCoordinates(false) as $coordinate) {
1189
                $cell = $sheet->getCell($coordinate);
1190
                $cell->setXfIndex($map[$cell->getXfIndex()]);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $map seems to be defined by a foreach iteration on line 1165. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
1191
            }
1192
1193
            // for all row dimensions
1194
            foreach ($sheet->getRowDimensions() as $rowDimension) {
1195
                if ($rowDimension->getXfIndex() !== null) {
1196
                    $rowDimension->setXfIndex($map[$rowDimension->getXfIndex()]);
1197
                }
1198
            }
1199
1200
            // for all column dimensions
1201
            foreach ($sheet->getColumnDimensions() as $columnDimension) {
1202
                $columnDimension->setXfIndex($map[$columnDimension->getXfIndex()]);
1203
            }
1204
1205
            // also do garbage collection for all the sheets
1206
            $sheet->garbageCollect();
1207
        }
1208
    }
1209
1210
    /**
1211
     * Return the unique ID value assigned to this spreadsheet workbook.
1212
     *
1213
     * @return string
1214
     */
1215
    public function getID()
1216
    {
1217
        return $this->uniqueID;
1218
    }
1219
}
1220