Failed Conditions
Push — develop ( eb5856...11b055 )
by Adrien
31:22
created

Worksheet::getTopLeftCell()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
ccs 2
cts 2
cp 1
crap 1
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Worksheet;
4
5
use ArrayObject;
6
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
7
use PhpOffice\PhpSpreadsheet\Cell\Cell;
8
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
9
use PhpOffice\PhpSpreadsheet\Cell\DataType;
10
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
11
use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
12
use PhpOffice\PhpSpreadsheet\Chart\Chart;
13
use PhpOffice\PhpSpreadsheet\Collection\Cells;
14
use PhpOffice\PhpSpreadsheet\Collection\CellsFactory;
15
use PhpOffice\PhpSpreadsheet\Comment;
16
use PhpOffice\PhpSpreadsheet\Exception;
17
use PhpOffice\PhpSpreadsheet\IComparable;
18
use PhpOffice\PhpSpreadsheet\NamedRange;
19
use PhpOffice\PhpSpreadsheet\ReferenceHelper;
20
use PhpOffice\PhpSpreadsheet\RichText\RichText;
21
use PhpOffice\PhpSpreadsheet\Shared;
22
use PhpOffice\PhpSpreadsheet\Spreadsheet;
23
use PhpOffice\PhpSpreadsheet\Style\Color;
24
use PhpOffice\PhpSpreadsheet\Style\Conditional;
25
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
26
use PhpOffice\PhpSpreadsheet\Style\Style;
27
28
class Worksheet implements IComparable
29
{
30
    // Break types
31
    const BREAK_NONE = 0;
32
    const BREAK_ROW = 1;
33
    const BREAK_COLUMN = 2;
34
35
    // Sheet state
36
    const SHEETSTATE_VISIBLE = 'visible';
37
    const SHEETSTATE_HIDDEN = 'hidden';
38
    const SHEETSTATE_VERYHIDDEN = 'veryHidden';
39
40
    /**
41
     * Invalid characters in sheet title.
42
     *
43
     * @var array
44
     */
45
    private static $invalidCharacters = ['*', ':', '/', '\\', '?', '[', ']'];
46
47
    /**
48
     * Parent spreadsheet.
49
     *
50
     * @var Spreadsheet
51
     */
52
    private $parent;
53
54
    /**
55
     * Collection of cells.
56
     *
57
     * @var Cells
58
     */
59
    private $cellCollection;
60
61
    /**
62
     * Collection of row dimensions.
63
     *
64
     * @var RowDimension[]
65
     */
66
    private $rowDimensions = [];
67
68
    /**
69
     * Default row dimension.
70
     *
71
     * @var RowDimension
72
     */
73
    private $defaultRowDimension;
74
75
    /**
76
     * Collection of column dimensions.
77
     *
78
     * @var ColumnDimension[]
79
     */
80
    private $columnDimensions = [];
81
82
    /**
83
     * Default column dimension.
84
     *
85
     * @var ColumnDimension
86
     */
87
    private $defaultColumnDimension;
88
89
    /**
90
     * Collection of drawings.
91
     *
92
     * @var BaseDrawing[]
93
     */
94
    private $drawingCollection;
95
96
    /**
97
     * Collection of Chart objects.
98
     *
99
     * @var Chart[]
100
     */
101
    private $chartCollection = [];
102
103
    /**
104
     * Worksheet title.
105
     *
106
     * @var string
107
     */
108
    private $title;
109
110
    /**
111
     * Sheet state.
112
     *
113
     * @var string
114
     */
115
    private $sheetState;
116
117
    /**
118
     * Page setup.
119
     *
120
     * @var PageSetup
121
     */
122
    private $pageSetup;
123
124
    /**
125
     * Page margins.
126
     *
127
     * @var PageMargins
128
     */
129
    private $pageMargins;
130
131
    /**
132
     * Page header/footer.
133
     *
134
     * @var HeaderFooter
135
     */
136
    private $headerFooter;
137
138
    /**
139
     * Sheet view.
140
     *
141
     * @var SheetView
142
     */
143
    private $sheetView;
144
145
    /**
146
     * Protection.
147
     *
148
     * @var Protection
149
     */
150
    private $protection;
151
152
    /**
153
     * Collection of styles.
154
     *
155
     * @var Style[]
156
     */
157
    private $styles = [];
158
159
    /**
160
     * Conditional styles. Indexed by cell coordinate, e.g. 'A1'.
161
     *
162
     * @var array
163
     */
164
    private $conditionalStylesCollection = [];
165
166
    /**
167
     * Is the current cell collection sorted already?
168
     *
169
     * @var bool
170
     */
171
    private $cellCollectionIsSorted = false;
172
173
    /**
174
     * Collection of breaks.
175
     *
176
     * @var array
177
     */
178
    private $breaks = [];
179
180
    /**
181
     * Collection of merged cell ranges.
182
     *
183
     * @var array
184
     */
185
    private $mergeCells = [];
186
187
    /**
188
     * Collection of protected cell ranges.
189
     *
190
     * @var array
191
     */
192
    private $protectedCells = [];
193
194
    /**
195
     * Autofilter Range and selection.
196
     *
197
     * @var AutoFilter
198
     */
199
    private $autoFilter;
200
201
    /**
202
     * Freeze pane.
203
     *
204
     * @var null|string
205
     */
206
    private $freezePane;
207
208
    /**
209
     * Default position of the right bottom pane.
210
     *
211
     * @var null|string
212
     */
213
    private $topLeftCell;
214
215
    /**
216
     * Show gridlines?
217
     *
218
     * @var bool
219
     */
220
    private $showGridlines = true;
221
222
    /**
223
     * Print gridlines?
224
     *
225
     * @var bool
226
     */
227
    private $printGridlines = false;
228
229
    /**
230
     * Show row and column headers?
231
     *
232
     * @var bool
233
     */
234
    private $showRowColHeaders = true;
235
236
    /**
237
     * Show summary below? (Row/Column outline).
238
     *
239
     * @var bool
240
     */
241
    private $showSummaryBelow = true;
242
243
    /**
244
     * Show summary right? (Row/Column outline).
245
     *
246
     * @var bool
247
     */
248
    private $showSummaryRight = true;
249
250
    /**
251
     * Collection of comments.
252
     *
253
     * @var Comment[]
254
     */
255
    private $comments = [];
256
257
    /**
258
     * Active cell. (Only one!).
259
     *
260
     * @var string
261
     */
262
    private $activeCell = 'A1';
263
264
    /**
265
     * Selected cells.
266
     *
267
     * @var string
268
     */
269
    private $selectedCells = 'A1';
270
271
    /**
272
     * Cached highest column.
273
     *
274
     * @var string
275
     */
276
    private $cachedHighestColumn = 'A';
277
278
    /**
279
     * Cached highest row.
280
     *
281
     * @var int
282
     */
283
    private $cachedHighestRow = 1;
284
285
    /**
286
     * Right-to-left?
287
     *
288
     * @var bool
289
     */
290
    private $rightToLeft = false;
291
292
    /**
293
     * Hyperlinks. Indexed by cell coordinate, e.g. 'A1'.
294
     *
295
     * @var array
296
     */
297
    private $hyperlinkCollection = [];
298
299
    /**
300
     * Data validation objects. Indexed by cell coordinate, e.g. 'A1'.
301
     *
302
     * @var array
303
     */
304
    private $dataValidationCollection = [];
305
306
    /**
307
     * Tab color.
308
     *
309
     * @var Color
310
     */
311
    private $tabColor;
312
313
    /**
314
     * Dirty flag.
315
     *
316
     * @var bool
317
     */
318
    private $dirty = true;
319
320
    /**
321
     * Hash.
322
     *
323
     * @var string
324
     */
325
    private $hash;
326
327
    /**
328
     * CodeName.
329
     *
330
     * @var string
331
     */
332
    private $codeName;
333
334
    /**
335
     * Create a new worksheet.
336
     *
337
     * @param Spreadsheet $parent
338
     * @param string $pTitle
339
     */
340 155
    public function __construct(Spreadsheet $parent = null, $pTitle = 'Worksheet')
341
    {
342
        // Set parent and title
343 155
        $this->parent = $parent;
344 155
        $this->setTitle($pTitle, false);
345
        // setTitle can change $pTitle
346 155
        $this->setCodeName($this->getTitle());
347 155
        $this->setSheetState(self::SHEETSTATE_VISIBLE);
348
349 155
        $this->cellCollection = CellsFactory::getInstance($this);
350
        // Set page setup
351 155
        $this->pageSetup = new PageSetup();
352
        // Set page margins
353 155
        $this->pageMargins = new PageMargins();
354
        // Set page header/footer
355 155
        $this->headerFooter = new HeaderFooter();
356
        // Set sheet view
357 155
        $this->sheetView = new SheetView();
358
        // Drawing collection
359 155
        $this->drawingCollection = new \ArrayObject();
0 ignored issues
show
Documentation Bug introduced by
It seems like new ArrayObject() of type ArrayObject is incompatible with the declared type PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing[] of property $drawingCollection.

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...
360
        // Chart collection
361 155
        $this->chartCollection = new \ArrayObject();
0 ignored issues
show
Documentation Bug introduced by
It seems like new ArrayObject() of type ArrayObject is incompatible with the declared type PhpOffice\PhpSpreadsheet\Chart\Chart[] of property $chartCollection.

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...
362
        // Protection
363 155
        $this->protection = new Protection();
364
        // Default row dimension
365 155
        $this->defaultRowDimension = new RowDimension(null);
366
        // Default column dimension
367 155
        $this->defaultColumnDimension = new ColumnDimension(null);
368 155
        $this->autoFilter = new AutoFilter(null, $this);
369 155
    }
370
371
    /**
372
     * Disconnect all cells from this Worksheet object,
373
     * typically so that the worksheet object can be unset.
374
     */
375 2
    public function disconnectCells()
376
    {
377 2
        if ($this->cellCollection !== null) {
378 2
            $this->cellCollection->unsetWorksheetCells();
379 2
            $this->cellCollection = null;
380
        }
381
        //    detach ourself from the workbook, so that it can then delete this worksheet successfully
382 2
        $this->parent = null;
383 2
    }
384
385
    /**
386
     * Code to execute when this worksheet is unset().
387
     */
388 2
    public function __destruct()
389
    {
390 2
        Calculation::getInstance($this->parent)->clearCalculationCacheForWorksheet($this->title);
391
392 2
        $this->disconnectCells();
393 2
    }
394
395
    /**
396
     * Return the cell collection.
397
     *
398
     * @return Cells
399
     */
400 117
    public function getCellCollection()
401
    {
402 117
        return $this->cellCollection;
403
    }
404
405
    /**
406
     * Get array of invalid characters for sheet title.
407
     *
408
     * @return array
409
     */
410
    public static function getInvalidCharacters()
411
    {
412
        return self::$invalidCharacters;
413
    }
414
415
    /**
416
     * Check sheet code name for valid Excel syntax.
417
     *
418
     * @param string $pValue The string to check
419
     *
420
     * @throws Exception
421
     *
422
     * @return string The valid string
423
     */
424 155
    private static function checkSheetCodeName($pValue)
425
    {
426 155
        $CharCount = Shared\StringHelper::countCharacters($pValue);
427 155
        if ($CharCount == 0) {
428
            throw new Exception('Sheet code name cannot be empty.');
429
        }
430
        // Some of the printable ASCII characters are invalid:  * : / \ ? [ ] and  first and last characters cannot be a "'"
431 155
        if ((str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) ||
432 155
            (Shared\StringHelper::substring($pValue, -1, 1) == '\'') ||
433 155
            (Shared\StringHelper::substring($pValue, 0, 1) == '\'')) {
434 1
            throw new Exception('Invalid character found in sheet code name');
435
        }
436
437
        // Maximum 31 characters allowed for sheet title
438 155
        if ($CharCount > 31) {
439 1
            throw new Exception('Maximum 31 characters allowed in sheet code name.');
440
        }
441
442 155
        return $pValue;
443
    }
444
445
    /**
446
     * Check sheet title for valid Excel syntax.
447
     *
448
     * @param string $pValue The string to check
449
     *
450
     * @throws Exception
451
     *
452
     * @return string The valid string
453
     */
454 155
    private static function checkSheetTitle($pValue)
455
    {
456
        // Some of the printable ASCII characters are invalid:  * : / \ ? [ ]
457 155
        if (str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) {
458 1
            throw new Exception('Invalid character found in sheet title');
459
        }
460
461
        // Maximum 31 characters allowed for sheet title
462 155
        if (Shared\StringHelper::countCharacters($pValue) > 31) {
463 1
            throw new Exception('Maximum 31 characters allowed in sheet title.');
464
        }
465
466 155
        return $pValue;
467
    }
468
469
    /**
470
     * Get a sorted list of all cell coordinates currently held in the collection by row and column.
471
     *
472
     * @param bool $sorted Also sort the cell collection?
473
     *
474
     * @return string[]
475
     */
476 70
    public function getCoordinates($sorted = true)
477
    {
478 70
        if ($this->cellCollection == null) {
479
            return [];
480
        }
481
482 70
        if ($sorted) {
483 65
            return $this->cellCollection->getSortedCoordinates();
484
        }
485
486 69
        return $this->cellCollection->getCoordinates();
487
    }
488
489
    /**
490
     * Get collection of row dimensions.
491
     *
492
     * @return RowDimension[]
493
     */
494 64
    public function getRowDimensions()
495
    {
496 64
        return $this->rowDimensions;
497
    }
498
499
    /**
500
     * Get default row dimension.
501
     *
502
     * @return RowDimension
503
     */
504 79
    public function getDefaultRowDimension()
505
    {
506 79
        return $this->defaultRowDimension;
507
    }
508
509
    /**
510
     * Get collection of column dimensions.
511
     *
512
     * @return ColumnDimension[]
513
     */
514 64
    public function getColumnDimensions()
515
    {
516 64
        return $this->columnDimensions;
517
    }
518
519
    /**
520
     * Get default column dimension.
521
     *
522
     * @return ColumnDimension
523
     */
524 61
    public function getDefaultColumnDimension()
525
    {
526 61
        return $this->defaultColumnDimension;
527
    }
528
529
    /**
530
     * Get collection of drawings.
531
     *
532
     * @return BaseDrawing[]
533
     */
534 65
    public function getDrawingCollection()
535
    {
536 65
        return $this->drawingCollection;
537
    }
538
539
    /**
540
     * Get collection of charts.
541
     *
542
     * @return Chart[]
543
     */
544 14
    public function getChartCollection()
545
    {
546 14
        return $this->chartCollection;
547
    }
548
549
    /**
550
     * Add chart.
551
     *
552
     * @param Chart $pChart
553
     * @param null|int $iChartIndex Index where chart should go (0,1,..., or null for last)
554
     *
555
     * @return Chart
556
     */
557 15
    public function addChart(Chart $pChart, $iChartIndex = null)
558
    {
559 15
        $pChart->setWorksheet($this);
560 15
        if ($iChartIndex === null) {
561 15
            $this->chartCollection[] = $pChart;
562
        } else {
563
            // Insert the chart at the requested index
564
            array_splice($this->chartCollection, $iChartIndex, 0, [$pChart]);
565
        }
566
567 15
        return $pChart;
568
    }
569
570
    /**
571
     * Return the count of charts on this worksheet.
572
     *
573
     * @return int The number of charts
574
     */
575 14
    public function getChartCount()
576
    {
577 14
        return count($this->chartCollection);
578
    }
579
580
    /**
581
     * Get a chart by its index position.
582
     *
583
     * @param string $index Chart index position
584
     *
585
     * @throws Exception
586
     *
587
     * @return Chart|false
588
     */
589 13
    public function getChartByIndex($index)
590
    {
591 13
        $chartCount = count($this->chartCollection);
592 13
        if ($chartCount == 0) {
593
            return false;
594
        }
595 13
        if ($index === null) {
596
            $index = --$chartCount;
597
        }
598 13
        if (!isset($this->chartCollection[$index])) {
599
            return false;
600
        }
601
602 13
        return $this->chartCollection[$index];
603
    }
604
605
    /**
606
     * Return an array of the names of charts on this worksheet.
607
     *
608
     * @throws Exception
609
     *
610
     * @return string[] The names of charts
611
     */
612 2
    public function getChartNames()
613
    {
614 2
        $chartNames = [];
615 2
        foreach ($this->chartCollection as $chart) {
616 2
            $chartNames[] = $chart->getName();
617
        }
618
619 2
        return $chartNames;
620
    }
621
622
    /**
623
     * Get a chart by name.
624
     *
625
     * @param string $chartName Chart name
626
     *
627
     * @throws Exception
628
     *
629
     * @return Chart|false
630
     */
631 2
    public function getChartByName($chartName)
632
    {
633 2
        $chartCount = count($this->chartCollection);
634 2
        if ($chartCount == 0) {
635
            return false;
636
        }
637 2
        foreach ($this->chartCollection as $index => $chart) {
638 2
            if ($chart->getName() == $chartName) {
639 2
                return $this->chartCollection[$index];
640
            }
641
        }
642
643
        return false;
644
    }
645
646
    /**
647
     * Refresh column dimensions.
648
     *
649
     * @return Worksheet
650
     */
651 15
    public function refreshColumnDimensions()
652
    {
653 15
        $currentColumnDimensions = $this->getColumnDimensions();
654 15
        $newColumnDimensions = [];
655
656 15
        foreach ($currentColumnDimensions as $objColumnDimension) {
657 15
            $newColumnDimensions[$objColumnDimension->getColumnIndex()] = $objColumnDimension;
658
        }
659
660 15
        $this->columnDimensions = $newColumnDimensions;
661
662 15
        return $this;
663
    }
664
665
    /**
666
     * Refresh row dimensions.
667
     *
668
     * @return Worksheet
669
     */
670 2
    public function refreshRowDimensions()
671
    {
672 2
        $currentRowDimensions = $this->getRowDimensions();
673 2
        $newRowDimensions = [];
674
675 2
        foreach ($currentRowDimensions as $objRowDimension) {
676 2
            $newRowDimensions[$objRowDimension->getRowIndex()] = $objRowDimension;
677
        }
678
679 2
        $this->rowDimensions = $newRowDimensions;
680
681 2
        return $this;
682
    }
683
684
    /**
685
     * Calculate worksheet dimension.
686
     *
687
     * @return string String containing the dimension of this worksheet
688
     */
689 62
    public function calculateWorksheetDimension()
690
    {
691
        // Return
692 62
        return 'A1' . ':' . $this->getHighestColumn() . $this->getHighestRow();
693
    }
694
695
    /**
696
     * Calculate worksheet data dimension.
697
     *
698
     * @return string String containing the dimension of this worksheet that actually contain data
699
     */
700
    public function calculateWorksheetDataDimension()
701
    {
702
        // Return
703
        return 'A1' . ':' . $this->getHighestDataColumn() . $this->getHighestDataRow();
704
    }
705
706
    /**
707
     * Calculate widths for auto-size columns.
708
     *
709
     * @return Worksheet;
710
     */
711 48
    public function calculateColumnWidths()
712
    {
713
        // initialize $autoSizes array
714 48
        $autoSizes = [];
715 48
        foreach ($this->getColumnDimensions() as $colDimension) {
716 26
            if ($colDimension->getAutoSize()) {
717 26
                $autoSizes[$colDimension->getColumnIndex()] = -1;
718
            }
719
        }
720
721
        // There is only something to do if there are some auto-size columns
722 48
        if (!empty($autoSizes)) {
723
            // build list of cells references that participate in a merge
724 14
            $isMergeCell = [];
725 14
            foreach ($this->getMergeCells() as $cells) {
726 11
                foreach (Coordinate::extractAllCellReferencesInRange($cells) as $cellReference) {
0 ignored issues
show
Bug introduced by
$cells of type array is incompatible with the type string expected by parameter $pRange of PhpOffice\PhpSpreadsheet...CellReferencesInRange(). ( Ignorable by Annotation )

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

726
                foreach (Coordinate::extractAllCellReferencesInRange(/** @scrutinizer ignore-type */ $cells) as $cellReference) {
Loading history...
727 11
                    $isMergeCell[$cellReference] = true;
728
                }
729
            }
730
731
            // loop through all cells in the worksheet
732 14
            foreach ($this->getCoordinates(false) as $coordinate) {
733 14
                $cell = $this->getCell($coordinate, false);
734 14
                if ($cell !== null && isset($autoSizes[$this->cellCollection->getCurrentColumn()])) {
735
                    //Determine if cell is in merge range
736 14
                    $isMerged = isset($isMergeCell[$this->cellCollection->getCurrentCoordinate()]);
737
738
                    //By default merged cells should be ignored
739 14
                    $isMergedButProceed = false;
740
741
                    //The only exception is if it's a merge range value cell of a 'vertical' randge (1 column wide)
742 14
                    if ($isMerged && $cell->isMergeRangeValueCell()) {
743
                        $range = $cell->getMergeRange();
744
                        $rangeBoundaries = Coordinate::rangeDimension($range);
745
                        if ($rangeBoundaries[0] == 1) {
746
                            $isMergedButProceed = true;
747
                        }
748
                    }
749
750
                    // Determine width if cell does not participate in a merge or does and is a value cell of 1-column wide range
751 14
                    if (!$isMerged || $isMergedButProceed) {
752
                        // Calculated value
753
                        // To formatted string
754 14
                        $cellValue = NumberFormat::toFormattedString(
755 14
                            $cell->getCalculatedValue(),
756 14
                            $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode()
757
                        );
758
759 14
                        $autoSizes[$this->cellCollection->getCurrentColumn()] = max(
760 14
                            (float) $autoSizes[$this->cellCollection->getCurrentColumn()],
761 14
                            (float) Shared\Font::calculateColumnWidth(
762 14
                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont(),
763 14
                                $cellValue,
764 14
                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getAlignment()->getTextRotation(),
765 14
                                $this->getParent()->getDefaultStyle()->getFont()
766
                            )
767
                        );
768
                    }
769
                }
770
            }
771
772
            // adjust column widths
773 14
            foreach ($autoSizes as $columnIndex => $width) {
774 14
                if ($width == -1) {
775
                    $width = $this->getDefaultColumnDimension()->getWidth();
776
                }
777 14
                $this->getColumnDimension($columnIndex)->setWidth($width);
778
            }
779
        }
780
781 48
        return $this;
782
    }
783
784
    /**
785
     * Get parent.
786
     *
787
     * @return Spreadsheet
788
     */
789 155
    public function getParent()
790
    {
791 155
        return $this->parent;
792
    }
793
794
    /**
795
     * Re-bind parent.
796
     *
797
     * @param Spreadsheet $parent
798
     *
799
     * @return Worksheet
800
     */
801 1
    public function rebindParent(Spreadsheet $parent)
802
    {
803 1
        if ($this->parent !== null) {
804 1
            $namedRanges = $this->parent->getNamedRanges();
805 1
            foreach ($namedRanges as $namedRange) {
806
                $parent->addNamedRange($namedRange);
807
            }
808
809 1
            $this->parent->removeSheetByIndex(
810 1
                $this->parent->getIndex($this)
811
            );
812
        }
813 1
        $this->parent = $parent;
814
815 1
        return $this;
816
    }
817
818
    /**
819
     * Get title.
820
     *
821
     * @return string
822
     */
823 155
    public function getTitle()
824
    {
825 155
        return $this->title;
826
    }
827
828
    /**
829
     * Set title.
830
     *
831
     * @param string $pValue String containing the dimension of this worksheet
832
     * @param bool $updateFormulaCellReferences Flag indicating whether cell references in formulae should
833
     *            be updated to reflect the new sheet name.
834
     *          This should be left as the default true, unless you are
835
     *          certain that no formula cells on any worksheet contain
836
     *          references to this worksheet
837
     * @param bool $validate False to skip validation of new title. WARNING: This should only be set
838
     *                       at parse time (by Readers), where titles can be assumed to be valid.
839
     *
840
     * @return Worksheet
841
     */
842 155
    public function setTitle($pValue, $updateFormulaCellReferences = true, $validate = true)
843
    {
844
        // Is this a 'rename' or not?
845 155
        if ($this->getTitle() == $pValue) {
846 9
            return $this;
847
        }
848
849
        // Old title
850 155
        $oldTitle = $this->getTitle();
851
852 155
        if ($validate) {
853
            // Syntax check
854 155
            self::checkSheetTitle($pValue);
855
856 155
            if ($this->parent) {
857
                // Is there already such sheet name?
858 139
                if ($this->parent->sheetNameExists($pValue)) {
859
                    // Use name, but append with lowest possible integer
860
861 5
                    if (Shared\StringHelper::countCharacters($pValue) > 29) {
862
                        $pValue = Shared\StringHelper::substring($pValue, 0, 29);
863
                    }
864 5
                    $i = 1;
865 5 View Code Duplication
                    while ($this->parent->sheetNameExists($pValue . ' ' . $i)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
866 2
                        ++$i;
867 2
                        if ($i == 10) {
868
                            if (Shared\StringHelper::countCharacters($pValue) > 28) {
869
                                $pValue = Shared\StringHelper::substring($pValue, 0, 28);
870
                            }
871 2
                        } elseif ($i == 100) {
872
                            if (Shared\StringHelper::countCharacters($pValue) > 27) {
873
                                $pValue = Shared\StringHelper::substring($pValue, 0, 27);
874
                            }
875
                        }
876
                    }
877
878 5
                    $pValue .= " $i";
879
                }
880
            }
881
        }
882
883
        // Set title
884 155
        $this->title = $pValue;
885 155
        $this->dirty = true;
886
887 155
        if ($this->parent && $this->parent->getCalculationEngine()) {
888
            // New title
889 139
            $newTitle = $this->getTitle();
890 139
            $this->parent->getCalculationEngine()
891 139
                ->renameCalculationCacheForWorksheet($oldTitle, $newTitle);
892 139
            if ($updateFormulaCellReferences) {
893 31
                ReferenceHelper::getInstance()->updateNamedFormulas($this->parent, $oldTitle, $newTitle);
894
            }
895
        }
896
897 155
        return $this;
898
    }
899
900
    /**
901
     * Get sheet state.
902
     *
903
     * @return string Sheet state (visible, hidden, veryHidden)
904
     */
905 60
    public function getSheetState()
906
    {
907 60
        return $this->sheetState;
908
    }
909
910
    /**
911
     * Set sheet state.
912
     *
913
     * @param string $value Sheet state (visible, hidden, veryHidden)
914
     *
915
     * @return Worksheet
916
     */
917 155
    public function setSheetState($value)
918
    {
919 155
        $this->sheetState = $value;
920
921 155
        return $this;
922
    }
923
924
    /**
925
     * Get page setup.
926
     *
927
     * @return PageSetup
928
     */
929 80
    public function getPageSetup()
930
    {
931 80
        return $this->pageSetup;
932
    }
933
934
    /**
935
     * Set page setup.
936
     *
937
     * @param PageSetup $pValue
938
     *
939
     * @return Worksheet
940
     */
941
    public function setPageSetup(PageSetup $pValue)
942
    {
943
        $this->pageSetup = $pValue;
944
945
        return $this;
946
    }
947
948
    /**
949
     * Get page margins.
950
     *
951
     * @return PageMargins
952
     */
953 80
    public function getPageMargins()
954
    {
955 80
        return $this->pageMargins;
956
    }
957
958
    /**
959
     * Set page margins.
960
     *
961
     * @param PageMargins $pValue
962
     *
963
     * @return Worksheet
964
     */
965
    public function setPageMargins(PageMargins $pValue)
966
    {
967
        $this->pageMargins = $pValue;
968
969
        return $this;
970
    }
971
972
    /**
973
     * Get page header/footer.
974
     *
975
     * @return HeaderFooter
976
     */
977 66
    public function getHeaderFooter()
978
    {
979 66
        return $this->headerFooter;
980
    }
981
982
    /**
983
     * Set page header/footer.
984
     *
985
     * @param HeaderFooter $pValue
986
     *
987
     * @return Worksheet
988
     */
989
    public function setHeaderFooter(HeaderFooter $pValue)
990
    {
991
        $this->headerFooter = $pValue;
992
993
        return $this;
994
    }
995
996
    /**
997
     * Get sheet view.
998
     *
999
     * @return SheetView
1000
     */
1001 74
    public function getSheetView()
1002
    {
1003 74
        return $this->sheetView;
1004
    }
1005
1006
    /**
1007
     * Set sheet view.
1008
     *
1009
     * @param SheetView $pValue
1010
     *
1011
     * @return Worksheet
1012
     */
1013
    public function setSheetView(SheetView $pValue)
1014
    {
1015
        $this->sheetView = $pValue;
1016
1017
        return $this;
1018
    }
1019
1020
    /**
1021
     * Get Protection.
1022
     *
1023
     * @return Protection
1024
     */
1025 67
    public function getProtection()
1026
    {
1027 67
        return $this->protection;
1028
    }
1029
1030
    /**
1031
     * Set Protection.
1032
     *
1033
     * @param Protection $pValue
1034
     *
1035
     * @return Worksheet
1036
     */
1037
    public function setProtection(Protection $pValue)
1038
    {
1039
        $this->protection = $pValue;
1040
        $this->dirty = true;
1041
1042
        return $this;
1043
    }
1044
1045
    /**
1046
     * Get highest worksheet column.
1047
     *
1048
     * @param string $row Return the data highest column for the specified row,
1049
     *                                     or the highest column of any row if no row number is passed
1050
     *
1051
     * @return string Highest column name
1052
     */
1053 80
    public function getHighestColumn($row = null)
1054
    {
1055 80
        if ($row == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $row of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
1056 80
            return $this->cachedHighestColumn;
1057
        }
1058
1059
        return $this->getHighestDataColumn($row);
1060
    }
1061
1062
    /**
1063
     * Get highest worksheet column that contains data.
1064
     *
1065
     * @param string $row Return the highest data column for the specified row,
1066
     *                                     or the highest data column of any row if no row number is passed
1067
     *
1068
     * @return string Highest column name that contains data
1069
     */
1070 14
    public function getHighestDataColumn($row = null)
1071
    {
1072 14
        return $this->cellCollection->getHighestColumn($row);
1073
    }
1074
1075
    /**
1076
     * Get highest worksheet row.
1077
     *
1078
     * @param string $column Return the highest data row for the specified column,
1079
     *                                     or the highest row of any column if no column letter is passed
1080
     *
1081
     * @return int Highest row number
1082
     */
1083 80
    public function getHighestRow($column = null)
1084
    {
1085 80
        if ($column == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $column of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
1086 80
            return $this->cachedHighestRow;
1087
        }
1088
1089
        return $this->getHighestDataRow($column);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getHighestDataRow($column) returns the type string which is incompatible with the documented return type integer.
Loading history...
1090
    }
1091
1092
    /**
1093
     * Get highest worksheet row that contains data.
1094
     *
1095
     * @param string $column Return the highest data row for the specified column,
1096
     *                                     or the highest data row of any column if no column letter is passed
1097
     *
1098
     * @return string Highest row number that contains data
1099
     */
1100 16
    public function getHighestDataRow($column = null)
1101
    {
1102 16
        return $this->cellCollection->getHighestRow($column);
1103
    }
1104
1105
    /**
1106
     * Get highest worksheet column and highest row that have cell records.
1107
     *
1108
     * @return array Highest column name and highest row number
1109
     */
1110
    public function getHighestRowAndColumn()
1111
    {
1112
        return $this->cellCollection->getHighestRowAndColumn();
1113
    }
1114
1115
    /**
1116
     * Set a cell value.
1117
     *
1118
     * @param string $pCoordinate Coordinate of the cell, eg: 'A1'
1119
     * @param mixed $pValue Value of the cell
1120
     *
1121
     * @return Worksheet
1122
     */
1123 55
    public function setCellValue($pCoordinate, $pValue)
1124
    {
1125 55
        $this->getCell($pCoordinate)->setValue($pValue);
1126
1127 55
        return $this;
1128
    }
1129
1130
    /**
1131
     * Set a cell value by using numeric cell coordinates.
1132
     *
1133
     * @param int $columnIndex Numeric column coordinate of the cell
1134
     * @param int $row Numeric row coordinate of the cell
1135
     * @param mixed $value Value of the cell
1136
     *
1137
     * @return Worksheet
1138
     */
1139
    public function setCellValueByColumnAndRow($columnIndex, $row, $value)
1140
    {
1141
        $this->getCellByColumnAndRow($columnIndex, $row)->setValue($value);
1142
1143
        return $this;
1144
    }
1145
1146
    /**
1147
     * Set a cell value.
1148
     *
1149
     * @param string $pCoordinate Coordinate of the cell, eg: 'A1'
1150
     * @param mixed $pValue Value of the cell
1151
     * @param string $pDataType Explicit data type, see DataType::TYPE_*
1152
     *
1153
     * @return Worksheet
1154
     */
1155 1
    public function setCellValueExplicit($pCoordinate, $pValue, $pDataType)
1156
    {
1157
        // Set value
1158 1
        $this->getCell($pCoordinate)->setValueExplicit($pValue, $pDataType);
1159
1160 1
        return $this;
1161
    }
1162
1163
    /**
1164
     * Set a cell value by using numeric cell coordinates.
1165
     *
1166
     * @param int $columnIndex Numeric column coordinate of the cell
1167
     * @param int $row Numeric row coordinate of the cell
1168
     * @param mixed $value Value of the cell
1169
     * @param string $dataType Explicit data type, see DataType::TYPE_*
1170
     *
1171
     * @return Worksheet
1172
     */
1173
    public function setCellValueExplicitByColumnAndRow($columnIndex, $row, $value, $dataType)
1174
    {
1175
        $this->getCellByColumnAndRow($columnIndex, $row)->setValueExplicit($value, $dataType);
1176
1177
        return $this;
1178
    }
1179
1180
    /**
1181
     * Get cell at a specific coordinate.
1182
     *
1183
     * @param string $pCoordinate Coordinate of the cell, eg: 'A1'
1184
     * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
1185
     *                                       already exist, or a null should be returned instead
1186
     *
1187
     * @throws Exception
1188
     *
1189
     * @return null|Cell Cell that was found/created or null
1190
     */
1191 116
    public function getCell($pCoordinate, $createIfNotExists = true)
1192
    {
1193
        // Check cell collection
1194 116
        if ($this->cellCollection->has(strtoupper($pCoordinate))) {
1195 104
            return $this->cellCollection->get($pCoordinate);
1196
        }
1197
1198
        // Worksheet reference?
1199 114 View Code Duplication
        if (strpos($pCoordinate, '!') !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1200 1
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1201
1202 1
            return $this->parent->getSheetByName($worksheetReference[0])->getCell(strtoupper($worksheetReference[1]), $createIfNotExists);
1203
        }
1204
1205
        // Named range?
1206 114
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1207 114
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1208
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1209
            if ($namedRange !== null) {
1210
                $pCoordinate = $namedRange->getRange();
1211
1212
                return $namedRange->getWorksheet()->getCell($pCoordinate, $createIfNotExists);
1213
            }
1214
        }
1215
1216
        // Uppercase coordinate
1217 114
        $pCoordinate = strtoupper($pCoordinate);
1218
1219 114
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
1220
            throw new Exception('Cell coordinate can not be a range of cells.');
1221 114
        } elseif (strpos($pCoordinate, '$') !== false) {
1222
            throw new Exception('Cell coordinate must not be absolute.');
1223
        }
1224
1225
        // Create new cell object, if required
1226 114
        return $createIfNotExists ? $this->createNewCell($pCoordinate) : null;
1227
    }
1228
1229
    /**
1230
     * Get cell at a specific coordinate by using numeric cell coordinates.
1231
     *
1232
     * @param int $columnIndex Numeric column coordinate of the cell
1233
     * @param int $row Numeric row coordinate of the cell
1234
     * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
1235
     *                                       already exist, or a null should be returned instead
1236
     *
1237
     * @return null|Cell Cell that was found/created or null
1238
     */
1239 47
    public function getCellByColumnAndRow($columnIndex, $row, $createIfNotExists = true)
1240
    {
1241 47
        $columnLetter = Coordinate::stringFromColumnIndex($columnIndex);
1242 47
        $coordinate = $columnLetter . $row;
1243
1244 47
        if ($this->cellCollection->has($coordinate)) {
1245 46
            return $this->cellCollection->get($coordinate);
1246
        }
1247
1248
        // Create new cell object, if required
1249 25
        return $createIfNotExists ? $this->createNewCell($coordinate) : null;
1250
    }
1251
1252
    /**
1253
     * Create a new cell at the specified coordinate.
1254
     *
1255
     * @param string $pCoordinate Coordinate of the cell
1256
     *
1257
     * @return Cell Cell that was created
1258
     */
1259 117
    private function createNewCell($pCoordinate)
1260
    {
1261 117
        $cell = new Cell(null, DataType::TYPE_NULL, $this);
1262 117
        $this->cellCollection->add($pCoordinate, $cell);
1263 117
        $this->cellCollectionIsSorted = false;
1264
1265
        // Coordinates
1266 117
        $aCoordinates = Coordinate::coordinateFromString($pCoordinate);
1267 117
        if (Coordinate::columnIndexFromString($this->cachedHighestColumn) < Coordinate::columnIndexFromString($aCoordinates[0])) {
1268 101
            $this->cachedHighestColumn = $aCoordinates[0];
1269
        }
1270 117
        $this->cachedHighestRow = max($this->cachedHighestRow, $aCoordinates[1]);
1271
1272
        // Cell needs appropriate xfIndex from dimensions records
1273
        //    but don't create dimension records if they don't already exist
1274 117
        $rowDimension = $this->getRowDimension($aCoordinates[1], false);
0 ignored issues
show
Bug introduced by
$aCoordinates[1] of type string is incompatible with the type integer expected by parameter $pRow of PhpOffice\PhpSpreadsheet...heet::getRowDimension(). ( Ignorable by Annotation )

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

1274
        $rowDimension = $this->getRowDimension(/** @scrutinizer ignore-type */ $aCoordinates[1], false);
Loading history...
1275 117
        $columnDimension = $this->getColumnDimension($aCoordinates[0], false);
1276
1277 117
        if ($rowDimension !== null && $rowDimension->getXfIndex() > 0) {
1278
            // then there is a row dimension with explicit style, assign it to the cell
1279 2
            $cell->setXfIndex($rowDimension->getXfIndex());
1280 117
        } elseif ($columnDimension !== null && $columnDimension->getXfIndex() > 0) {
1281
            // then there is a column dimension, assign it to the cell
1282
            $cell->setXfIndex($columnDimension->getXfIndex());
1283
        }
1284
1285 117
        return $cell;
1286
    }
1287
1288
    /**
1289
     * Does the cell at a specific coordinate exist?
1290
     *
1291
     * @param string $pCoordinate Coordinate of the cell eg: 'A1'
1292
     *
1293
     * @throws Exception
1294
     *
1295
     * @return bool
1296
     */
1297 75
    public function cellExists($pCoordinate)
1298
    {
1299
        // Worksheet reference?
1300 75 View Code Duplication
        if (strpos($pCoordinate, '!') !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1301
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1302
1303
            return $this->parent->getSheetByName($worksheetReference[0])->cellExists(strtoupper($worksheetReference[1]));
1304
        }
1305
1306
        // Named range?
1307 75
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1308 75
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1309
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1310
            if ($namedRange !== null) {
1311
                $pCoordinate = $namedRange->getRange();
1312
                if ($this->getHashCode() != $namedRange->getWorksheet()->getHashCode()) {
1313
                    if (!$namedRange->getLocalOnly()) {
1314
                        return $namedRange->getWorksheet()->cellExists($pCoordinate);
1315
                    }
1316
1317
                    throw new Exception('Named range ' . $namedRange->getName() . ' is not accessible from within sheet ' . $this->getTitle());
1318
                }
1319
            } else {
1320
                return false;
1321
            }
1322
        }
1323
1324
        // Uppercase coordinate
1325 75
        $pCoordinate = strtoupper($pCoordinate);
1326
1327 75
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
1328
            throw new Exception('Cell coordinate can not be a range of cells.');
1329 75
        } elseif (strpos($pCoordinate, '$') !== false) {
1330
            throw new Exception('Cell coordinate must not be absolute.');
1331
        }
1332
1333
        // Cell exists?
1334 75
        return $this->cellCollection->has($pCoordinate);
1335
    }
1336
1337
    /**
1338
     * Cell at a specific coordinate by using numeric cell coordinates exists?
1339
     *
1340
     * @param int $columnIndex Numeric column coordinate of the cell
1341
     * @param int $row Numeric row coordinate of the cell
1342
     *
1343
     * @return bool
1344
     */
1345 6
    public function cellExistsByColumnAndRow($columnIndex, $row)
1346
    {
1347 6
        return $this->cellExists(Coordinate::stringFromColumnIndex($columnIndex) . $row);
1348
    }
1349
1350
    /**
1351
     * Get row dimension at a specific row.
1352
     *
1353
     * @param int $pRow Numeric index of the row
1354
     * @param bool $create
1355
     *
1356
     * @return RowDimension
1357
     */
1358 118
    public function getRowDimension($pRow, $create = true)
1359
    {
1360
        // Found
1361 118
        $found = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $found is dead and can be removed.
Loading history...
1362
1363
        // Get row dimension
1364 118
        if (!isset($this->rowDimensions[$pRow])) {
1365 118
            if (!$create) {
1366 103
                return null;
1367
            }
1368 74
            $this->rowDimensions[$pRow] = new RowDimension($pRow);
1369
1370 74
            $this->cachedHighestRow = max($this->cachedHighestRow, $pRow);
1371
        }
1372
1373 74
        return $this->rowDimensions[$pRow];
1374
    }
1375
1376
    /**
1377
     * Get column dimension at a specific column.
1378
     *
1379
     * @param string $pColumn String index of the column eg: 'A'
1380
     * @param bool $create
1381
     *
1382
     * @return ColumnDimension
1383
     */
1384 118
    public function getColumnDimension($pColumn, $create = true)
1385
    {
1386
        // Uppercase coordinate
1387 118
        $pColumn = strtoupper($pColumn);
1388
1389
        // Fetch dimensions
1390 118
        if (!isset($this->columnDimensions[$pColumn])) {
1391 118
            if (!$create) {
1392 113
                return null;
1393
            }
1394 42
            $this->columnDimensions[$pColumn] = new ColumnDimension($pColumn);
1395
1396 42
            if (Coordinate::columnIndexFromString($this->cachedHighestColumn) < Coordinate::columnIndexFromString($pColumn)) {
1397 23
                $this->cachedHighestColumn = $pColumn;
1398
            }
1399
        }
1400
1401 42
        return $this->columnDimensions[$pColumn];
1402
    }
1403
1404
    /**
1405
     * Get column dimension at a specific column by using numeric cell coordinates.
1406
     *
1407
     * @param int $columnIndex Numeric column coordinate of the cell
1408
     *
1409
     * @return ColumnDimension
1410
     */
1411 13
    public function getColumnDimensionByColumn($columnIndex)
1412
    {
1413 13
        return $this->getColumnDimension(Coordinate::stringFromColumnIndex($columnIndex));
1414
    }
1415
1416
    /**
1417
     * Get styles.
1418
     *
1419
     * @return Style[]
1420
     */
1421
    public function getStyles()
1422
    {
1423
        return $this->styles;
1424
    }
1425
1426
    /**
1427
     * Get style for cell.
1428
     *
1429
     * @param string $pCellCoordinate Cell coordinate (or range) to get style for, eg: 'A1'
1430
     *
1431
     * @throws Exception
1432
     *
1433
     * @return Style
1434
     */
1435 46
    public function getStyle($pCellCoordinate)
1436
    {
1437
        // set this sheet as active
1438 46
        $this->parent->setActiveSheetIndex($this->parent->getIndex($this));
1439
1440
        // set cell coordinate as active
1441 46
        $this->setSelectedCells(strtoupper($pCellCoordinate));
1442
1443 46
        return $this->parent->getCellXfSupervisor();
1444
    }
1445
1446
    /**
1447
     * Get conditional styles for a cell.
1448
     *
1449
     * @param string $pCoordinate eg: 'A1'
1450
     *
1451
     * @return Conditional[]
1452
     */
1453 2
    public function getConditionalStyles($pCoordinate)
1454
    {
1455 2
        $pCoordinate = strtoupper($pCoordinate);
1456 2
        if (!isset($this->conditionalStylesCollection[$pCoordinate])) {
1457 2
            $this->conditionalStylesCollection[$pCoordinate] = [];
1458
        }
1459
1460 2
        return $this->conditionalStylesCollection[$pCoordinate];
1461
    }
1462
1463
    /**
1464
     * Do conditional styles exist for this cell?
1465
     *
1466
     * @param string $pCoordinate eg: 'A1'
1467
     *
1468
     * @return bool
1469
     */
1470 13
    public function conditionalStylesExists($pCoordinate)
1471
    {
1472 13
        if (isset($this->conditionalStylesCollection[strtoupper($pCoordinate)])) {
1473
            return true;
1474
        }
1475
1476 13
        return false;
1477
    }
1478
1479
    /**
1480
     * Removes conditional styles for a cell.
1481
     *
1482
     * @param string $pCoordinate eg: 'A1'
1483
     *
1484
     * @return Worksheet
1485
     */
1486 14
    public function removeConditionalStyles($pCoordinate)
1487
    {
1488 14
        unset($this->conditionalStylesCollection[strtoupper($pCoordinate)]);
1489
1490 14
        return $this;
1491
    }
1492
1493
    /**
1494
     * Get collection of conditional styles.
1495
     *
1496
     * @return array
1497
     */
1498 60
    public function getConditionalStylesCollection()
1499
    {
1500 60
        return $this->conditionalStylesCollection;
1501
    }
1502
1503
    /**
1504
     * Set conditional styles.
1505
     *
1506
     * @param string $pCoordinate eg: 'A1'
1507
     * @param $pValue Conditional[]
1508
     *
1509
     * @return Worksheet
1510
     */
1511 2
    public function setConditionalStyles($pCoordinate, $pValue)
1512
    {
1513 2
        $this->conditionalStylesCollection[strtoupper($pCoordinate)] = $pValue;
1514
1515 2
        return $this;
1516
    }
1517
1518
    /**
1519
     * Get style for cell by using numeric cell coordinates.
1520
     *
1521
     * @param int $columnIndex1 Numeric column coordinate of the cell
1522
     * @param int $row1 Numeric row coordinate of the cell
1523
     * @param int $columnIndex2 Numeric column coordinate of the range cell
1524
     * @param int $row2 Numeric row coordinate of the range cell
1525
     * @param null|int $columnIndex2
1526
     * @param null|int $row2
1527
     *
1528
     * @return Style
1529
     */
1530
    public function getStyleByColumnAndRow($columnIndex1, $row1, $columnIndex2 = null, $row2 = null)
1531
    {
1532
        if ($columnIndex2 !== null && $row2 !== null) {
1533
            $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
1534
1535
            return $this->getStyle($cellRange);
1536
        }
1537
1538
        return $this->getStyle(Coordinate::stringFromColumnIndex($columnIndex1) . $row1);
1539
    }
1540
1541
    /**
1542
     * Duplicate cell style to a range of cells.
1543
     *
1544
     * Please note that this will overwrite existing cell styles for cells in range!
1545
     *
1546
     * @param Style $pCellStyle Cell style to duplicate
1547
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
1548
     *
1549
     * @throws Exception
1550
     *
1551
     * @return Worksheet
1552
     */
1553 2
    public function duplicateStyle(Style $pCellStyle, $pRange)
1554
    {
1555
        // Add the style to the workbook if necessary
1556 2
        $workbook = $this->parent;
1557 2
        if ($existingStyle = $this->parent->getCellXfByHashCode($pCellStyle->getHashCode())) {
1558
            // there is already such cell Xf in our collection
1559 1
            $xfIndex = $existingStyle->getIndex();
1560
        } else {
1561
            // we don't have such a cell Xf, need to add
1562 2
            $workbook->addCellXf($pCellStyle);
1563 2
            $xfIndex = $pCellStyle->getIndex();
1564
        }
1565
1566
        // Calculate range outer borders
1567 2
        list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($pRange . ':' . $pRange);
1568
1569
        // Make sure we can loop upwards on rows and columns
1570 2 View Code Duplication
        if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1571
            $tmp = $rangeStart;
1572
            $rangeStart = $rangeEnd;
1573
            $rangeEnd = $tmp;
1574
        }
1575
1576
        // Loop through cells and apply styles
1577 2 View Code Duplication
        for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1578 2
            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
1579 2
                $this->getCell(Coordinate::stringFromColumnIndex($col) . $row)->setXfIndex($xfIndex);
1580
            }
1581
        }
1582
1583 2
        return $this;
1584
    }
1585
1586
    /**
1587
     * Duplicate conditional style to a range of cells.
1588
     *
1589
     * Please note that this will overwrite existing cell styles for cells in range!
1590
     *
1591
     * @param Conditional[] $pCellStyle Cell style to duplicate
1592
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
1593
     *
1594
     * @throws Exception
1595
     *
1596
     * @return Worksheet
1597
     */
1598 2
    public function duplicateConditionalStyle(array $pCellStyle, $pRange = '')
1599
    {
1600 2
        foreach ($pCellStyle as $cellStyle) {
1601 2
            if (!($cellStyle instanceof Conditional)) {
1602 2
                throw new Exception('Style is not a conditional style');
1603
            }
1604
        }
1605
1606
        // Calculate range outer borders
1607 2
        list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($pRange . ':' . $pRange);
1608
1609
        // Make sure we can loop upwards on rows and columns
1610 2 View Code Duplication
        if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1611
            $tmp = $rangeStart;
1612
            $rangeStart = $rangeEnd;
1613
            $rangeEnd = $tmp;
1614
        }
1615
1616
        // Loop through cells and apply styles
1617 2 View Code Duplication
        for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1618 2
            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
1619 2
                $this->setConditionalStyles(Coordinate::stringFromColumnIndex($col) . $row, $pCellStyle);
1620
            }
1621
        }
1622
1623 2
        return $this;
1624
    }
1625
1626
    /**
1627
     * Set break on a cell.
1628
     *
1629
     * @param string $pCoordinate Cell coordinate (e.g. A1)
1630
     * @param int $pBreak Break type (type of Worksheet::BREAK_*)
1631
     *
1632
     * @throws Exception
1633
     *
1634
     * @return Worksheet
1635
     */
1636 1
    public function setBreak($pCoordinate, $pBreak)
1637
    {
1638
        // Uppercase coordinate
1639 1
        $pCoordinate = strtoupper($pCoordinate);
1640
1641 1
        if ($pCoordinate != '') {
1642 1
            if ($pBreak == self::BREAK_NONE) {
1643
                if (isset($this->breaks[$pCoordinate])) {
1644
                    unset($this->breaks[$pCoordinate]);
1645
                }
1646
            } else {
1647 1
                $this->breaks[$pCoordinate] = $pBreak;
1648
            }
1649
        } else {
1650
            throw new Exception('No cell coordinate specified.');
1651
        }
1652
1653 1
        return $this;
1654
    }
1655
1656
    /**
1657
     * Set break on a cell by using numeric cell coordinates.
1658
     *
1659
     * @param int $columnIndex Numeric column coordinate of the cell
1660
     * @param int $row Numeric row coordinate of the cell
1661
     * @param int $break Break type (type of Worksheet::BREAK_*)
1662
     *
1663
     * @return Worksheet
1664
     */
1665
    public function setBreakByColumnAndRow($columnIndex, $row, $break)
1666
    {
1667
        return $this->setBreak(Coordinate::stringFromColumnIndex($columnIndex) . $row, $break);
1668
    }
1669
1670
    /**
1671
     * Get breaks.
1672
     *
1673
     * @return array[]
1674
     */
1675 64
    public function getBreaks()
1676
    {
1677 64
        return $this->breaks;
1678
    }
1679
1680
    /**
1681
     * Set merge on a cell range.
1682
     *
1683
     * @param string $pRange Cell range (e.g. A1:E1)
1684
     *
1685
     * @throws Exception
1686
     *
1687
     * @return Worksheet
1688
     */
1689 33
    public function mergeCells($pRange)
1690
    {
1691
        // Uppercase coordinate
1692 33
        $pRange = strtoupper($pRange);
1693
1694 33
        if (strpos($pRange, ':') !== false) {
1695 33
            $this->mergeCells[$pRange] = $pRange;
1696
1697
            // make sure cells are created
1698
1699
            // get the cells in the range
1700 33
            $aReferences = Coordinate::extractAllCellReferencesInRange($pRange);
1701
1702
            // create upper left cell if it does not already exist
1703 33
            $upperLeft = $aReferences[0];
1704 33
            if (!$this->cellExists($upperLeft)) {
1705 14
                $this->getCell($upperLeft)->setValueExplicit(null, DataType::TYPE_NULL);
1706
            }
1707
1708
            // Blank out the rest of the cells in the range (if they exist)
1709 33
            $count = count($aReferences);
1710 33
            for ($i = 1; $i < $count; ++$i) {
1711 33
                if ($this->cellExists($aReferences[$i])) {
1712 14
                    $this->getCell($aReferences[$i])->setValueExplicit(null, DataType::TYPE_NULL);
1713
                }
1714
            }
1715
        } else {
1716
            throw new Exception('Merge must be set on a range of cells.');
1717
        }
1718
1719 33
        return $this;
1720
    }
1721
1722
    /**
1723
     * Set merge on a cell range by using numeric cell coordinates.
1724
     *
1725
     * @param int $columnIndex1 Numeric column coordinate of the first cell
1726
     * @param int $row1 Numeric row coordinate of the first cell
1727
     * @param int $columnIndex2 Numeric column coordinate of the last cell
1728
     * @param int $row2 Numeric row coordinate of the last cell
1729
     *
1730
     * @throws Exception
1731
     *
1732
     * @return Worksheet
1733
     */
1734
    public function mergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
1735
    {
1736
        $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
1737
1738
        return $this->mergeCells($cellRange);
1739
    }
1740
1741
    /**
1742
     * Remove merge on a cell range.
1743
     *
1744
     * @param string $pRange Cell range (e.g. A1:E1)
1745
     *
1746
     * @throws Exception
1747
     *
1748
     * @return Worksheet
1749
     */
1750 12
    public function unmergeCells($pRange)
1751
    {
1752
        // Uppercase coordinate
1753 12
        $pRange = strtoupper($pRange);
1754
1755 12
        if (strpos($pRange, ':') !== false) {
1756 12
            if (isset($this->mergeCells[$pRange])) {
1757 12
                unset($this->mergeCells[$pRange]);
1758
            } else {
1759 12
                throw new Exception('Cell range ' . $pRange . ' not known as merged.');
1760
            }
1761
        } else {
1762
            throw new Exception('Merge can only be removed from a range of cells.');
1763
        }
1764
1765 12
        return $this;
1766
    }
1767
1768
    /**
1769
     * Remove merge on a cell range by using numeric cell coordinates.
1770
     *
1771
     * @param int $columnIndex1 Numeric column coordinate of the first cell
1772
     * @param int $row1 Numeric row coordinate of the first cell
1773
     * @param int $columnIndex2 Numeric column coordinate of the last cell
1774
     * @param int $row2 Numeric row coordinate of the last cell
1775
     *
1776
     * @throws Exception
1777
     *
1778
     * @return Worksheet
1779
     */
1780
    public function unmergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
1781
    {
1782
        $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
1783
1784
        return $this->unmergeCells($cellRange);
1785
    }
1786
1787
    /**
1788
     * Get merge cells array.
1789
     *
1790
     * @return array[]
1791
     */
1792 66
    public function getMergeCells()
1793
    {
1794 66
        return $this->mergeCells;
1795
    }
1796
1797
    /**
1798
     * Set merge cells array for the entire sheet. Use instead mergeCells() to merge
1799
     * a single cell range.
1800
     *
1801
     * @param array
1802
     * @param mixed $pValue
1803
     */
1804 15
    public function setMergeCells(array $pValue)
1805
    {
1806 15
        $this->mergeCells = $pValue;
1807
1808 15
        return $this;
1809
    }
1810
1811
    /**
1812
     * Set protection on a cell range.
1813
     *
1814
     * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
1815
     * @param string $pPassword Password to unlock the protection
1816
     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
1817
     *
1818
     * @throws Exception
1819
     *
1820
     * @return Worksheet
1821
     */
1822 12
    public function protectCells($pRange, $pPassword, $pAlreadyHashed = false)
1823
    {
1824
        // Uppercase coordinate
1825 12
        $pRange = strtoupper($pRange);
1826
1827 12
        if (!$pAlreadyHashed) {
1828 12
            $pPassword = Shared\PasswordHasher::hashPassword($pPassword);
1829
        }
1830 12
        $this->protectedCells[$pRange] = $pPassword;
1831
1832 12
        return $this;
1833
    }
1834
1835
    /**
1836
     * Set protection on a cell range by using numeric cell coordinates.
1837
     *
1838
     * @param int $columnIndex1 Numeric column coordinate of the first cell
1839
     * @param int $row1 Numeric row coordinate of the first cell
1840
     * @param int $columnIndex2 Numeric column coordinate of the last cell
1841
     * @param int $row2 Numeric row coordinate of the last cell
1842
     * @param string $password Password to unlock the protection
1843
     * @param bool $alreadyHashed If the password has already been hashed, set this to true
1844
     *
1845
     * @throws Exception
1846
     *
1847
     * @return Worksheet
1848
     */
1849
    public function protectCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2, $password, $alreadyHashed = false)
1850
    {
1851
        $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
1852
1853
        return $this->protectCells($cellRange, $password, $alreadyHashed);
1854
    }
1855
1856
    /**
1857
     * Remove protection on a cell range.
1858
     *
1859
     * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
1860
     *
1861
     * @throws Exception
1862
     *
1863
     * @return Worksheet
1864
     */
1865 12
    public function unprotectCells($pRange)
1866
    {
1867
        // Uppercase coordinate
1868 12
        $pRange = strtoupper($pRange);
1869
1870 12
        if (isset($this->protectedCells[$pRange])) {
1871 12
            unset($this->protectedCells[$pRange]);
1872
        } else {
1873
            throw new Exception('Cell range ' . $pRange . ' not known as protected.');
1874
        }
1875
1876 12
        return $this;
1877
    }
1878
1879
    /**
1880
     * Remove protection on a cell range by using numeric cell coordinates.
1881
     *
1882
     * @param int $columnIndex1 Numeric column coordinate of the first cell
1883
     * @param int $row1 Numeric row coordinate of the first cell
1884
     * @param int $columnIndex2 Numeric column coordinate of the last cell
1885
     * @param int $row2 Numeric row coordinate of the last cell
1886
     *
1887
     * @throws Exception
1888
     *
1889
     * @return Worksheet
1890
     */
1891
    public function unprotectCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
1892
    {
1893
        $cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
1894
1895
        return $this->unprotectCells($cellRange);
1896
    }
1897
1898
    /**
1899
     * Get protected cells.
1900
     *
1901
     * @return array[]
1902
     */
1903 64
    public function getProtectedCells()
1904
    {
1905 64
        return $this->protectedCells;
1906
    }
1907
1908
    /**
1909
     * Get Autofilter.
1910
     *
1911
     * @return AutoFilter
1912
     */
1913 65
    public function getAutoFilter()
1914
    {
1915 65
        return $this->autoFilter;
1916
    }
1917
1918
    /**
1919
     * Set AutoFilter.
1920
     *
1921
     * @param AutoFilter|string $pValue
1922
     *            A simple string containing a Cell range like 'A1:E10' is permitted for backward compatibility
1923
     *
1924
     * @throws Exception
1925
     *
1926
     * @return Worksheet
1927
     */
1928 4
    public function setAutoFilter($pValue)
1929
    {
1930 4
        if (is_string($pValue)) {
1931 4
            $this->autoFilter->setRange($pValue);
1932
        } elseif (is_object($pValue) && ($pValue instanceof AutoFilter)) {
1933
            $this->autoFilter = $pValue;
1934
        }
1935
1936 4
        return $this;
1937
    }
1938
1939
    /**
1940
     * Set Autofilter Range by using numeric cell coordinates.
1941
     *
1942
     * @param int $columnIndex1 Numeric column coordinate of the first cell
1943
     * @param int $row1 Numeric row coordinate of the first cell
1944
     * @param int $columnIndex2 Numeric column coordinate of the second cell
1945
     * @param int $row2 Numeric row coordinate of the second cell
1946
     *
1947
     * @throws Exception
1948
     *
1949
     * @return Worksheet
1950
     */
1951
    public function setAutoFilterByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
1952
    {
1953
        return $this->setAutoFilter(
1954
            Coordinate::stringFromColumnIndex($columnIndex1) . $row1
1955
            . ':' .
1956
            Coordinate::stringFromColumnIndex($columnIndex2) . $row2
1957
        );
1958
    }
1959
1960
    /**
1961
     * Remove autofilter.
1962
     *
1963
     * @return Worksheet
1964
     */
1965
    public function removeAutoFilter()
1966
    {
1967
        $this->autoFilter->setRange(null);
1968
1969
        return $this;
1970
    }
1971
1972
    /**
1973
     * Get Freeze Pane.
1974
     *
1975
     * @return string
1976
     */
1977 64
    public function getFreezePane()
1978
    {
1979 64
        return $this->freezePane;
1980
    }
1981
1982
    /**
1983
     * Freeze Pane.
1984
     *
1985
     * Examples:
1986
     *
1987
     *     - A2 will freeze the rows above cell A2 (i.e row 1)
1988
     *     - B1 will freeze the columns to the left of cell B1 (i.e column A)
1989
     *     - B2 will freeze the rows above and to the left of cell A2 (i.e row 1 and column A)
1990
     *
1991
     * @param null|string $cell Position of the split
1992
     * @param null|string $topLeftCell default position of the right bottom pane
1993
     *
1994
     * @throws Exception
1995
     *
1996
     * @return Worksheet
1997
     */
1998 6
    public function freezePane($cell, $topLeftCell = null)
1999
    {
2000 6
        if (is_string($cell) && (strpos($cell, ':') !== false || strpos($cell, ',') !== false)) {
2001
            throw new Exception('Freeze pane can not be set on a range of cells.');
2002
        }
2003
2004 6
        if ($cell !== null && $topLeftCell === null) {
2005 4
            $coordinate = Coordinate::coordinateFromString($cell);
2006 4
            $topLeftCell = $coordinate[0] . ($coordinate[1] + 1);
2007
        }
2008
2009 6
        $this->freezePane = $cell;
2010 6
        $this->topLeftCell = $topLeftCell;
2011
2012 6
        return $this;
2013
    }
2014
2015
    /**
2016
     * Freeze Pane by using numeric cell coordinates.
2017
     *
2018
     * @param int $columnIndex Numeric column coordinate of the cell
2019
     * @param int $row Numeric row coordinate of the cell
2020
     *
2021
     * @return Worksheet
2022
     */
2023
    public function freezePaneByColumnAndRow($columnIndex, $row)
2024
    {
2025
        return $this->freezePane(Coordinate::stringFromColumnIndex($columnIndex) . $row);
2026
    }
2027
2028
    /**
2029
     * Unfreeze Pane.
2030
     *
2031
     * @return Worksheet
2032
     */
2033
    public function unfreezePane()
2034
    {
2035
        return $this->freezePane(null);
2036
    }
2037
2038
    /**
2039
     * Get the default position of the right bottom pane.
2040
     *
2041
     * @return int
2042
     */
2043 5
    public function getTopLeftCell()
2044
    {
2045 5
        return $this->topLeftCell;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->topLeftCell also could return the type string which is incompatible with the documented return type integer.
Loading history...
2046
    }
2047
2048
    /**
2049
     * Insert a new row, updating all possible related data.
2050
     *
2051
     * @param int $pBefore Insert before this one
2052
     * @param int $pNumRows Number of rows to insert
2053
     *
2054
     * @throws Exception
2055
     *
2056
     * @return Worksheet
2057
     */
2058 13 View Code Duplication
    public function insertNewRowBefore($pBefore, $pNumRows = 1)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2059
    {
2060 13
        if ($pBefore >= 1) {
2061 13
            $objReferenceHelper = ReferenceHelper::getInstance();
2062 13
            $objReferenceHelper->insertNewBefore('A' . $pBefore, 0, $pNumRows, $this);
2063
        } else {
2064
            throw new Exception('Rows can only be inserted before at least row 1.');
2065
        }
2066
2067 13
        return $this;
2068
    }
2069
2070
    /**
2071
     * Insert a new column, updating all possible related data.
2072
     *
2073
     * @param int $pBefore Insert before this one, eg: 'A'
2074
     * @param int $pNumCols Number of columns to insert
2075
     *
2076
     * @throws Exception
2077
     *
2078
     * @return Worksheet
2079
     */
2080 12 View Code Duplication
    public function insertNewColumnBefore($pBefore, $pNumCols = 1)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2081
    {
2082 12
        if (!is_numeric($pBefore)) {
2083 12
            $objReferenceHelper = ReferenceHelper::getInstance();
2084 12
            $objReferenceHelper->insertNewBefore($pBefore . '1', $pNumCols, 0, $this);
2085
        } else {
2086
            throw new Exception('Column references should not be numeric.');
2087
        }
2088
2089 12
        return $this;
2090
    }
2091
2092
    /**
2093
     * Insert a new column, updating all possible related data.
2094
     *
2095
     * @param int $beforeColumnIndex Insert before this one (numeric column coordinate of the cell)
2096
     * @param int $pNumCols Number of columns to insert
2097
     *
2098
     * @throws Exception
2099
     *
2100
     * @return Worksheet
2101
     */
2102
    public function insertNewColumnBeforeByIndex($beforeColumnIndex, $pNumCols = 1)
2103
    {
2104
        if ($beforeColumnIndex >= 1) {
2105
            return $this->insertNewColumnBefore(Coordinate::stringFromColumnIndex($beforeColumnIndex), $pNumCols);
0 ignored issues
show
Bug introduced by
PhpOffice\PhpSpreadsheet...dex($beforeColumnIndex) of type string is incompatible with the type integer expected by parameter $pBefore of PhpOffice\PhpSpreadsheet...insertNewColumnBefore(). ( Ignorable by Annotation )

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

2105
            return $this->insertNewColumnBefore(/** @scrutinizer ignore-type */ Coordinate::stringFromColumnIndex($beforeColumnIndex), $pNumCols);
Loading history...
2106
        }
2107
2108
        throw new Exception('Columns can only be inserted before at least column A (1).');
2109
    }
2110
2111
    /**
2112
     * Delete a row, updating all possible related data.
2113
     *
2114
     * @param int $pRow Remove starting with this one
2115
     * @param int $pNumRows Number of rows to remove
2116
     *
2117
     * @throws Exception
2118
     *
2119
     * @return Worksheet
2120
     */
2121 15
    public function removeRow($pRow, $pNumRows = 1)
2122
    {
2123 15
        if ($pRow >= 1) {
2124 15
            $highestRow = $this->getHighestDataRow();
2125 15
            $objReferenceHelper = ReferenceHelper::getInstance();
2126 15
            $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this);
2127 15
            for ($r = 0; $r < $pNumRows; ++$r) {
2128 15
                $this->getCellCollection()->removeRow($highestRow);
2129 15
                --$highestRow;
2130
            }
2131
        } else {
2132
            throw new Exception('Rows to be deleted should at least start from row 1.');
2133
        }
2134
2135 15
        return $this;
2136
    }
2137
2138
    /**
2139
     * Remove a column, updating all possible related data.
2140
     *
2141
     * @param string $pColumn Remove starting with this one, eg: 'A'
2142
     * @param int $pNumCols Number of columns to remove
2143
     *
2144
     * @throws Exception
2145
     *
2146
     * @return Worksheet
2147
     */
2148 12
    public function removeColumn($pColumn, $pNumCols = 1)
2149
    {
2150 12
        if (!is_numeric($pColumn)) {
2151 12
            $highestColumn = $this->getHighestDataColumn();
2152 12
            $pColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($pColumn) + $pNumCols);
2153 12
            $objReferenceHelper = ReferenceHelper::getInstance();
2154 12
            $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this);
2155 12
            for ($c = 0; $c < $pNumCols; ++$c) {
2156 12
                $this->getCellCollection()->removeColumn($highestColumn);
2157 12
                $highestColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($highestColumn) - 1);
2158
            }
2159
        } else {
2160
            throw new Exception('Column references should not be numeric.');
2161
        }
2162
2163 12
        return $this;
2164
    }
2165
2166
    /**
2167
     * Remove a column, updating all possible related data.
2168
     *
2169
     * @param int $columnIndex Remove starting with this one (numeric column coordinate of the cell)
2170
     * @param int $numColumns Number of columns to remove
2171
     *
2172
     * @throws Exception
2173
     *
2174
     * @return Worksheet
2175
     */
2176
    public function removeColumnByIndex($columnIndex, $numColumns = 1)
2177
    {
2178
        if ($columnIndex >= 1) {
2179
            return $this->removeColumn(Coordinate::stringFromColumnIndex($columnIndex), $numColumns);
2180
        }
2181
2182
        throw new Exception('Columns to be deleted should at least start from column A (1)');
2183
    }
2184
2185
    /**
2186
     * Show gridlines?
2187
     *
2188
     * @return bool
2189
     */
2190 64
    public function getShowGridlines()
2191
    {
2192 64
        return $this->showGridlines;
2193
    }
2194
2195
    /**
2196
     * Set show gridlines.
2197
     *
2198
     * @param bool $pValue Show gridlines (true/false)
2199
     *
2200
     * @return Worksheet
2201
     */
2202 28
    public function setShowGridlines($pValue)
2203
    {
2204 28
        $this->showGridlines = $pValue;
2205
2206 28
        return $this;
2207
    }
2208
2209
    /**
2210
     * Print gridlines?
2211
     *
2212
     * @return bool
2213
     */
2214 60
    public function getPrintGridlines()
2215
    {
2216 60
        return $this->printGridlines;
2217
    }
2218
2219
    /**
2220
     * Set print gridlines.
2221
     *
2222
     * @param bool $pValue Print gridlines (true/false)
2223
     *
2224
     * @return Worksheet
2225
     */
2226 18
    public function setPrintGridlines($pValue)
2227
    {
2228 18
        $this->printGridlines = $pValue;
2229
2230 18
        return $this;
2231
    }
2232
2233
    /**
2234
     * Show row and column headers?
2235
     *
2236
     * @return bool
2237
     */
2238 60
    public function getShowRowColHeaders()
2239
    {
2240 60
        return $this->showRowColHeaders;
2241
    }
2242
2243
    /**
2244
     * Set show row and column headers.
2245
     *
2246
     * @param bool $pValue Show row and column headers (true/false)
2247
     *
2248
     * @return Worksheet
2249
     */
2250 25
    public function setShowRowColHeaders($pValue)
2251
    {
2252 25
        $this->showRowColHeaders = $pValue;
2253
2254 25
        return $this;
2255
    }
2256
2257
    /**
2258
     * Show summary below? (Row/Column outlining).
2259
     *
2260
     * @return bool
2261
     */
2262 60
    public function getShowSummaryBelow()
2263
    {
2264 60
        return $this->showSummaryBelow;
2265
    }
2266
2267
    /**
2268
     * Set show summary below.
2269
     *
2270
     * @param bool $pValue Show summary below (true/false)
2271
     *
2272
     * @return Worksheet
2273
     */
2274 25
    public function setShowSummaryBelow($pValue)
2275
    {
2276 25
        $this->showSummaryBelow = $pValue;
2277
2278 25
        return $this;
2279
    }
2280
2281
    /**
2282
     * Show summary right? (Row/Column outlining).
2283
     *
2284
     * @return bool
2285
     */
2286 60
    public function getShowSummaryRight()
2287
    {
2288 60
        return $this->showSummaryRight;
2289
    }
2290
2291
    /**
2292
     * Set show summary right.
2293
     *
2294
     * @param bool $pValue Show summary right (true/false)
2295
     *
2296
     * @return Worksheet
2297
     */
2298 25
    public function setShowSummaryRight($pValue)
2299
    {
2300 25
        $this->showSummaryRight = $pValue;
2301
2302 25
        return $this;
2303
    }
2304
2305
    /**
2306
     * Get comments.
2307
     *
2308
     * @return Comment[]
2309
     */
2310 64
    public function getComments()
2311
    {
2312 64
        return $this->comments;
2313
    }
2314
2315
    /**
2316
     * Set comments array for the entire sheet.
2317
     *
2318
     * @param array of Comment
0 ignored issues
show
Bug introduced by
The type PhpOffice\PhpSpreadsheet\Worksheet\of was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2319
     * @param mixed $pValue
2320
     *
2321
     * @return Worksheet
2322
     */
2323 15
    public function setComments(array $pValue)
2324
    {
2325 15
        $this->comments = $pValue;
2326
2327 15
        return $this;
2328
    }
2329
2330
    /**
2331
     * Get comment for cell.
2332
     *
2333
     * @param string $pCellCoordinate Cell coordinate to get comment for, eg: 'A1'
2334
     *
2335
     * @throws Exception
2336
     *
2337
     * @return Comment
2338
     */
2339 21
    public function getComment($pCellCoordinate)
2340
    {
2341
        // Uppercase coordinate
2342 21
        $pCellCoordinate = strtoupper($pCellCoordinate);
2343
2344 21 View Code Duplication
        if (strpos($pCellCoordinate, ':') !== false || strpos($pCellCoordinate, ',') !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2345
            throw new Exception('Cell coordinate string can not be a range of cells.');
2346 21
        } elseif (strpos($pCellCoordinate, '$') !== false) {
2347
            throw new Exception('Cell coordinate string must not be absolute.');
2348 21
        } elseif ($pCellCoordinate == '') {
2349
            throw new Exception('Cell coordinate can not be zero-length string.');
2350
        }
2351
2352
        // Check if we already have a comment for this cell.
2353 21
        if (isset($this->comments[$pCellCoordinate])) {
2354 13
            return $this->comments[$pCellCoordinate];
2355
        }
2356
2357
        // If not, create a new comment.
2358 21
        $newComment = new Comment();
2359 21
        $this->comments[$pCellCoordinate] = $newComment;
2360
2361 21
        return $newComment;
2362
    }
2363
2364
    /**
2365
     * Get comment for cell by using numeric cell coordinates.
2366
     *
2367
     * @param int $columnIndex Numeric column coordinate of the cell
2368
     * @param int $row Numeric row coordinate of the cell
2369
     *
2370
     * @return Comment
2371
     */
2372 2
    public function getCommentByColumnAndRow($columnIndex, $row)
2373
    {
2374 2
        return $this->getComment(Coordinate::stringFromColumnIndex($columnIndex) . $row);
2375
    }
2376
2377
    /**
2378
     * Get active cell.
2379
     *
2380
     * @return string Example: 'A1'
2381
     */
2382 64
    public function getActiveCell()
2383
    {
2384 64
        return $this->activeCell;
2385
    }
2386
2387
    /**
2388
     * Get selected cells.
2389
     *
2390
     * @return string
2391
     */
2392 63
    public function getSelectedCells()
2393
    {
2394 63
        return $this->selectedCells;
2395
    }
2396
2397
    /**
2398
     * Selected cell.
2399
     *
2400
     * @param string $pCoordinate Cell (i.e. A1)
2401
     *
2402
     * @return Worksheet
2403
     */
2404
    public function setSelectedCell($pCoordinate)
2405
    {
2406
        return $this->setSelectedCells($pCoordinate);
2407
    }
2408
2409
    /**
2410
     * Select a range of cells.
2411
     *
2412
     * @param string $pCoordinate Cell range, examples: 'A1', 'B2:G5', 'A:C', '3:6'
2413
     *
2414
     * @throws Exception
2415
     *
2416
     * @return Worksheet
2417
     */
2418 71
    public function setSelectedCells($pCoordinate)
2419
    {
2420
        // Uppercase coordinate
2421 71
        $pCoordinate = strtoupper($pCoordinate);
2422
2423
        // Convert 'A' to 'A:A'
2424 71
        $pCoordinate = preg_replace('/^([A-Z]+)$/', '${1}:${1}', $pCoordinate);
2425
2426
        // Convert '1' to '1:1'
2427 71
        $pCoordinate = preg_replace('/^([0-9]+)$/', '${1}:${1}', $pCoordinate);
2428
2429
        // Convert 'A:C' to 'A1:C1048576'
2430 71
        $pCoordinate = preg_replace('/^([A-Z]+):([A-Z]+)$/', '${1}1:${2}1048576', $pCoordinate);
2431
2432
        // Convert '1:3' to 'A1:XFD3'
2433 71
        $pCoordinate = preg_replace('/^([0-9]+):([0-9]+)$/', 'A${1}:XFD${2}', $pCoordinate);
2434
2435 71
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
2436 45
            list($first) = Coordinate::splitRange($pCoordinate);
2437 45
            $this->activeCell = $first[0];
2438
        } else {
2439 59
            $this->activeCell = $pCoordinate;
2440
        }
2441 71
        $this->selectedCells = $pCoordinate;
2442
2443 71
        return $this;
2444
    }
2445
2446
    /**
2447
     * Selected cell by using numeric cell coordinates.
2448
     *
2449
     * @param int $columnIndex Numeric column coordinate of the cell
2450
     * @param int $row Numeric row coordinate of the cell
2451
     *
2452
     * @throws Exception
2453
     *
2454
     * @return Worksheet
2455
     */
2456
    public function setSelectedCellByColumnAndRow($columnIndex, $row)
2457
    {
2458
        return $this->setSelectedCells(Coordinate::stringFromColumnIndex($columnIndex) . $row);
2459
    }
2460
2461
    /**
2462
     * Get right-to-left.
2463
     *
2464
     * @return bool
2465
     */
2466 60
    public function getRightToLeft()
2467
    {
2468 60
        return $this->rightToLeft;
2469
    }
2470
2471
    /**
2472
     * Set right-to-left.
2473
     *
2474
     * @param bool $value Right-to-left true/false
2475
     *
2476
     * @return Worksheet
2477
     */
2478 19
    public function setRightToLeft($value)
2479
    {
2480 19
        $this->rightToLeft = $value;
2481
2482 19
        return $this;
2483
    }
2484
2485
    /**
2486
     * Fill worksheet from values in array.
2487
     *
2488
     * @param array $source Source array
2489
     * @param mixed $nullValue Value in source array that stands for blank cell
2490
     * @param string $startCell Insert array starting from this cell address as the top left coordinate
2491
     * @param bool $strictNullComparison Apply strict comparison when testing for null values in the array
2492
     *
2493
     * @throws Exception
2494
     *
2495
     * @return Worksheet
2496
     */
2497 30
    public function fromArray(array $source, $nullValue = null, $startCell = 'A1', $strictNullComparison = false)
2498
    {
2499
        //    Convert a 1-D array to 2-D (for ease of looping)
2500 30
        if (!is_array(end($source))) {
2501 3
            $source = [$source];
2502
        }
2503
2504
        // start coordinate
2505 30
        list($startColumn, $startRow) = Coordinate::coordinateFromString($startCell);
2506
2507
        // Loop through $source
2508 30
        foreach ($source as $rowData) {
2509 30
            $currentColumn = $startColumn;
2510 30
            foreach ($rowData as $cellValue) {
2511 30
                if ($strictNullComparison) {
2512 3
                    if ($cellValue !== $nullValue) {
2513
                        // Set cell value
2514 3
                        $this->getCell($currentColumn . $startRow)->setValue($cellValue);
2515
                    }
2516
                } else {
2517 27
                    if ($cellValue != $nullValue) {
2518
                        // Set cell value
2519 27
                        $this->getCell($currentColumn . $startRow)->setValue($cellValue);
2520
                    }
2521
                }
2522 30
                ++$currentColumn;
2523
            }
2524 30
            ++$startRow;
2525
        }
2526
2527 30
        return $this;
2528
    }
2529
2530
    /**
2531
     * Create array from a range of cells.
2532
     *
2533
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
2534
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2535
     * @param bool $calculateFormulas Should formulas be calculated?
2536
     * @param bool $formatData Should formatting be applied to cell values?
2537
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2538
     *                               True - Return rows and columns indexed by their actual row and column IDs
2539
     *
2540
     * @return array
2541
     */
2542 25
    public function rangeToArray($pRange, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2543
    {
2544
        // Returnvalue
2545 25
        $returnValue = [];
2546
        //    Identify the range that we need to extract from the worksheet
2547 25
        list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($pRange);
2548 25
        $minCol = Coordinate::stringFromColumnIndex($rangeStart[0]);
2549 25
        $minRow = $rangeStart[1];
2550 25
        $maxCol = Coordinate::stringFromColumnIndex($rangeEnd[0]);
2551 25
        $maxRow = $rangeEnd[1];
2552
2553 25
        ++$maxCol;
2554
        // Loop through rows
2555 25
        $r = -1;
2556 25
        for ($row = $minRow; $row <= $maxRow; ++$row) {
2557 25
            $rRef = ($returnCellRef) ? $row : ++$r;
2558 25
            $c = -1;
2559
            // Loop through columns in the current row
2560 25
            for ($col = $minCol; $col != $maxCol; ++$col) {
2561 25
                $cRef = ($returnCellRef) ? $col : ++$c;
2562
                //    Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
2563
                //        so we test and retrieve directly against cellCollection
2564 25
                if ($this->cellCollection->has($col . $row)) {
2565
                    // Cell exists
2566 25
                    $cell = $this->cellCollection->get($col . $row);
2567 25
                    if ($cell->getValue() !== null) {
2568 25
                        if ($cell->getValue() instanceof RichText) {
2569 2
                            $returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
2570
                        } else {
2571 25
                            if ($calculateFormulas) {
2572 24
                                $returnValue[$rRef][$cRef] = $cell->getCalculatedValue();
2573
                            } else {
2574 2
                                $returnValue[$rRef][$cRef] = $cell->getValue();
2575
                            }
2576
                        }
2577
2578 25
                        if ($formatData) {
2579 24
                            $style = $this->parent->getCellXfByIndex($cell->getXfIndex());
2580 24
                            $returnValue[$rRef][$cRef] = NumberFormat::toFormattedString(
2581 24
                                $returnValue[$rRef][$cRef],
2582 25
                                ($style && $style->getNumberFormat()) ? $style->getNumberFormat()->getFormatCode() : NumberFormat::FORMAT_GENERAL
2583
                            );
2584
                        }
2585
                    } else {
2586
                        // Cell holds a NULL
2587 25
                        $returnValue[$rRef][$cRef] = $nullValue;
2588
                    }
2589
                } else {
2590
                    // Cell doesn't exist
2591 12
                    $returnValue[$rRef][$cRef] = $nullValue;
2592
                }
2593
            }
2594
        }
2595
2596
        // Return
2597 25
        return $returnValue;
2598
    }
2599
2600
    /**
2601
     * Create array from a range of cells.
2602
     *
2603
     * @param string $pNamedRange Name of the Named Range
2604
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2605
     * @param bool $calculateFormulas Should formulas be calculated?
2606
     * @param bool $formatData Should formatting be applied to cell values?
2607
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2608
     *                                True - Return rows and columns indexed by their actual row and column IDs
2609
     *
2610
     * @throws Exception
2611
     *
2612
     * @return array
2613
     */
2614
    public function namedRangeToArray($pNamedRange, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2615
    {
2616
        $namedRange = NamedRange::resolveRange($pNamedRange, $this);
2617
        if ($namedRange !== null) {
2618
            $pWorkSheet = $namedRange->getWorksheet();
2619
            $pCellRange = $namedRange->getRange();
2620
2621
            return $pWorkSheet->rangeToArray($pCellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
2622
        }
2623
2624
        throw new Exception('Named Range ' . $pNamedRange . ' does not exist.');
2625
    }
2626
2627
    /**
2628
     * Create array from worksheet.
2629
     *
2630
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2631
     * @param bool $calculateFormulas Should formulas be calculated?
2632
     * @param bool $formatData Should formatting be applied to cell values?
2633
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2634
     *                               True - Return rows and columns indexed by their actual row and column IDs
2635
     *
2636
     * @return array
2637
     */
2638 12
    public function toArray($nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2639
    {
2640
        // Garbage collect...
2641 12
        $this->garbageCollect();
2642
2643
        //    Identify the range that we need to extract from the worksheet
2644 12
        $maxCol = $this->getHighestColumn();
2645 12
        $maxRow = $this->getHighestRow();
2646
2647
        // Return
2648 12
        return $this->rangeToArray('A1:' . $maxCol . $maxRow, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
2649
    }
2650
2651
    /**
2652
     * Get row iterator.
2653
     *
2654
     * @param int $startRow The row number at which to start iterating
2655
     * @param int $endRow The row number at which to stop iterating
2656
     *
2657
     * @return RowIterator
2658
     */
2659 4
    public function getRowIterator($startRow = 1, $endRow = null)
2660
    {
2661 4
        return new RowIterator($this, $startRow, $endRow);
2662
    }
2663
2664
    /**
2665
     * Get column iterator.
2666
     *
2667
     * @param string $startColumn The column address at which to start iterating
2668
     * @param string $endColumn The column address at which to stop iterating
2669
     *
2670
     * @return ColumnIterator
2671
     */
2672
    public function getColumnIterator($startColumn = 'A', $endColumn = null)
2673
    {
2674
        return new ColumnIterator($this, $startColumn, $endColumn);
2675
    }
2676
2677
    /**
2678
     * Run PhpSpreadsheet garbage collector.
2679
     *
2680
     * @return Worksheet
2681
     */
2682 76
    public function garbageCollect()
2683
    {
2684
        // Flush cache
2685 76
        $this->cellCollection->get('A1');
2686
2687
        // Lookup highest column and highest row if cells are cleaned
2688 76
        $colRow = $this->cellCollection->getHighestRowAndColumn();
2689 76
        $highestRow = $colRow['row'];
2690 76
        $highestColumn = Coordinate::columnIndexFromString($colRow['column']);
2691
2692
        // Loop through column dimensions
2693 76
        foreach ($this->columnDimensions as $dimension) {
2694 33
            $highestColumn = max($highestColumn, Coordinate::columnIndexFromString($dimension->getColumnIndex()));
2695
        }
2696
2697
        // Loop through row dimensions
2698 76
        foreach ($this->rowDimensions as $dimension) {
2699 47
            $highestRow = max($highestRow, $dimension->getRowIndex());
2700
        }
2701
2702
        // Cache values
2703 76
        if ($highestColumn < 1) {
2704
            $this->cachedHighestColumn = 'A';
2705
        } else {
2706 76
            $this->cachedHighestColumn = Coordinate::stringFromColumnIndex($highestColumn);
2707
        }
2708 76
        $this->cachedHighestRow = $highestRow;
2709
2710
        // Return
2711 76
        return $this;
2712
    }
2713
2714
    /**
2715
     * Get hash code.
2716
     *
2717
     * @return string Hash code
2718
     */
2719 92
    public function getHashCode()
2720
    {
2721 92
        if ($this->dirty) {
2722 92
            $this->hash = md5($this->title . $this->autoFilter . ($this->protection->isProtectionEnabled() ? 't' : 'f') . __CLASS__);
2723 92
            $this->dirty = false;
2724
        }
2725
2726 92
        return $this->hash;
2727
    }
2728
2729
    /**
2730
     * Extract worksheet title from range.
2731
     *
2732
     * Example: extractSheetTitle("testSheet!A1") ==> 'A1'
2733
     * Example: extractSheetTitle("'testSheet 1'!A1", true) ==> array('testSheet 1', 'A1');
2734
     *
2735
     * @param string $pRange Range to extract title from
2736
     * @param bool $returnRange Return range? (see example)
2737
     *
2738
     * @return mixed
2739
     */
2740 1
    public static function extractSheetTitle($pRange, $returnRange = false)
2741
    {
2742
        // Sheet title included?
2743 1
        if (($sep = strpos($pRange, '!')) === false) {
2744
            return '';
2745
        }
2746
2747 1
        if ($returnRange) {
2748 1
            return [trim(substr($pRange, 0, $sep), "'"), substr($pRange, $sep + 1)];
1 ignored issue
show
Bug introduced by
It seems like substr($pRange, 0, $sep) can also be of type false; however, parameter $str of trim() does only seem to accept string, 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

2748
            return [trim(/** @scrutinizer ignore-type */ substr($pRange, 0, $sep), "'"), substr($pRange, $sep + 1)];
Loading history...
2749
        }
2750
2751
        return substr($pRange, $sep + 1);
2752
    }
2753
2754
    /**
2755
     * Get hyperlink.
2756
     *
2757
     * @param string $pCellCoordinate Cell coordinate to get hyperlink for, eg: 'A1'
2758
     */
2759 21
    public function getHyperlink($pCellCoordinate)
2760
    {
2761
        // return hyperlink if we already have one
2762 21
        if (isset($this->hyperlinkCollection[$pCellCoordinate])) {
2763 15
            return $this->hyperlinkCollection[$pCellCoordinate];
2764
        }
2765
2766
        // else create hyperlink
2767 21
        $this->hyperlinkCollection[$pCellCoordinate] = new Hyperlink();
2768
2769 21
        return $this->hyperlinkCollection[$pCellCoordinate];
2770
    }
2771
2772
    /**
2773
     * Set hyperlink.
2774
     *
2775
     * @param string $pCellCoordinate Cell coordinate to insert hyperlink, eg: 'A1'
2776
     * @param null|Hyperlink $pHyperlink
2777
     *
2778
     * @return Worksheet
2779
     */
2780 13
    public function setHyperlink($pCellCoordinate, Hyperlink $pHyperlink = null)
2781
    {
2782 13
        if ($pHyperlink === null) {
2783 13
            unset($this->hyperlinkCollection[$pCellCoordinate]);
2784
        } else {
2785 13
            $this->hyperlinkCollection[$pCellCoordinate] = $pHyperlink;
2786
        }
2787
2788 13
        return $this;
2789
    }
2790
2791
    /**
2792
     * Hyperlink at a specific coordinate exists?
2793
     *
2794
     * @param string $pCoordinate eg: 'A1'
2795
     *
2796
     * @return bool
2797
     */
2798 6
    public function hyperlinkExists($pCoordinate)
2799
    {
2800 6
        return isset($this->hyperlinkCollection[$pCoordinate]);
2801
    }
2802
2803
    /**
2804
     * Get collection of hyperlinks.
2805
     *
2806
     * @return Hyperlink[]
2807
     */
2808 64
    public function getHyperlinkCollection()
2809
    {
2810 64
        return $this->hyperlinkCollection;
2811
    }
2812
2813
    /**
2814
     * Get data validation.
2815
     *
2816
     * @param string $pCellCoordinate Cell coordinate to get data validation for, eg: 'A1'
2817
     */
2818 4
    public function getDataValidation($pCellCoordinate)
2819
    {
2820
        // return data validation if we already have one
2821 4
        if (isset($this->dataValidationCollection[$pCellCoordinate])) {
2822 2
            return $this->dataValidationCollection[$pCellCoordinate];
2823
        }
2824
2825
        // else create data validation
2826 4
        $this->dataValidationCollection[$pCellCoordinate] = new DataValidation();
2827
2828 4
        return $this->dataValidationCollection[$pCellCoordinate];
2829
    }
2830
2831
    /**
2832
     * Set data validation.
2833
     *
2834
     * @param string $pCellCoordinate Cell coordinate to insert data validation, eg: 'A1'
2835
     * @param null|DataValidation $pDataValidation
2836
     *
2837
     * @return Worksheet
2838
     */
2839
    public function setDataValidation($pCellCoordinate, DataValidation $pDataValidation = null)
2840
    {
2841
        if ($pDataValidation === null) {
2842
            unset($this->dataValidationCollection[$pCellCoordinate]);
2843
        } else {
2844
            $this->dataValidationCollection[$pCellCoordinate] = $pDataValidation;
2845
        }
2846
2847
        return $this;
2848
    }
2849
2850
    /**
2851
     * Data validation at a specific coordinate exists?
2852
     *
2853
     * @param string $pCoordinate eg: 'A1'
2854
     *
2855
     * @return bool
2856
     */
2857 3
    public function dataValidationExists($pCoordinate)
2858
    {
2859 3
        return isset($this->dataValidationCollection[$pCoordinate]);
2860
    }
2861
2862
    /**
2863
     * Get collection of data validations.
2864
     *
2865
     * @return DataValidation[]
2866
     */
2867 64
    public function getDataValidationCollection()
2868
    {
2869 64
        return $this->dataValidationCollection;
2870
    }
2871
2872
    /**
2873
     * Accepts a range, returning it as a range that falls within the current highest row and column of the worksheet.
2874
     *
2875
     * @param string $range
2876
     *
2877
     * @return string Adjusted range value
2878
     */
2879
    public function shrinkRangeToFit($range)
2880
    {
2881
        $maxCol = $this->getHighestColumn();
2882
        $maxRow = $this->getHighestRow();
2883
        $maxCol = Coordinate::columnIndexFromString($maxCol);
2884
2885
        $rangeBlocks = explode(' ', $range);
2886
        foreach ($rangeBlocks as &$rangeSet) {
2887
            $rangeBoundaries = Coordinate::getRangeBoundaries($rangeSet);
2888
2889 View Code Duplication
            if (Coordinate::columnIndexFromString($rangeBoundaries[0][0]) > $maxCol) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2890
                $rangeBoundaries[0][0] = Coordinate::stringFromColumnIndex($maxCol);
2891
            }
2892
            if ($rangeBoundaries[0][1] > $maxRow) {
2893
                $rangeBoundaries[0][1] = $maxRow;
2894
            }
2895 View Code Duplication
            if (Coordinate::columnIndexFromString($rangeBoundaries[1][0]) > $maxCol) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2896
                $rangeBoundaries[1][0] = Coordinate::stringFromColumnIndex($maxCol);
2897
            }
2898
            if ($rangeBoundaries[1][1] > $maxRow) {
2899
                $rangeBoundaries[1][1] = $maxRow;
2900
            }
2901
            $rangeSet = $rangeBoundaries[0][0] . $rangeBoundaries[0][1] . ':' . $rangeBoundaries[1][0] . $rangeBoundaries[1][1];
2902
        }
2903
        unset($rangeSet);
2904
        $stRange = implode(' ', $rangeBlocks);
2905
2906
        return $stRange;
2907
    }
2908
2909
    /**
2910
     * Get tab color.
2911
     *
2912
     * @return Color
2913
     */
2914 12
    public function getTabColor()
2915
    {
2916 12
        if ($this->tabColor === null) {
2917 12
            $this->tabColor = new Color();
2918
        }
2919
2920 12
        return $this->tabColor;
2921
    }
2922
2923
    /**
2924
     * Reset tab color.
2925
     *
2926
     * @return Worksheet
2927
     */
2928
    public function resetTabColor()
2929
    {
2930
        $this->tabColor = null;
2931
        unset($this->tabColor);
2932
2933
        return $this;
2934
    }
2935
2936
    /**
2937
     * Tab color set?
2938
     *
2939
     * @return bool
2940
     */
2941 69
    public function isTabColorSet()
2942
    {
2943 69
        return $this->tabColor !== null;
2944
    }
2945
2946
    /**
2947
     * Copy worksheet (!= clone!).
2948
     *
2949
     * @return Worksheet
2950
     */
2951
    public function copy()
2952
    {
2953
        $copied = clone $this;
2954
2955
        return $copied;
2956
    }
2957
2958
    /**
2959
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
2960
     */
2961 1
    public function __clone()
2962
    {
2963 1
        foreach ($this as $key => $val) {
2964 1
            if ($key == 'parent') {
2965 1
                continue;
2966
            }
2967
2968 1
            if (is_object($val) || (is_array($val))) {
2969 1
                if ($key == 'cellCollection') {
2970 1
                    $newCollection = $this->cellCollection->cloneCellCollection($this);
2971 1
                    $this->cellCollection = $newCollection;
2972 1
                } elseif ($key == 'drawingCollection') {
2973 1
                    $newCollection = new ArrayObject();
2974 1
                    foreach ($this->drawingCollection as $id => $item) {
2975
                        if (is_object($item)) {
2976
                            $newCollection[$id] = clone $this->drawingCollection[$id];
2977
                        }
2978
                    }
2979 1
                    $this->drawingCollection = $newCollection;
0 ignored issues
show
Documentation Bug introduced by
It seems like $newCollection of type ArrayObject is incompatible with the declared type PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing[] of property $drawingCollection.

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...
2980 1
                } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof AutoFilter)) {
2981 1
                    $newAutoFilter = clone $this->autoFilter;
2982 1
                    $this->autoFilter = $newAutoFilter;
2983 1
                    $this->autoFilter->setParent($this);
2984
                } else {
2985 1
                    $this->{$key} = unserialize(serialize($val));
2986
                }
2987
            }
2988
        }
2989 1
    }
2990
2991
    /**
2992
     * Define the code name of the sheet.
2993
     *
2994
     * @param string $pValue Same rule as Title minus space not allowed (but, like Excel, change
2995
     *                       silently space to underscore)
2996
     * @param bool $validate False to skip validation of new title. WARNING: This should only be set
2997
     *                       at parse time (by Readers), where titles can be assumed to be valid.
2998
     *
2999
     * @throws Exception
3000
     *
3001
     * @return Worksheet
3002
     */
3003 155
    public function setCodeName($pValue, $validate = true)
3004
    {
3005
        // Is this a 'rename' or not?
3006 155
        if ($this->getCodeName() == $pValue) {
3007
            return $this;
3008
        }
3009
3010 155
        if ($validate) {
3011 155
            $pValue = str_replace(' ', '_', $pValue); //Excel does this automatically without flinching, we are doing the same
3012
3013
            // Syntax check
3014
            // throw an exception if not valid
3015 155
            self::checkSheetCodeName($pValue);
3016
3017
            // We use the same code that setTitle to find a valid codeName else not using a space (Excel don't like) but a '_'
3018
3019 155
            if ($this->getParent()) {
3020
                // Is there already such sheet name?
3021 139
                if ($this->getParent()->sheetCodeNameExists($pValue)) {
3022
                    // Use name, but append with lowest possible integer
3023
3024 39
                    if (Shared\StringHelper::countCharacters($pValue) > 29) {
3025
                        $pValue = Shared\StringHelper::substring($pValue, 0, 29);
3026
                    }
3027 39
                    $i = 1;
3028 39 View Code Duplication
                    while ($this->getParent()->sheetCodeNameExists($pValue . '_' . $i)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3029 10
                        ++$i;
3030 10
                        if ($i == 10) {
3031
                            if (Shared\StringHelper::countCharacters($pValue) > 28) {
3032
                                $pValue = Shared\StringHelper::substring($pValue, 0, 28);
3033
                            }
3034 10
                        } elseif ($i == 100) {
3035
                            if (Shared\StringHelper::countCharacters($pValue) > 27) {
3036
                                $pValue = Shared\StringHelper::substring($pValue, 0, 27);
3037
                            }
3038
                        }
3039
                    }
3040
3041 39
                    $pValue = $pValue . '_' . $i; // ok, we have a valid name
3042
                }
3043
            }
3044
        }
3045
3046 155
        $this->codeName = $pValue;
3047
3048 155
        return $this;
3049
    }
3050
3051
    /**
3052
     * Return the code name of the sheet.
3053
     *
3054
     * @return null|string
3055
     */
3056 155
    public function getCodeName()
3057
    {
3058 155
        return $this->codeName;
3059
    }
3060
3061
    /**
3062
     * Sheet has a code name ?
3063
     *
3064
     * @return bool
3065
     */
3066
    public function hasCodeName()
3067
    {
3068
        return !($this->codeName === null);
3069
    }
3070
}
3071