Completed
Push — develop ( 3ee9cc...870d86 )
by Adrien
29:45
created

Worksheet::rangeToArray()   C

Complexity

Conditions 12
Paths 35

Size

Total Lines 57
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 12.144

Importance

Changes 0
Metric Value
cc 12
eloc 34
nc 35
nop 5
dl 0
loc 57
ccs 27
cts 30
cp 0.9
crap 12.144
rs 6.62
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet;
4
5
use ArrayObject;
6
use PhpOffice\PhpSpreadsheet\Collection\Cells;
7
use PhpOffice\PhpSpreadsheet\Collection\CellsFactory;
8
9
/**
10
 * Copyright (c) 2006 - 2016 PhpSpreadsheet.
11
 *
12
 * This library is free software; you can redistribute it and/or
13
 * modify it under the terms of the GNU Lesser General Public
14
 * License as published by the Free Software Foundation; either
15
 * version 2.1 of the License, or (at your option) any later version.
16
 *
17
 * This library is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20
 * Lesser General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Lesser General Public
23
 * License along with this library; if not, write to the Free Software
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25
 *
26
 * @category   PhpSpreadsheet
27
 *
28
 * @copyright  Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
29
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
30
 */
31
class Worksheet implements IComparable
32
{
33
    /* Break types */
34
    const BREAK_NONE = 0;
35
    const BREAK_ROW = 1;
36
    const BREAK_COLUMN = 2;
37
38
    /* Sheet state */
39
    const SHEETSTATE_VISIBLE = 'visible';
40
    const SHEETSTATE_HIDDEN = 'hidden';
41
    const SHEETSTATE_VERYHIDDEN = 'veryHidden';
42
43
    /**
44
     * Invalid characters in sheet title.
45
     *
46
     * @var array
47
     */
48
    private static $invalidCharacters = ['*', ':', '/', '\\', '?', '[', ']'];
49
50
    /**
51
     * Parent spreadsheet.
52
     *
53
     * @var Spreadsheet
54
     */
55
    private $parent;
56
57
    /**
58
     * Collection of cells.
59
     *
60
     * @var Cells
61
     */
62
    private $cellCollection;
63
64
    /**
65
     * Collection of row dimensions.
66
     *
67
     * @var Worksheet\RowDimension[]
68
     */
69
    private $rowDimensions = [];
70
71
    /**
72
     * Default row dimension.
73
     *
74
     * @var Worksheet\RowDimension
75
     */
76
    private $defaultRowDimension;
77
78
    /**
79
     * Collection of column dimensions.
80
     *
81
     * @var Worksheet\ColumnDimension[]
82
     */
83
    private $columnDimensions = [];
84
85
    /**
86
     * Default column dimension.
87
     *
88
     * @var Worksheet\ColumnDimension
89
     */
90
    private $defaultColumnDimension = null;
91
92
    /**
93
     * Collection of drawings.
94
     *
95
     * @var Worksheet\BaseDrawing[]
96
     */
97
    private $drawingCollection = null;
98
99
    /**
100
     * Collection of Chart objects.
101
     *
102
     * @var Chart[]
103
     */
104
    private $chartCollection = [];
105
106
    /**
107
     * Worksheet title.
108
     *
109
     * @var string
110
     */
111
    private $title;
112
113
    /**
114
     * Sheet state.
115
     *
116
     * @var string
117
     */
118
    private $sheetState;
119
120
    /**
121
     * Page setup.
122
     *
123
     * @var Worksheet\PageSetup
124
     */
125
    private $pageSetup;
126
127
    /**
128
     * Page margins.
129
     *
130
     * @var Worksheet\PageMargins
131
     */
132
    private $pageMargins;
133
134
    /**
135
     * Page header/footer.
136
     *
137
     * @var Worksheet\HeaderFooter
138
     */
139
    private $headerFooter;
140
141
    /**
142
     * Sheet view.
143
     *
144
     * @var Worksheet\SheetView
145
     */
146
    private $sheetView;
147
148
    /**
149
     * Protection.
150
     *
151
     * @var Worksheet\Protection
152
     */
153
    private $protection;
154
155
    /**
156
     * Collection of styles.
157
     *
158
     * @var Style[]
159
     */
160
    private $styles = [];
161
162
    /**
163
     * Conditional styles. Indexed by cell coordinate, e.g. 'A1'.
164
     *
165
     * @var array
166
     */
167
    private $conditionalStylesCollection = [];
168
169
    /**
170
     * Is the current cell collection sorted already?
171
     *
172
     * @var bool
173
     */
174
    private $cellCollectionIsSorted = false;
175
176
    /**
177
     * Collection of breaks.
178
     *
179
     * @var array
180
     */
181
    private $breaks = [];
182
183
    /**
184
     * Collection of merged cell ranges.
185
     *
186
     * @var array
187
     */
188
    private $mergeCells = [];
189
190
    /**
191
     * Collection of protected cell ranges.
192
     *
193
     * @var array
194
     */
195
    private $protectedCells = [];
196
197
    /**
198
     * Autofilter Range and selection.
199
     *
200
     * @var Worksheet\AutoFilter
201
     */
202
    private $autoFilter;
203
204
    /**
205
     * Freeze pane.
206
     *
207
     * @var string
208
     */
209
    private $freezePane = '';
210
211
    /**
212
     * Show gridlines?
213
     *
214
     * @var bool
215
     */
216
    private $showGridlines = true;
217
218
    /**
219
     * Print gridlines?
220
     *
221
     * @var bool
222
     */
223
    private $printGridlines = false;
224
225
    /**
226
     * Show row and column headers?
227
     *
228
     * @var bool
229
     */
230
    private $showRowColHeaders = true;
231
232
    /**
233
     * Show summary below? (Row/Column outline).
234
     *
235
     * @var bool
236
     */
237
    private $showSummaryBelow = true;
238
239
    /**
240
     * Show summary right? (Row/Column outline).
241
     *
242
     * @var bool
243
     */
244
    private $showSummaryRight = true;
245
246
    /**
247
     * Collection of comments.
248
     *
249
     * @var Comment[]
250
     */
251
    private $comments = [];
252
253
    /**
254
     * Active cell. (Only one!).
255
     *
256
     * @var string
257
     */
258
    private $activeCell = 'A1';
259
260
    /**
261
     * Selected cells.
262
     *
263
     * @var string
264
     */
265
    private $selectedCells = 'A1';
266
267
    /**
268
     * Cached highest column.
269
     *
270
     * @var string
271
     */
272
    private $cachedHighestColumn = 'A';
273
274
    /**
275
     * Cached highest row.
276
     *
277
     * @var int
278
     */
279
    private $cachedHighestRow = 1;
280
281
    /**
282
     * Right-to-left?
283
     *
284
     * @var bool
285
     */
286
    private $rightToLeft = false;
287
288
    /**
289
     * Hyperlinks. Indexed by cell coordinate, e.g. 'A1'.
290
     *
291
     * @var array
292
     */
293
    private $hyperlinkCollection = [];
294
295
    /**
296
     * Data validation objects. Indexed by cell coordinate, e.g. 'A1'.
297
     *
298
     * @var array
299
     */
300
    private $dataValidationCollection = [];
301
302
    /**
303
     * Tab color.
304
     *
305
     * @var Style\Color
306
     */
307
    private $tabColor;
308
309
    /**
310
     * Dirty flag.
311
     *
312
     * @var bool
313
     */
314
    private $dirty = true;
315
316
    /**
317
     * Hash.
318
     *
319
     * @var string
320
     */
321
    private $hash;
322
323
    /**
324
     * CodeName.
325
     *
326
     * @var string
327
     */
328
    private $codeName = null;
329
330
    /**
331
     * Create a new worksheet.
332
     *
333
     * @param Spreadsheet $parent
334
     * @param string $pTitle
335
     */
336 83
    public function __construct(Spreadsheet $parent = null, $pTitle = 'Worksheet')
337
    {
338
        // Set parent and title
339 83
        $this->parent = $parent;
340 83
        $this->setTitle($pTitle, false);
341
        // setTitle can change $pTitle
342 83
        $this->setCodeName($this->getTitle());
343 83
        $this->setSheetState(self::SHEETSTATE_VISIBLE);
344
345 83
        $this->cellCollection = CellsFactory::getInstance($this);
346
        // Set page setup
347 83
        $this->pageSetup = new Worksheet\PageSetup();
348
        // Set page margins
349 83
        $this->pageMargins = new Worksheet\PageMargins();
350
        // Set page header/footer
351 83
        $this->headerFooter = new Worksheet\HeaderFooter();
352
        // Set sheet view
353 83
        $this->sheetView = new Worksheet\SheetView();
354
        // Drawing collection
355 83
        $this->drawingCollection = new \ArrayObject();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \ArrayObject() of type object<ArrayObject> is incompatible with the declared type array<integer,object<Php...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...
356
        // Chart collection
357 83
        $this->chartCollection = new \ArrayObject();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \ArrayObject() of type object<ArrayObject> is incompatible with the declared type array<integer,object<Php...\PhpSpreadsheet\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...
358
        // Protection
359 83
        $this->protection = new Worksheet\Protection();
360
        // Default row dimension
361 83
        $this->defaultRowDimension = new Worksheet\RowDimension(null);
362
        // Default column dimension
363 83
        $this->defaultColumnDimension = new Worksheet\ColumnDimension(null);
364 83
        $this->autoFilter = new Worksheet\AutoFilter(null, $this);
365 83
    }
366
367
    /**
368
     * Disconnect all cells from this Worksheet object,
369
     * typically so that the worksheet object can be unset.
370
     */
371 1
    public function disconnectCells()
372
    {
373 1
        if ($this->cellCollection !== null) {
374 1
            $this->cellCollection->unsetWorksheetCells();
375 1
            $this->cellCollection = null;
376
        }
377
        //    detach ourself from the workbook, so that it can then delete this worksheet successfully
378 1
        $this->parent = null;
379 1
    }
380
381
    /**
382
     * Code to execute when this worksheet is unset().
383
     */
384 1
    public function __destruct()
385
    {
386 1
        Calculation::getInstance($this->parent)->clearCalculationCacheForWorksheet($this->title);
387
388 1
        $this->disconnectCells();
389 1
    }
390
391
    /**
392
     * Return the cell collection.
393
     *
394
     * @return Cells
395
     */
396 73
    public function getCellCollection()
397
    {
398 73
        return $this->cellCollection;
399
    }
400
401
    /**
402
     * Get array of invalid characters for sheet title.
403
     *
404
     * @return array
405
     */
406
    public static function getInvalidCharacters()
407
    {
408
        return self::$invalidCharacters;
409
    }
410
411
    /**
412
     * Check sheet code name for valid Excel syntax.
413
     *
414
     * @param string $pValue The string to check
415
     *
416
     * @throws Exception
417
     *
418
     * @return string The valid string
419
     */
420 83
    private static function checkSheetCodeName($pValue)
421
    {
422 83
        $CharCount = Shared\StringHelper::countCharacters($pValue);
423 83
        if ($CharCount == 0) {
424
            throw new Exception('Sheet code name cannot be empty.');
425
        }
426
        // Some of the printable ASCII characters are invalid:  * : / \ ? [ ] and  first and last characters cannot be a "'"
427 83
        if ((str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) ||
428 83
            (Shared\StringHelper::substring($pValue, -1, 1) == '\'') ||
429 83
            (Shared\StringHelper::substring($pValue, 0, 1) == '\'')) {
430
            throw new Exception('Invalid character found in sheet code name');
431
        }
432
433
        // Maximum 31 characters allowed for sheet title
434 83
        if ($CharCount > 31) {
435
            throw new Exception('Maximum 31 characters allowed in sheet code name.');
436
        }
437
438 83
        return $pValue;
439
    }
440
441
    /**
442
     * Check sheet title for valid Excel syntax.
443
     *
444
     * @param string $pValue The string to check
445
     *
446
     * @throws Exception
447
     *
448
     * @return string The valid string
449
     */
450 83
    private static function checkSheetTitle($pValue)
451
    {
452
        // Some of the printable ASCII characters are invalid:  * : / \ ? [ ]
453 83
        if (str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) {
454
            throw new Exception('Invalid character found in sheet title');
455
        }
456
457
        // Maximum 31 characters allowed for sheet title
458 83
        if (Shared\StringHelper::countCharacters($pValue) > 31) {
459
            throw new Exception('Maximum 31 characters allowed in sheet title.');
460
        }
461
462 83
        return $pValue;
463
    }
464
465
    /**
466
     * Get a sorted list of all cell coordinates currently held in the collection by row and column.
467
     *
468
     * @param bool $sorted Also sort the cell collection?
469
     *
470
     * @return string[]
471
     */
472 60
    public function getCoordinates($sorted = true)
473
    {
474 60
        if ($this->cellCollection == null) {
475
            return [];
476
        }
477
478 60
        if ($sorted) {
479 59
            return $this->cellCollection->getSortedCoordinates();
480
        }
481
482 59
        return $this->cellCollection->getCoordinates();
483
    }
484
485
    /**
486
     * Get collection of row dimensions.
487
     *
488
     * @return Worksheet\RowDimension[]
489
     */
490 58
    public function getRowDimensions()
491
    {
492 58
        return $this->rowDimensions;
493
    }
494
495
    /**
496
     * Get default row dimension.
497
     *
498
     * @return Worksheet\RowDimension
499
     */
500 58
    public function getDefaultRowDimension()
501
    {
502 58
        return $this->defaultRowDimension;
503
    }
504
505
    /**
506
     * Get collection of column dimensions.
507
     *
508
     * @return Worksheet\ColumnDimension[]
509
     */
510 58
    public function getColumnDimensions()
511
    {
512 58
        return $this->columnDimensions;
513
    }
514
515
    /**
516
     * Get default column dimension.
517
     *
518
     * @return Worksheet\ColumnDimension
519
     */
520 54
    public function getDefaultColumnDimension()
521
    {
522 54
        return $this->defaultColumnDimension;
523
    }
524
525
    /**
526
     * Get collection of drawings.
527
     *
528
     * @return Worksheet\BaseDrawing[]
529
     */
530 58
    public function getDrawingCollection()
531
    {
532 58
        return $this->drawingCollection;
533
    }
534
535
    /**
536
     * Get collection of charts.
537
     *
538
     * @return Chart[]
539
     */
540 14
    public function getChartCollection()
541
    {
542 14
        return $this->chartCollection;
543
    }
544
545
    /**
546
     * Add chart.
547
     *
548
     * @param Chart $pChart
549
     * @param int|null $iChartIndex Index where chart should go (0,1,..., or null for last)
550
     *
551
     * @return Chart
552
     */
553 14
    public function addChart(Chart $pChart, $iChartIndex = null)
554
    {
555 14
        $pChart->setWorksheet($this);
556 14
        if (is_null($iChartIndex)) {
557 14
            $this->chartCollection[] = $pChart;
558
        } else {
559
            // Insert the chart at the requested index
560
            array_splice($this->chartCollection, $iChartIndex, 0, [$pChart]);
561
        }
562
563 14
        return $pChart;
564
    }
565
566
    /**
567
     * Return the count of charts on this worksheet.
568
     *
569
     * @return int The number of charts
570
     */
571 14
    public function getChartCount()
572
    {
573 14
        return count($this->chartCollection);
574
    }
575
576
    /**
577
     * Get a chart by its index position.
578
     *
579
     * @param string $index Chart index position
580
     *
581
     * @throws Exception
582
     *
583
     * @return false|Chart
584
     */
585 13
    public function getChartByIndex($index)
586
    {
587 13
        $chartCount = count($this->chartCollection);
588 13
        if ($chartCount == 0) {
589
            return false;
590
        }
591 13
        if (is_null($index)) {
592
            $index = --$chartCount;
593
        }
594 13
        if (!isset($this->chartCollection[$index])) {
595
            return false;
596
        }
597
598 13
        return $this->chartCollection[$index];
599
    }
600
601
    /**
602
     * Return an array of the names of charts on this worksheet.
603
     *
604
     * @throws Exception
605
     *
606
     * @return string[] The names of charts
607
     */
608 1
    public function getChartNames()
609
    {
610 1
        $chartNames = [];
611 1
        foreach ($this->chartCollection as $chart) {
612 1
            $chartNames[] = $chart->getName();
613
        }
614
615 1
        return $chartNames;
616
    }
617
618
    /**
619
     * Get a chart by name.
620
     *
621
     * @param string $chartName Chart name
622
     *
623
     * @throws Exception
624
     *
625
     * @return false|Chart
626
     */
627 1
    public function getChartByName($chartName)
628
    {
629 1
        $chartCount = count($this->chartCollection);
630 1
        if ($chartCount == 0) {
631
            return false;
632
        }
633 1
        foreach ($this->chartCollection as $index => $chart) {
634 1
            if ($chart->getName() == $chartName) {
635 1
                return $this->chartCollection[$index];
636
            }
637
        }
638
639
        return false;
640
    }
641
642
    /**
643
     * Refresh column dimensions.
644
     *
645
     * @return Worksheet
646
     */
647 14
    public function refreshColumnDimensions()
648
    {
649 14
        $currentColumnDimensions = $this->getColumnDimensions();
650 14
        $newColumnDimensions = [];
651
652 14
        foreach ($currentColumnDimensions as $objColumnDimension) {
653 14
            $newColumnDimensions[$objColumnDimension->getColumnIndex()] = $objColumnDimension;
654
        }
655
656 14
        $this->columnDimensions = $newColumnDimensions;
657
658 14
        return $this;
659
    }
660
661
    /**
662
     * Refresh row dimensions.
663
     *
664
     * @return Worksheet
665
     */
666 2
    public function refreshRowDimensions()
667
    {
668 2
        $currentRowDimensions = $this->getRowDimensions();
669 2
        $newRowDimensions = [];
670
671 2
        foreach ($currentRowDimensions as $objRowDimension) {
672 2
            $newRowDimensions[$objRowDimension->getRowIndex()] = $objRowDimension;
673
        }
674
675 2
        $this->rowDimensions = $newRowDimensions;
676
677 2
        return $this;
678
    }
679
680
    /**
681
     * Calculate worksheet dimension.
682
     *
683
     * @return string String containing the dimension of this worksheet
684
     */
685 57
    public function calculateWorksheetDimension()
686
    {
687
        // Return
688 57
        return 'A1' . ':' . $this->getHighestColumn() . $this->getHighestRow();
689
    }
690
691
    /**
692
     * Calculate worksheet data dimension.
693
     *
694
     * @return string String containing the dimension of this worksheet that actually contain data
695
     */
696
    public function calculateWorksheetDataDimension()
697
    {
698
        // Return
699
        return 'A1' . ':' . $this->getHighestDataColumn() . $this->getHighestDataRow();
700
    }
701
702
    /**
703
     * Calculate widths for auto-size columns.
704
     *
705
     * @return Worksheet;
0 ignored issues
show
Documentation introduced by
The doc-type Worksheet; could not be parsed: Expected "|" or "end of type", but got ";" at position 9. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
706
     */
707 45
    public function calculateColumnWidths()
708
    {
709
        // initialize $autoSizes array
710 45
        $autoSizes = [];
711 45
        foreach ($this->getColumnDimensions() as $colDimension) {
712 26
            if ($colDimension->getAutoSize()) {
713 14
                $autoSizes[$colDimension->getColumnIndex()] = -1;
714
            }
715
        }
716
717
        // There is only something to do if there are some auto-size columns
718 45
        if (!empty($autoSizes)) {
719
            // build list of cells references that participate in a merge
720 14
            $isMergeCell = [];
721 14
            foreach ($this->getMergeCells() as $cells) {
722 11
                foreach (Cell::extractAllCellReferencesInRange($cells) as $cellReference) {
0 ignored issues
show
Documentation introduced by
$cells is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
723 11
                    $isMergeCell[$cellReference] = true;
724
                }
725
            }
726
727
            // loop through all cells in the worksheet
728 14
            foreach ($this->getCoordinates(false) as $coordinate) {
729 14
                $cell = $this->getCell($coordinate, false);
730 14
                if ($cell !== null && isset($autoSizes[$this->cellCollection->getCurrentColumn()])) {
731
                    //Determine if cell is in merge range
732 14
                    $isMerged = isset($isMergeCell[$this->cellCollection->getCurrentCoordinate()]);
733
734
                    //By default merged cells should be ignored
735 14
                    $isMergedButProceed = false;
736
737
                    //The only exception is if it's a merge range value cell of a 'vertical' randge (1 column wide)
738 14
                    if ($isMerged && $cell->isMergeRangeValueCell()) {
739
                        $range = $cell->getMergeRange();
740
                        $rangeBoundaries = Cell::rangeDimension($range);
0 ignored issues
show
Documentation introduced by
$range is of type array|false, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
741
                        if ($rangeBoundaries[0] == 1) {
742
                            $isMergedButProceed = true;
743
                        }
744
                    }
745
746
                    // Determine width if cell does not participate in a merge or does and is a value cell of 1-column wide range
747 14
                    if (!$isMerged || $isMergedButProceed) {
748
                        // Calculated value
749
                        // To formatted string
750 14
                        $cellValue = Style\NumberFormat::toFormattedString(
751 14
                            $cell->getCalculatedValue(),
752 14
                            $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode()
753
                        );
754
755 14
                        $autoSizes[$this->cellCollection->getCurrentColumn()] = max(
756 14
                            (float) $autoSizes[$this->cellCollection->getCurrentColumn()],
757 14
                            (float) Shared\Font::calculateColumnWidth(
758 14
                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont(),
759
                                $cellValue,
760 14
                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getAlignment()->getTextRotation(),
761 14
                                $this->getParent()->getDefaultStyle()->getFont()
762
                            )
763
                        );
764
                    }
765
                }
766
            }
767
768
            // adjust column widths
769 14
            foreach ($autoSizes as $columnIndex => $width) {
770 14
                if ($width == -1) {
771
                    $width = $this->getDefaultColumnDimension()->getWidth();
772
                }
773 14
                $this->getColumnDimension($columnIndex)->setWidth($width);
774
            }
775
        }
776
777 45
        return $this;
778
    }
779
780
    /**
781
     * Get parent.
782
     *
783
     * @return Spreadsheet
784
     */
785 83
    public function getParent()
786
    {
787 83
        return $this->parent;
788
    }
789
790
    /**
791
     * Re-bind parent.
792
     *
793
     * @param Spreadsheet $parent
794
     *
795
     * @return Worksheet
796
     */
797 1
    public function rebindParent(Spreadsheet $parent)
798
    {
799 1
        if ($this->parent !== null) {
800 1
            $namedRanges = $this->parent->getNamedRanges();
801 1
            foreach ($namedRanges as $namedRange) {
802
                $parent->addNamedRange($namedRange);
803
            }
804
805 1
            $this->parent->removeSheetByIndex(
806 1
                $this->parent->getIndex($this)
807
            );
808
        }
809 1
        $this->parent = $parent;
810
811 1
        return $this;
812
    }
813
814
    /**
815
     * Get title.
816
     *
817
     * @return string
818
     */
819 83
    public function getTitle()
820
    {
821 83
        return $this->title;
822
    }
823
824
    /**
825
     * Set title.
826
     *
827
     * @param string $pValue String containing the dimension of this worksheet
828
     * @param string $updateFormulaCellReferences boolean Flag indicating whether cell references in formulae should
829
     *            be updated to reflect the new sheet name.
830
     *          This should be left as the default true, unless you are
831
     *          certain that no formula cells on any worksheet contain
832
     *          references to this worksheet
833
     *
834
     * @return Worksheet
835
     */
836 83
    public function setTitle($pValue, $updateFormulaCellReferences = true)
837
    {
838
        // Is this a 'rename' or not?
839 83
        if ($this->getTitle() == $pValue) {
840 3
            return $this;
841
        }
842
843
        // Syntax check
844 83
        self::checkSheetTitle($pValue);
845
846
        // Old title
847 83
        $oldTitle = $this->getTitle();
848
849 83
        if ($this->parent) {
850
            // Is there already such sheet name?
851 73
            if ($this->parent->sheetNameExists($pValue)) {
852
                // Use name, but append with lowest possible integer
853
854 3
                if (Shared\StringHelper::countCharacters($pValue) > 29) {
855
                    $pValue = Shared\StringHelper::substring($pValue, 0, 29);
856
                }
857 3
                $i = 1;
858 3 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...
859
                    ++$i;
860
                    if ($i == 10) {
861
                        if (Shared\StringHelper::countCharacters($pValue) > 28) {
862
                            $pValue = Shared\StringHelper::substring($pValue, 0, 28);
863
                        }
864
                    } elseif ($i == 100) {
865
                        if (Shared\StringHelper::countCharacters($pValue) > 27) {
866
                            $pValue = Shared\StringHelper::substring($pValue, 0, 27);
867
                        }
868
                    }
869
                }
870
871 3
                $altTitle = $pValue . ' ' . $i;
872
873 3
                return $this->setTitle($altTitle, $updateFormulaCellReferences);
874
            }
875
        }
876
877
        // Set title
878 83
        $this->title = $pValue;
879 83
        $this->dirty = true;
880
881 83
        if ($this->parent && $this->parent->getCalculationEngine()) {
882
            // New title
883 73
            $newTitle = $this->getTitle();
884 73
            $this->parent->getCalculationEngine()
885 73
                ->renameCalculationCacheForWorksheet($oldTitle, $newTitle);
886 73
            if ($updateFormulaCellReferences) {
887 25
                ReferenceHelper::getInstance()->updateNamedFormulas($this->parent, $oldTitle, $newTitle);
888
            }
889
        }
890
891 83
        return $this;
892
    }
893
894
    /**
895
     * Get sheet state.
896
     *
897
     * @return string Sheet state (visible, hidden, veryHidden)
898
     */
899 54
    public function getSheetState()
900
    {
901 54
        return $this->sheetState;
902
    }
903
904
    /**
905
     * Set sheet state.
906
     *
907
     * @param string $value Sheet state (visible, hidden, veryHidden)
908
     *
909
     * @return Worksheet
910
     */
911 83
    public function setSheetState($value)
912
    {
913 83
        $this->sheetState = $value;
914
915 83
        return $this;
916
    }
917
918
    /**
919
     * Get page setup.
920
     *
921
     * @return Worksheet\PageSetup
922
     */
923 58
    public function getPageSetup()
924
    {
925 58
        return $this->pageSetup;
926
    }
927
928
    /**
929
     * Set page setup.
930
     *
931
     * @param Worksheet\PageSetup $pValue
932
     *
933
     * @return Worksheet
934
     */
935
    public function setPageSetup(Worksheet\PageSetup $pValue)
936
    {
937
        $this->pageSetup = $pValue;
938
939
        return $this;
940
    }
941
942
    /**
943
     * Get page margins.
944
     *
945
     * @return Worksheet\PageMargins
946
     */
947 58
    public function getPageMargins()
948
    {
949 58
        return $this->pageMargins;
950
    }
951
952
    /**
953
     * Set page margins.
954
     *
955
     * @param Worksheet\PageMargins $pValue
956
     *
957
     * @return Worksheet
958
     */
959
    public function setPageMargins(Worksheet\PageMargins $pValue)
960
    {
961
        $this->pageMargins = $pValue;
962
963
        return $this;
964
    }
965
966
    /**
967
     * Get page header/footer.
968
     *
969
     * @return Worksheet\HeaderFooter
970
     */
971 58
    public function getHeaderFooter()
972
    {
973 58
        return $this->headerFooter;
974
    }
975
976
    /**
977
     * Set page header/footer.
978
     *
979
     * @param Worksheet\HeaderFooter $pValue
980
     *
981
     * @return Worksheet
982
     */
983
    public function setHeaderFooter(Worksheet\HeaderFooter $pValue)
984
    {
985
        $this->headerFooter = $pValue;
986
987
        return $this;
988
    }
989
990
    /**
991
     * Get sheet view.
992
     *
993
     * @return Worksheet\SheetView
994
     */
995 54
    public function getSheetView()
996
    {
997 54
        return $this->sheetView;
998
    }
999
1000
    /**
1001
     * Set sheet view.
1002
     *
1003
     * @param Worksheet\SheetView $pValue
1004
     *
1005
     * @return Worksheet
1006
     */
1007
    public function setSheetView(Worksheet\SheetView $pValue)
1008
    {
1009
        $this->sheetView = $pValue;
1010
1011
        return $this;
1012
    }
1013
1014
    /**
1015
     * Get Protection.
1016
     *
1017
     * @return Worksheet\Protection
1018
     */
1019 58
    public function getProtection()
1020
    {
1021 58
        return $this->protection;
1022
    }
1023
1024
    /**
1025
     * Set Protection.
1026
     *
1027
     * @param Worksheet\Protection $pValue
1028
     *
1029
     * @return Worksheet
1030
     */
1031
    public function setProtection(Worksheet\Protection $pValue)
1032
    {
1033
        $this->protection = $pValue;
1034
        $this->dirty = true;
1035
1036
        return $this;
1037
    }
1038
1039
    /**
1040
     * Get highest worksheet column.
1041
     *
1042
     * @param string $row Return the data highest column for the specified row,
1043
     *                                     or the highest column of any row if no row number is passed
1044
     *
1045
     * @return string Highest column name
1046
     */
1047 62
    public function getHighestColumn($row = null)
1048
    {
1049 62
        if ($row == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $row of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
1050 62
            return $this->cachedHighestColumn;
1051
        }
1052
1053
        return $this->getHighestDataColumn($row);
1054
    }
1055
1056
    /**
1057
     * Get highest worksheet column that contains data.
1058
     *
1059
     * @param string $row Return the highest data column for the specified row,
1060
     *                                     or the highest data column of any row if no row number is passed
1061
     *
1062
     * @return string Highest column name that contains data
1063
     */
1064 13
    public function getHighestDataColumn($row = null)
1065
    {
1066 13
        return $this->cellCollection->getHighestColumn($row);
1067
    }
1068
1069
    /**
1070
     * Get highest worksheet row.
1071
     *
1072
     * @param string $column Return the highest data row for the specified column,
1073
     *                                     or the highest row of any column if no column letter is passed
1074
     *
1075
     * @return int Highest row number
1076
     */
1077 62
    public function getHighestRow($column = null)
1078
    {
1079 62
        if ($column == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $column of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
1080 62
            return $this->cachedHighestRow;
1081
        }
1082
1083
        return $this->getHighestDataRow($column);
1084
    }
1085
1086
    /**
1087
     * Get highest worksheet row that contains data.
1088
     *
1089
     * @param string $column Return the highest data row for the specified column,
1090
     *                                     or the highest data row of any column if no column letter is passed
1091
     *
1092
     * @return string Highest row number that contains data
1093
     */
1094 15
    public function getHighestDataRow($column = null)
1095
    {
1096 15
        return $this->cellCollection->getHighestRow($column);
1097
    }
1098
1099
    /**
1100
     * Get highest worksheet column and highest row that have cell records.
1101
     *
1102
     * @return array Highest column name and highest row number
1103
     */
1104
    public function getHighestRowAndColumn()
1105
    {
1106
        return $this->cellCollection->getHighestRowAndColumn();
1107
    }
1108
1109
    /**
1110
     * Set a cell value.
1111
     *
1112
     * @param string $pCoordinate Coordinate of the cell, eg: 'A1'
1113
     * @param mixed $pValue Value of the cell
1114
     *
1115
     * @return Worksheet
1116
     */
1117 38
    public function setCellValue($pCoordinate, $pValue)
1118
    {
1119 38
        $this->getCell($pCoordinate)->setValue($pValue);
1120
1121 38
        return $this;
1122
    }
1123
1124
    /**
1125
     * Set a cell value by using numeric cell coordinates.
1126
     *
1127
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
1128
     * @param int $pRow Numeric row coordinate of the cell
1129
     * @param mixed $pValue Value of the cell
1130
     *
1131
     * @return Worksheet
1132
     */
1133
    public function setCellValueByColumnAndRow($pColumn, $pRow, $pValue)
1134
    {
1135
        $this->getCellByColumnAndRow($pColumn, $pRow)->setValue($pValue);
1136
1137
        return $this;
1138
    }
1139
1140
    /**
1141
     * Set a cell value.
1142
     *
1143
     * @param string $pCoordinate Coordinate of the cell, eg: 'A1'
1144
     * @param mixed $pValue Value of the cell
1145
     * @param string $pDataType Explicit data type, see Cell\DataType::TYPE_*
1146
     *
1147
     * @return Worksheet
1148
     */
1149 1
    public function setCellValueExplicit($pCoordinate, $pValue, $pDataType)
1150
    {
1151
        // Set value
1152 1
        $this->getCell($pCoordinate)->setValueExplicit($pValue, $pDataType);
1153
1154 1
        return $this;
1155
    }
1156
1157
    /**
1158
     * Set a cell value by using numeric cell coordinates.
1159
     *
1160
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
1161
     * @param int $pRow Numeric row coordinate of the cell
1162
     * @param mixed $pValue Value of the cell
1163
     * @param string $pDataType Explicit data type, see Cell\DataType::TYPE_*
1164
     * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
0 ignored issues
show
Bug introduced by
There is no parameter named $returnCell. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1165
     *
1166
     * @return Worksheet
1167
     */
1168
    public function setCellValueExplicitByColumnAndRow($pColumn, $pRow, $pValue, $pDataType)
1169
    {
1170
        $this->getCellByColumnAndRow($pColumn, $pRow)->setValueExplicit($pValue, $pDataType);
1171
1172
        return $this;
1173
    }
1174
1175
    /**
1176
     * Get cell at a specific coordinate.
1177
     *
1178
     * @param string $pCoordinate Coordinate of the cell, eg: 'A1'
1179
     * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
1180
     *                                       already exist, or a null should be returned instead
1181
     *
1182
     * @throws Exception
1183
     *
1184
     * @return null|Cell Cell that was found/created or null
1185
     */
1186 72
    public function getCell($pCoordinate, $createIfNotExists = true)
1187
    {
1188
        // Check cell collection
1189 72
        if ($this->cellCollection->has(strtoupper($pCoordinate))) {
1190 69
            return $this->cellCollection->get($pCoordinate);
1191
        }
1192
1193
        // Worksheet reference?
1194 71 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...
1195 1
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1196
1197 1
            return $this->parent->getSheetByName($worksheetReference[0])->getCell(strtoupper($worksheetReference[1]), $createIfNotExists);
1198
        }
1199
1200
        // Named range?
1201 71
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1202
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1203
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1204
            if ($namedRange !== null) {
1205
                $pCoordinate = $namedRange->getRange();
1206
1207
                return $namedRange->getWorksheet()->getCell($pCoordinate, $createIfNotExists);
1208
            }
1209
        }
1210
1211
        // Uppercase coordinate
1212 71
        $pCoordinate = strtoupper($pCoordinate);
1213
1214 71 View Code Duplication
        if (strpos($pCoordinate, ':') !== false || 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...
1215
            throw new Exception('Cell coordinate can not be a range of cells.');
1216 71
        } elseif (strpos($pCoordinate, '$') !== false) {
1217
            throw new Exception('Cell coordinate must not be absolute.');
1218
        }
1219
1220
        // Create new cell object, if required
1221 71
        return $createIfNotExists ? $this->createNewCell($pCoordinate) : null;
1222
    }
1223
1224
    /**
1225
     * Get cell at a specific coordinate by using numeric cell coordinates.
1226
     *
1227
     * @param string $pColumn Numeric column coordinate of the cell
1228
     * @param string $pRow Numeric row coordinate of the cell
1229
     * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
1230
     *                                       already exist, or a null should be returned instead
1231
     *
1232
     * @return null|Cell Cell that was found/created or null
1233
     */
1234 38
    public function getCellByColumnAndRow($pColumn, $pRow, $createIfNotExists = true)
1235
    {
1236 38
        $columnLetter = Cell::stringFromColumnIndex($pColumn);
1237 38
        $coordinate = $columnLetter . $pRow;
1238
1239 38
        if ($this->cellCollection->has($coordinate)) {
1240 37
            return $this->cellCollection->get($coordinate);
1241
        }
1242
1243
        // Create new cell object, if required
1244 21
        return $createIfNotExists ? $this->createNewCell($coordinate) : null;
1245
    }
1246
1247
    /**
1248
     * Create a new cell at the specified coordinate.
1249
     *
1250
     * @param string $pCoordinate Coordinate of the cell
1251
     *
1252
     * @return Cell Cell that was created
1253
     */
1254 73
    private function createNewCell($pCoordinate)
1255
    {
1256 73
        $cell = new Cell(null, Cell\DataType::TYPE_NULL, $this);
1257 73
        $this->cellCollection->add($pCoordinate, $cell);
1258 73
        $this->cellCollectionIsSorted = false;
1259
1260
        // Coordinates
1261 73
        $aCoordinates = Cell::coordinateFromString($pCoordinate);
1262 73
        if (Cell::columnIndexFromString($this->cachedHighestColumn) < Cell::columnIndexFromString($aCoordinates[0])) {
1263 62
            $this->cachedHighestColumn = $aCoordinates[0];
1264
        }
1265 73
        $this->cachedHighestRow = max($this->cachedHighestRow, $aCoordinates[1]);
0 ignored issues
show
Documentation Bug introduced by
It seems like max($this->cachedHighestRow, $aCoordinates[1]) can also be of type string. However, the property $cachedHighestRow is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1266
1267
        // Cell needs appropriate xfIndex from dimensions records
1268
        //    but don't create dimension records if they don't already exist
1269 73
        $rowDimension = $this->getRowDimension($aCoordinates[1], false);
1270 73
        $columnDimension = $this->getColumnDimension($aCoordinates[0], false);
1271
1272 73
        if ($rowDimension !== null && $rowDimension->getXfIndex() > 0) {
1273
            // then there is a row dimension with explicit style, assign it to the cell
1274
            $cell->setXfIndex($rowDimension->getXfIndex());
1275 73
        } elseif ($columnDimension !== null && $columnDimension->getXfIndex() > 0) {
1276
            // then there is a column dimension, assign it to the cell
1277
            $cell->setXfIndex($columnDimension->getXfIndex());
1278
        }
1279
1280 73
        return $cell;
1281
    }
1282
1283
    /**
1284
     * Does the cell at a specific coordinate exist?
1285
     *
1286
     * @param string $pCoordinate Coordinate of the cell eg: 'A1'
1287
     *
1288
     * @throws Exception
1289
     *
1290
     * @return bool
1291
     */
1292 40
    public function cellExists($pCoordinate)
1293
    {
1294
        // Worksheet reference?
1295 40 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...
1296
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1297
1298
            return $this->parent->getSheetByName($worksheetReference[0])->cellExists(strtoupper($worksheetReference[1]));
1299
        }
1300
1301
        // Named range?
1302 40
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1303
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1304
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1305
            if ($namedRange !== null) {
1306
                $pCoordinate = $namedRange->getRange();
1307
                if ($this->getHashCode() != $namedRange->getWorksheet()->getHashCode()) {
1308
                    if (!$namedRange->getLocalOnly()) {
1309
                        return $namedRange->getWorksheet()->cellExists($pCoordinate);
1310
                    }
1311
                    throw new Exception('Named range ' . $namedRange->getName() . ' is not accessible from within sheet ' . $this->getTitle());
1312
                }
1313
            } else {
1314
                return false;
1315
            }
1316
        }
1317
1318
        // Uppercase coordinate
1319 40
        $pCoordinate = strtoupper($pCoordinate);
1320
1321 40
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
1322
            throw new Exception('Cell coordinate can not be a range of cells.');
1323 40
        } elseif (strpos($pCoordinate, '$') !== false) {
1324
            throw new Exception('Cell coordinate must not be absolute.');
1325
        }
1326
            // Coordinates
1327 40
            $aCoordinates = Cell::coordinateFromString($pCoordinate);
0 ignored issues
show
Unused Code introduced by
$aCoordinates is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1328
1329
            // Cell exists?
1330 40
            return $this->cellCollection->has($pCoordinate);
1331
    }
1332
1333
    /**
1334
     * Cell at a specific coordinate by using numeric cell coordinates exists?
1335
     *
1336
     * @param string $pColumn Numeric column coordinate of the cell (A = 0)
1337
     * @param string $pRow Numeric row coordinate of the cell
1338
     *
1339
     * @return bool
1340
     */
1341 6
    public function cellExistsByColumnAndRow($pColumn, $pRow)
1342
    {
1343 6
        return $this->cellExists(Cell::stringFromColumnIndex($pColumn) . $pRow);
1344
    }
1345
1346
    /**
1347
     * Get row dimension at a specific row.
1348
     *
1349
     * @param int $pRow Numeric index of the row
1350
     * @param mixed $create
1351
     *
1352
     * @return Worksheet\RowDimension
1353
     */
1354 73
    public function getRowDimension($pRow, $create = true)
1355
    {
1356
        // Found
1357 73
        $found = null;
0 ignored issues
show
Unused Code introduced by
$found is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1358
1359
        // Get row dimension
1360 73
        if (!isset($this->rowDimensions[$pRow])) {
1361 73
            if (!$create) {
1362 72
                return null;
1363
            }
1364 54
            $this->rowDimensions[$pRow] = new Worksheet\RowDimension($pRow);
1365
1366 54
            $this->cachedHighestRow = max($this->cachedHighestRow, $pRow);
1367
        }
1368
1369 54
        return $this->rowDimensions[$pRow];
1370
    }
1371
1372
    /**
1373
     * Get column dimension at a specific column.
1374
     *
1375
     * @param string $pColumn String index of the column eg: 'A'
1376
     * @param mixed $create
1377
     *
1378
     * @return Worksheet\ColumnDimension
1379
     */
1380 73
    public function getColumnDimension($pColumn, $create = true)
1381
    {
1382
        // Uppercase coordinate
1383 73
        $pColumn = strtoupper($pColumn);
1384
1385
        // Fetch dimensions
1386 73
        if (!isset($this->columnDimensions[$pColumn])) {
1387 73
            if (!$create) {
1388 72
                return null;
1389
            }
1390 28
            $this->columnDimensions[$pColumn] = new Worksheet\ColumnDimension($pColumn);
1391
1392 28
            if (Cell::columnIndexFromString($this->cachedHighestColumn) < Cell::columnIndexFromString($pColumn)) {
1393 12
                $this->cachedHighestColumn = $pColumn;
1394
            }
1395
        }
1396
1397 28
        return $this->columnDimensions[$pColumn];
1398
    }
1399
1400
    /**
1401
     * Get column dimension at a specific column by using numeric cell coordinates.
1402
     *
1403
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
1404
     *
1405
     * @return Worksheet\ColumnDimension
1406
     */
1407 3
    public function getColumnDimensionByColumn($pColumn)
1408
    {
1409 3
        return $this->getColumnDimension(Cell::stringFromColumnIndex($pColumn));
1410
    }
1411
1412
    /**
1413
     * Get styles.
1414
     *
1415
     * @return Style[]
1416
     */
1417
    public function getStyles()
1418
    {
1419
        return $this->styles;
1420
    }
1421
1422
    /**
1423
     * Get style for cell.
1424
     *
1425
     * @param string $pCellCoordinate Cell coordinate (or range) to get style for, eg: 'A1'
1426
     *
1427
     * @throws Exception
1428
     *
1429
     * @return Style
1430
     */
1431 37
    public function getStyle($pCellCoordinate)
1432
    {
1433
        // set this sheet as active
1434 37
        $this->parent->setActiveSheetIndex($this->parent->getIndex($this));
1435
1436
        // set cell coordinate as active
1437 37
        $this->setSelectedCells(strtoupper($pCellCoordinate));
1438
1439 37
        return $this->parent->getCellXfSupervisor();
1440
    }
1441
1442
    /**
1443
     * Get conditional styles for a cell.
1444
     *
1445
     * @param string $pCoordinate eg: 'A1'
1446
     *
1447
     * @return Style\Conditional[]
1448
     */
1449 2
    public function getConditionalStyles($pCoordinate)
1450
    {
1451 2
        $pCoordinate = strtoupper($pCoordinate);
1452 2
        if (!isset($this->conditionalStylesCollection[$pCoordinate])) {
1453 2
            $this->conditionalStylesCollection[$pCoordinate] = [];
1454
        }
1455
1456 2
        return $this->conditionalStylesCollection[$pCoordinate];
1457
    }
1458
1459
    /**
1460
     * Do conditional styles exist for this cell?
1461
     *
1462
     * @param string $pCoordinate eg: 'A1'
1463
     *
1464
     * @return bool
1465
     */
1466 12
    public function conditionalStylesExists($pCoordinate)
1467
    {
1468 12
        if (isset($this->conditionalStylesCollection[strtoupper($pCoordinate)])) {
1469
            return true;
1470
        }
1471
1472 12
        return false;
1473
    }
1474
1475
    /**
1476
     * Removes conditional styles for a cell.
1477
     *
1478
     * @param string $pCoordinate eg: 'A1'
1479
     *
1480
     * @return Worksheet
1481
     */
1482 13
    public function removeConditionalStyles($pCoordinate)
1483
    {
1484 13
        unset($this->conditionalStylesCollection[strtoupper($pCoordinate)]);
1485
1486 13
        return $this;
1487
    }
1488
1489
    /**
1490
     * Get collection of conditional styles.
1491
     *
1492
     * @return array
1493
     */
1494 54
    public function getConditionalStylesCollection()
1495
    {
1496 54
        return $this->conditionalStylesCollection;
1497
    }
1498
1499
    /**
1500
     * Set conditional styles.
1501
     *
1502
     * @param string $pCoordinate eg: 'A1'
1503
     * @param $pValue Style\Conditional[]
1504
     *
1505
     * @return Worksheet
1506
     */
1507 2
    public function setConditionalStyles($pCoordinate, $pValue)
1508
    {
1509 2
        $this->conditionalStylesCollection[strtoupper($pCoordinate)] = $pValue;
1510
1511 2
        return $this;
1512
    }
1513
1514
    /**
1515
     * Get style for cell by using numeric cell coordinates.
1516
     *
1517
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
1518
     * @param int $pRow Numeric row coordinate of the cell
1519
     * @param int pColumn2 Numeric column coordinate of the range cell (A = 0)
1520
     * @param int pRow2 Numeric row coordinate of the range cell
1521
     * @param null|mixed $pColumn2
1522
     * @param null|mixed $pRow2
1523
     *
1524
     * @return Style
1525
     */
1526
    public function getStyleByColumnAndRow($pColumn, $pRow, $pColumn2 = null, $pRow2 = null)
1527
    {
1528
        if (!is_null($pColumn2) && !is_null($pRow2)) {
1529
            $cellRange = Cell::stringFromColumnIndex($pColumn) . $pRow . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1530
1531
            return $this->getStyle($cellRange);
1532
        }
1533
1534
        return $this->getStyle(Cell::stringFromColumnIndex($pColumn) . $pRow);
1535
    }
1536
1537
    /**
1538
     * Duplicate cell style to a range of cells.
1539
     *
1540
     * Please note that this will overwrite existing cell styles for cells in range!
1541
     *
1542
     * @param Style $pCellStyle Cell style to duplicate
1543
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
1544
     *
1545
     * @throws Exception
1546
     *
1547
     * @return Worksheet
1548
     */
1549 2
    public function duplicateStyle(Style $pCellStyle, $pRange)
1550
    {
1551
        // make sure we have a real style and not supervisor
1552 2
        $style = $pCellStyle->getIsSupervisor() ? $pCellStyle->getSharedComponent() : $pCellStyle;
0 ignored issues
show
Unused Code introduced by
$style is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1553
1554
        // Add the style to the workbook if necessary
1555 2
        $workbook = $this->parent;
1556 2
        if ($existingStyle = $this->parent->getCellXfByHashCode($pCellStyle->getHashCode())) {
1557
            // there is already such cell Xf in our collection
1558 1
            $xfIndex = $existingStyle->getIndex();
1559
        } else {
1560
            // we don't have such a cell Xf, need to add
1561 2
            $workbook->addCellXf($pCellStyle);
1562 2
            $xfIndex = $pCellStyle->getIndex();
1563
        }
1564
1565
        // Calculate range outer borders
1566 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange . ':' . $pRange);
1567
1568
        // Make sure we can loop upwards on rows and columns
1569 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...
1570
            $tmp = $rangeStart;
1571
            $rangeStart = $rangeEnd;
1572
            $rangeEnd = $tmp;
1573
        }
1574
1575
        // Loop through cells and apply styles
1576 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...
1577 2
            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
1578 2
                $this->getCell(Cell::stringFromColumnIndex($col - 1) . $row)->setXfIndex($xfIndex);
1579
            }
1580
        }
1581
1582 2
        return $this;
1583
    }
1584
1585
    /**
1586
     * Duplicate conditional style to a range of cells.
1587
     *
1588
     * Please note that this will overwrite existing cell styles for cells in range!
1589
     *
1590
     * @param Style\Conditional[] $pCellStyle Cell style to duplicate
1591
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
1592
     *
1593
     * @throws Exception
1594
     *
1595
     * @return Worksheet
1596
     */
1597 2
    public function duplicateConditionalStyle(array $pCellStyle, $pRange = '')
1598
    {
1599 2
        foreach ($pCellStyle as $cellStyle) {
1600 2
            if (!($cellStyle instanceof Style\Conditional)) {
1601
                throw new Exception('Style is not a conditional style');
1602
            }
1603
        }
1604
1605
        // Calculate range outer borders
1606 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange . ':' . $pRange);
1607
1608
        // Make sure we can loop upwards on rows and columns
1609 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...
1610
            $tmp = $rangeStart;
1611
            $rangeStart = $rangeEnd;
1612
            $rangeEnd = $tmp;
1613
        }
1614
1615
        // Loop through cells and apply styles
1616 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...
1617 2
            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
1618 2
                $this->setConditionalStyles(Cell::stringFromColumnIndex($col - 1) . $row, $pCellStyle);
1619
            }
1620
        }
1621
1622 2
        return $this;
1623
    }
1624
1625
    /**
1626
     * Set break on a cell.
1627
     *
1628
     * @param string $pCoordinate Cell coordinate (e.g. A1)
1629
     * @param int $pBreak Break type (type of Worksheet::BREAK_*)
1630
     *
1631
     * @throws Exception
1632
     *
1633
     * @return Worksheet
1634
     */
1635 1
    public function setBreak($pCoordinate, $pBreak)
1636
    {
1637
        // Uppercase coordinate
1638 1
        $pCoordinate = strtoupper($pCoordinate);
1639
1640 1
        if ($pCoordinate != '') {
1641 1
            if ($pBreak == self::BREAK_NONE) {
1642
                if (isset($this->breaks[$pCoordinate])) {
1643
                    unset($this->breaks[$pCoordinate]);
1644
                }
1645
            } else {
1646 1
                $this->breaks[$pCoordinate] = $pBreak;
1647
            }
1648
        } else {
1649
            throw new Exception('No cell coordinate specified.');
1650
        }
1651
1652 1
        return $this;
1653
    }
1654
1655
    /**
1656
     * Set break on a cell by using numeric cell coordinates.
1657
     *
1658
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
1659
     * @param int $pRow Numeric row coordinate of the cell
1660
     * @param int $pBreak Break type (type of \PhpOffice\PhpSpreadsheet\Worksheet::BREAK_*)
1661
     *
1662
     * @return Worksheet
1663
     */
1664
    public function setBreakByColumnAndRow($pColumn, $pRow, $pBreak)
1665
    {
1666
        return $this->setBreak(Cell::stringFromColumnIndex($pColumn) . $pRow, $pBreak);
1667
    }
1668
1669
    /**
1670
     * Get breaks.
1671
     *
1672
     * @return array[]
1673
     */
1674 58
    public function getBreaks()
1675
    {
1676 58
        return $this->breaks;
1677
    }
1678
1679
    /**
1680
     * Set merge on a cell range.
1681
     *
1682
     * @param string $pRange Cell range (e.g. A1:E1)
1683
     *
1684
     * @throws Exception
1685
     *
1686
     * @return Worksheet
1687
     */
1688 16
    public function mergeCells($pRange)
1689
    {
1690
        // Uppercase coordinate
1691 16
        $pRange = strtoupper($pRange);
1692
1693 16
        if (strpos($pRange, ':') !== false) {
1694 16
            $this->mergeCells[$pRange] = $pRange;
1695
1696
            // make sure cells are created
1697
1698
            // get the cells in the range
1699 16
            $aReferences = Cell::extractAllCellReferencesInRange($pRange);
1700
1701
            // create upper left cell if it does not already exist
1702 16
            $upperLeft = $aReferences[0];
1703 16
            if (!$this->cellExists($upperLeft)) {
1704 12
                $this->getCell($upperLeft)->setValueExplicit(null, Cell\DataType::TYPE_NULL);
1705
            }
1706
1707
            // Blank out the rest of the cells in the range (if they exist)
1708 16
            $count = count($aReferences);
1709 16
            for ($i = 1; $i < $count; ++$i) {
1710 16
                if ($this->cellExists($aReferences[$i])) {
1711 4
                    $this->getCell($aReferences[$i])->setValueExplicit(null, Cell\DataType::TYPE_NULL);
1712
                }
1713
            }
1714
        } else {
1715
            throw new Exception('Merge must be set on a range of cells.');
1716
        }
1717
1718 16
        return $this;
1719
    }
1720
1721
    /**
1722
     * Set merge on a cell range by using numeric cell coordinates.
1723
     *
1724
     * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0)
1725
     * @param int $pRow1 Numeric row coordinate of the first cell
1726
     * @param int $pColumn2 Numeric column coordinate of the last cell (A = 0)
1727
     * @param int $pRow2 Numeric row coordinate of the last cell
1728
     *
1729
     * @throws Exception
1730
     *
1731
     * @return Worksheet
1732
     */
1733
    public function mergeCellsByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2)
1734
    {
1735
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1736
1737
        return $this->mergeCells($cellRange);
1738
    }
1739
1740
    /**
1741
     * Remove merge on a cell range.
1742
     *
1743
     * @param string $pRange Cell range (e.g. A1:E1)
1744
     *
1745
     * @throws Exception
1746
     *
1747
     * @return Worksheet
1748
     */
1749 11
    public function unmergeCells($pRange)
1750
    {
1751
        // Uppercase coordinate
1752 11
        $pRange = strtoupper($pRange);
1753
1754 11
        if (strpos($pRange, ':') !== false) {
1755 11
            if (isset($this->mergeCells[$pRange])) {
1756 11
                unset($this->mergeCells[$pRange]);
1757
            } else {
1758
                throw new Exception('Cell range ' . $pRange . ' not known as merged.');
1759
            }
1760
        } else {
1761
            throw new Exception('Merge can only be removed from a range of cells.');
1762
        }
1763
1764 11
        return $this;
1765
    }
1766
1767
    /**
1768
     * Remove merge on a cell range by using numeric cell coordinates.
1769
     *
1770
     * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0)
1771
     * @param int $pRow1 Numeric row coordinate of the first cell
1772
     * @param int $pColumn2 Numeric column coordinate of the last cell (A = 0)
1773
     * @param int $pRow2 Numeric row coordinate of the last cell
1774
     *
1775
     * @throws Exception
1776
     *
1777
     * @return Worksheet
1778
     */
1779
    public function unmergeCellsByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2)
1780
    {
1781
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1782
1783
        return $this->unmergeCells($cellRange);
1784
    }
1785
1786
    /**
1787
     * Get merge cells array.
1788
     *
1789
     * @return array[]
1790
     */
1791 58
    public function getMergeCells()
1792
    {
1793 58
        return $this->mergeCells;
1794
    }
1795
1796
    /**
1797
     * Set merge cells array for the entire sheet. Use instead mergeCells() to merge
1798
     * a single cell range.
1799
     *
1800
     * @param array
1801
     * @param mixed $pValue
1802
     */
1803 14
    public function setMergeCells(array $pValue)
1804
    {
1805 14
        $this->mergeCells = $pValue;
1806
1807 14
        return $this;
1808
    }
1809
1810
    /**
1811
     * Set protection on a cell range.
1812
     *
1813
     * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
1814
     * @param string $pPassword Password to unlock the protection
1815
     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
1816
     *
1817
     * @throws Exception
1818
     *
1819
     * @return Worksheet
1820
     */
1821 11
    public function protectCells($pRange, $pPassword, $pAlreadyHashed = false)
1822
    {
1823
        // Uppercase coordinate
1824 11
        $pRange = strtoupper($pRange);
1825
1826 11
        if (!$pAlreadyHashed) {
1827 11
            $pPassword = Shared\PasswordHasher::hashPassword($pPassword);
1828
        }
1829 11
        $this->protectedCells[$pRange] = $pPassword;
1830
1831 11
        return $this;
1832
    }
1833
1834
    /**
1835
     * Set protection on a cell range by using numeric cell coordinates.
1836
     *
1837
     * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0)
1838
     * @param int $pRow1 Numeric row coordinate of the first cell
1839
     * @param int $pColumn2 Numeric column coordinate of the last cell (A = 0)
1840
     * @param int $pRow2 Numeric row coordinate of the last cell
1841
     * @param string $pPassword Password to unlock the protection
1842
     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
1843
     *
1844
     * @throws Exception
1845
     *
1846
     * @return Worksheet
1847
     */
1848
    public function protectCellsByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2, $pPassword, $pAlreadyHashed = false)
1849
    {
1850
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1851
1852
        return $this->protectCells($cellRange, $pPassword, $pAlreadyHashed);
1853
    }
1854
1855
    /**
1856
     * Remove protection on a cell range.
1857
     *
1858
     * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
1859
     *
1860
     * @throws Exception
1861
     *
1862
     * @return Worksheet
1863
     */
1864 11
    public function unprotectCells($pRange)
1865
    {
1866
        // Uppercase coordinate
1867 11
        $pRange = strtoupper($pRange);
1868
1869 11
        if (isset($this->protectedCells[$pRange])) {
1870 11
            unset($this->protectedCells[$pRange]);
1871
        } else {
1872
            throw new Exception('Cell range ' . $pRange . ' not known as protected.');
1873
        }
1874
1875 11
        return $this;
1876
    }
1877
1878
    /**
1879
     * Remove protection on a cell range by using numeric cell coordinates.
1880
     *
1881
     * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0)
1882
     * @param int $pRow1 Numeric row coordinate of the first cell
1883
     * @param int $pColumn2 Numeric column coordinate of the last cell (A = 0)
1884
     * @param int $pRow2 Numeric row coordinate of the last cell
1885
     * @param string $pPassword Password to unlock the protection
1886
     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
1887
     *
1888
     * @throws Exception
1889
     *
1890
     * @return Worksheet
1891
     */
1892
    public function unprotectCellsByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2, $pPassword, $pAlreadyHashed = false)
0 ignored issues
show
Unused Code introduced by
The parameter $pPassword is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $pAlreadyHashed is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1893
    {
1894
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1895
1896
        return $this->unprotectCells($cellRange);
1897
    }
1898
1899
    /**
1900
     * Get protected cells.
1901
     *
1902
     * @return array[]
1903
     */
1904 58
    public function getProtectedCells()
1905
    {
1906 58
        return $this->protectedCells;
1907
    }
1908
1909
    /**
1910
     * Get Autofilter.
1911
     *
1912
     * @return Worksheet\AutoFilter
1913
     */
1914 59
    public function getAutoFilter()
1915
    {
1916 59
        return $this->autoFilter;
1917
    }
1918
1919
    /**
1920
     * Set AutoFilter.
1921
     *
1922
     * @param Worksheet\AutoFilter|string $pValue
1923
     *            A simple string containing a Cell range like 'A1:E10' is permitted for backward compatibility
1924
     *
1925
     * @throws Exception
1926
     *
1927
     * @return Worksheet
1928
     */
1929 4
    public function setAutoFilter($pValue)
1930
    {
1931 4
        $pRange = strtoupper($pValue);
0 ignored issues
show
Unused Code introduced by
$pRange is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1932 4
        if (is_string($pValue)) {
1933 4
            $this->autoFilter->setRange($pValue);
1934
        } elseif (is_object($pValue) && ($pValue instanceof Worksheet\AutoFilter)) {
1935
            $this->autoFilter = $pValue;
1936
        }
1937
1938 4
        return $this;
1939
    }
1940
1941
    /**
1942
     * Set Autofilter Range by using numeric cell coordinates.
1943
     *
1944
     * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0)
1945
     * @param int $pRow1 Numeric row coordinate of the first cell
1946
     * @param int $pColumn2 Numeric column coordinate of the second cell (A = 0)
1947
     * @param int $pRow2 Numeric row coordinate of the second cell
1948
     *
1949
     * @throws Exception
1950
     *
1951
     * @return Worksheet
1952
     */
1953
    public function setAutoFilterByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2)
1954
    {
1955
        return $this->setAutoFilter(
1956
            Cell::stringFromColumnIndex($pColumn1) . $pRow1
1957
            . ':' .
1958
            Cell::stringFromColumnIndex($pColumn2) . $pRow2
1959
        );
1960
    }
1961
1962
    /**
1963
     * Remove autofilter.
1964
     *
1965
     * @return Worksheet
1966
     */
1967
    public function removeAutoFilter()
1968
    {
1969
        $this->autoFilter->setRange(null);
1970
1971
        return $this;
1972
    }
1973
1974
    /**
1975
     * Get Freeze Pane.
1976
     *
1977
     * @return string
1978
     */
1979 58
    public function getFreezePane()
1980
    {
1981 58
        return $this->freezePane;
1982
    }
1983
1984
    /**
1985
     * Freeze Pane.
1986
     *
1987
     * @param string $pCell Cell (i.e. A2)
1988
     *                                    Examples:
1989
     *                                        A2 will freeze the rows above cell A2 (i.e row 1)
1990
     *                                        B1 will freeze the columns to the left of cell B1 (i.e column A)
1991
     *                                        B2 will freeze the rows above and to the left of cell A2
1992
     *                                            (i.e row 1 and column A)
1993
     *
1994
     * @throws Exception
1995
     *
1996
     * @return Worksheet
1997
     */
1998 4
    public function freezePane($pCell)
1999
    {
2000
        // Uppercase coordinate
2001 4
        $pCell = strtoupper($pCell);
2002 4
        if (strpos($pCell, ':') === false && strpos($pCell, ',') === false) {
2003 4
            $this->freezePane = $pCell;
2004
        } else {
2005
            throw new Exception('Freeze pane can not be set on a range of cells.');
2006
        }
2007
2008 4
        return $this;
2009
    }
2010
2011
    /**
2012
     * Freeze Pane by using numeric cell coordinates.
2013
     *
2014
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
2015
     * @param int $pRow Numeric row coordinate of the cell
2016
     *
2017
     * @throws Exception
2018
     *
2019
     * @return Worksheet
2020
     */
2021
    public function freezePaneByColumnAndRow($pColumn, $pRow)
2022
    {
2023
        return $this->freezePane(Cell::stringFromColumnIndex($pColumn) . $pRow);
2024
    }
2025
2026
    /**
2027
     * Unfreeze Pane.
2028
     *
2029
     * @return Worksheet
2030
     */
2031
    public function unfreezePane()
2032
    {
2033
        return $this->freezePane('');
2034
    }
2035
2036
    /**
2037
     * Insert a new row, updating all possible related data.
2038
     *
2039
     * @param int $pBefore Insert before this one
2040
     * @param int $pNumRows Number of rows to insert
2041
     *
2042
     * @throws Exception
2043
     *
2044
     * @return Worksheet
2045
     */
2046 12 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...
2047
    {
2048 12
        if ($pBefore >= 1) {
2049 12
            $objReferenceHelper = ReferenceHelper::getInstance();
2050 12
            $objReferenceHelper->insertNewBefore('A' . $pBefore, 0, $pNumRows, $this);
2051
        } else {
2052
            throw new Exception('Rows can only be inserted before at least row 1.');
2053
        }
2054
2055 12
        return $this;
2056
    }
2057
2058
    /**
2059
     * Insert a new column, updating all possible related data.
2060
     *
2061
     * @param int $pBefore Insert before this one, eg: 'A'
2062
     * @param int $pNumCols Number of columns to insert
2063
     *
2064
     * @throws Exception
2065
     *
2066
     * @return Worksheet
2067
     */
2068 11 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...
2069
    {
2070 11
        if (!is_numeric($pBefore)) {
2071 11
            $objReferenceHelper = ReferenceHelper::getInstance();
2072 11
            $objReferenceHelper->insertNewBefore($pBefore . '1', $pNumCols, 0, $this);
2073
        } else {
2074
            throw new Exception('Column references should not be numeric.');
2075
        }
2076
2077 11
        return $this;
2078
    }
2079
2080
    /**
2081
     * Insert a new column, updating all possible related data.
2082
     *
2083
     * @param int $pBefore Insert before this one (numeric column coordinate of the cell, A = 0)
2084
     * @param int $pNumCols Number of columns to insert
2085
     *
2086
     * @throws Exception
2087
     *
2088
     * @return Worksheet
2089
     */
2090
    public function insertNewColumnBeforeByIndex($pBefore, $pNumCols = 1)
2091
    {
2092
        if ($pBefore >= 0) {
2093
            return $this->insertNewColumnBefore(Cell::stringFromColumnIndex($pBefore), $pNumCols);
2094
        }
2095
        throw new Exception('Columns can only be inserted before at least column A (0).');
2096
    }
2097
2098
    /**
2099
     * Delete a row, updating all possible related data.
2100
     *
2101
     * @param int $pRow Remove starting with this one
2102
     * @param int $pNumRows Number of rows to remove
2103
     *
2104
     * @throws Exception
2105
     *
2106
     * @return Worksheet
2107
     */
2108 14
    public function removeRow($pRow, $pNumRows = 1)
2109
    {
2110 14
        if ($pRow >= 1) {
2111 14
            $highestRow = $this->getHighestDataRow();
2112 14
            $objReferenceHelper = ReferenceHelper::getInstance();
2113 14
            $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this);
2114 14
            for ($r = 0; $r < $pNumRows; ++$r) {
2115 14
                $this->getCellCollection()->removeRow($highestRow);
2116 14
                --$highestRow;
2117
            }
2118
        } else {
2119
            throw new Exception('Rows to be deleted should at least start from row 1.');
2120
        }
2121
2122 14
        return $this;
2123
    }
2124
2125
    /**
2126
     * Remove a column, updating all possible related data.
2127
     *
2128
     * @param string $pColumn Remove starting with this one, eg: 'A'
2129
     * @param int $pNumCols Number of columns to remove
2130
     *
2131
     * @throws Exception
2132
     *
2133
     * @return Worksheet
2134
     */
2135 11
    public function removeColumn($pColumn, $pNumCols = 1)
2136
    {
2137 11
        if (!is_numeric($pColumn)) {
2138 11
            $highestColumn = $this->getHighestDataColumn();
2139 11
            $pColumn = Cell::stringFromColumnIndex(Cell::columnIndexFromString($pColumn) - 1 + $pNumCols);
2140 11
            $objReferenceHelper = ReferenceHelper::getInstance();
2141 11
            $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this);
2142 11
            for ($c = 0; $c < $pNumCols; ++$c) {
2143 11
                $this->getCellCollection()->removeColumn($highestColumn);
2144 11
                $highestColumn = Cell::stringFromColumnIndex(Cell::columnIndexFromString($highestColumn) - 2);
2145
            }
2146
        } else {
2147
            throw new Exception('Column references should not be numeric.');
2148
        }
2149
2150 11
        return $this;
2151
    }
2152
2153
    /**
2154
     * Remove a column, updating all possible related data.
2155
     *
2156
     * @param int $pColumn Remove starting with this one (numeric column coordinate of the cell A = 0)
2157
     * @param int $pNumCols Number of columns to remove
2158
     *
2159
     * @throws Exception
2160
     *
2161
     * @return Worksheet
2162
     */
2163
    public function removeColumnByIndex($pColumn, $pNumCols = 1)
2164
    {
2165
        if ($pColumn >= 0) {
2166
            return $this->removeColumn(Cell::stringFromColumnIndex($pColumn), $pNumCols);
2167
        }
2168
        throw new Exception('Columns to be deleted should at least start from column 0');
2169
    }
2170
2171
    /**
2172
     * Show gridlines?
2173
     *
2174
     * @return bool
2175
     */
2176 58
    public function getShowGridlines()
2177
    {
2178 58
        return $this->showGridlines;
2179
    }
2180
2181
    /**
2182
     * Set show gridlines.
2183
     *
2184
     * @param bool $pValue Show gridlines (true/false)
2185
     *
2186
     * @return Worksheet
2187
     */
2188 11
    public function setShowGridlines($pValue)
2189
    {
2190 11
        $this->showGridlines = $pValue;
2191
2192 11
        return $this;
2193
    }
2194
2195
    /**
2196
     * Print gridlines?
2197
     *
2198
     * @return bool
2199
     */
2200 54
    public function getPrintGridlines()
2201
    {
2202 54
        return $this->printGridlines;
2203
    }
2204
2205
    /**
2206
     * Set print gridlines.
2207
     *
2208
     * @param bool $pValue Print gridlines (true/false)
2209
     *
2210
     * @return Worksheet
2211
     */
2212 4
    public function setPrintGridlines($pValue)
2213
    {
2214 4
        $this->printGridlines = $pValue;
2215
2216 4
        return $this;
2217
    }
2218
2219
    /**
2220
     * Show row and column headers?
2221
     *
2222
     * @return bool
2223
     */
2224 54
    public function getShowRowColHeaders()
2225
    {
2226 54
        return $this->showRowColHeaders;
2227
    }
2228
2229
    /**
2230
     * Set show row and column headers.
2231
     *
2232
     * @param bool $pValue Show row and column headers (true/false)
2233
     *
2234
     * @return Worksheet
2235
     */
2236 8
    public function setShowRowColHeaders($pValue)
2237
    {
2238 8
        $this->showRowColHeaders = $pValue;
2239
2240 8
        return $this;
2241
    }
2242
2243
    /**
2244
     * Show summary below? (Row/Column outlining).
2245
     *
2246
     * @return bool
2247
     */
2248 54
    public function getShowSummaryBelow()
2249
    {
2250 54
        return $this->showSummaryBelow;
2251
    }
2252
2253
    /**
2254
     * Set show summary below.
2255
     *
2256
     * @param bool $pValue Show summary below (true/false)
2257
     *
2258
     * @return Worksheet
2259
     */
2260 8
    public function setShowSummaryBelow($pValue)
2261
    {
2262 8
        $this->showSummaryBelow = $pValue;
2263
2264 8
        return $this;
2265
    }
2266
2267
    /**
2268
     * Show summary right? (Row/Column outlining).
2269
     *
2270
     * @return bool
2271
     */
2272 54
    public function getShowSummaryRight()
2273
    {
2274 54
        return $this->showSummaryRight;
2275
    }
2276
2277
    /**
2278
     * Set show summary right.
2279
     *
2280
     * @param bool $pValue Show summary right (true/false)
2281
     *
2282
     * @return Worksheet
2283
     */
2284 8
    public function setShowSummaryRight($pValue)
2285
    {
2286 8
        $this->showSummaryRight = $pValue;
2287
2288 8
        return $this;
2289
    }
2290
2291
    /**
2292
     * Get comments.
2293
     *
2294
     * @return Comment[]
2295
     */
2296 59
    public function getComments()
2297
    {
2298 59
        return $this->comments;
2299
    }
2300
2301
    /**
2302
     * Set comments array for the entire sheet.
2303
     *
2304
     * @param array of Comment
2305
     * @param mixed $pValue
2306
     *
2307
     * @return Worksheet
2308
     */
2309 14
    public function setComments(array $pValue)
2310
    {
2311 14
        $this->comments = $pValue;
2312
2313 14
        return $this;
2314
    }
2315
2316
    /**
2317
     * Get comment for cell.
2318
     *
2319
     * @param string $pCellCoordinate Cell coordinate to get comment for, eg: 'A1'
2320
     *
2321
     * @throws Exception
2322
     *
2323
     * @return Comment
2324
     */
2325 19
    public function getComment($pCellCoordinate)
2326
    {
2327
        // Uppercase coordinate
2328 19
        $pCellCoordinate = strtoupper($pCellCoordinate);
2329
2330 19 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...
2331
            throw new Exception('Cell coordinate string can not be a range of cells.');
2332 19
        } elseif (strpos($pCellCoordinate, '$') !== false) {
2333
            throw new Exception('Cell coordinate string must not be absolute.');
2334 19
        } elseif ($pCellCoordinate == '') {
2335
            throw new Exception('Cell coordinate can not be zero-length string.');
2336
        }
2337
2338
        // Check if we already have a comment for this cell.
2339 19
        if (isset($this->comments[$pCellCoordinate])) {
2340 12
            return $this->comments[$pCellCoordinate];
2341
        }
2342
2343
        // If not, create a new comment.
2344 19
        $newComment = new Comment();
2345 19
        $this->comments[$pCellCoordinate] = $newComment;
2346
2347 19
        return $newComment;
2348
    }
2349
2350
    /**
2351
     * Get comment for cell by using numeric cell coordinates.
2352
     *
2353
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
2354
     * @param int $pRow Numeric row coordinate of the cell
2355
     *
2356
     * @return Comment
2357
     */
2358 2
    public function getCommentByColumnAndRow($pColumn, $pRow)
2359
    {
2360 2
        return $this->getComment(Cell::stringFromColumnIndex($pColumn) . $pRow);
2361
    }
2362
2363
    /**
2364
     * Get active cell.
2365
     *
2366
     * @return string Example: 'A1'
2367
     */
2368 54
    public function getActiveCell()
2369
    {
2370 54
        return $this->activeCell;
2371
    }
2372
2373
    /**
2374
     * Get selected cells.
2375
     *
2376
     * @return string
2377
     */
2378 52
    public function getSelectedCells()
2379
    {
2380 52
        return $this->selectedCells;
2381
    }
2382
2383
    /**
2384
     * Selected cell.
2385
     *
2386
     * @param string $pCoordinate Cell (i.e. A1)
2387
     *
2388
     * @return Worksheet
2389
     */
2390
    public function setSelectedCell($pCoordinate)
2391
    {
2392
        return $this->setSelectedCells($pCoordinate);
2393
    }
2394
2395
    /**
2396
     * Select a range of cells.
2397
     *
2398
     * @param string $pCoordinate Cell range, examples: 'A1', 'B2:G5', 'A:C', '3:6'
2399
     *
2400
     * @throws Exception
2401
     *
2402
     * @return Worksheet
2403
     */
2404 45
    public function setSelectedCells($pCoordinate)
2405
    {
2406
        // Uppercase coordinate
2407 45
        $pCoordinate = strtoupper($pCoordinate);
2408
2409
        // Convert 'A' to 'A:A'
2410 45
        $pCoordinate = preg_replace('/^([A-Z]+)$/', '${1}:${1}', $pCoordinate);
2411
2412
        // Convert '1' to '1:1'
2413 45
        $pCoordinate = preg_replace('/^([0-9]+)$/', '${1}:${1}', $pCoordinate);
2414
2415
        // Convert 'A:C' to 'A1:C1048576'
2416 45
        $pCoordinate = preg_replace('/^([A-Z]+):([A-Z]+)$/', '${1}1:${2}1048576', $pCoordinate);
2417
2418
        // Convert '1:3' to 'A1:XFD3'
2419 45
        $pCoordinate = preg_replace('/^([0-9]+):([0-9]+)$/', 'A${1}:XFD${2}', $pCoordinate);
2420
2421 45
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
2422 25
            list($first) = Cell::splitRange($pCoordinate);
2423 25
            $this->activeCell = $first[0];
2424
        } else {
2425 38
            $this->activeCell = $pCoordinate;
2426
        }
2427 45
        $this->selectedCells = $pCoordinate;
2428
2429 45
        return $this;
2430
    }
2431
2432
    /**
2433
     * Selected cell by using numeric cell coordinates.
2434
     *
2435
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
2436
     * @param int $pRow Numeric row coordinate of the cell
2437
     *
2438
     * @throws Exception
2439
     *
2440
     * @return Worksheet
2441
     */
2442
    public function setSelectedCellByColumnAndRow($pColumn, $pRow)
2443
    {
2444
        return $this->setSelectedCells(Cell::stringFromColumnIndex($pColumn) . $pRow);
2445
    }
2446
2447
    /**
2448
     * Get right-to-left.
2449
     *
2450
     * @return bool
2451
     */
2452 54
    public function getRightToLeft()
2453
    {
2454 54
        return $this->rightToLeft;
2455
    }
2456
2457
    /**
2458
     * Set right-to-left.
2459
     *
2460
     * @param bool $value Right-to-left true/false
2461
     *
2462
     * @return Worksheet
2463
     */
2464 4
    public function setRightToLeft($value)
2465
    {
2466 4
        $this->rightToLeft = $value;
2467
2468 4
        return $this;
2469
    }
2470
2471
    /**
2472
     * Fill worksheet from values in array.
2473
     *
2474
     * @param array $source Source array
2475
     * @param mixed $nullValue Value in source array that stands for blank cell
2476
     * @param string $startCell Insert array starting from this cell address as the top left coordinate
2477
     * @param bool $strictNullComparison Apply strict comparison when testing for null values in the array
2478
     *
2479
     * @throws Exception
2480
     *
2481
     * @return Worksheet
2482
     */
2483 17
    public function fromArray(array $source, $nullValue = null, $startCell = 'A1', $strictNullComparison = false)
2484
    {
2485
        //    Convert a 1-D array to 2-D (for ease of looping)
2486 17
        if (!is_array(end($source))) {
2487 3
            $source = [$source];
2488
        }
2489
2490
        // start coordinate
2491 17
        list($startColumn, $startRow) = Cell::coordinateFromString($startCell);
2492
2493
        // Loop through $source
2494 17
        foreach ($source as $rowData) {
2495 17
            $currentColumn = $startColumn;
2496 17
            foreach ($rowData as $cellValue) {
2497 17
                if ($strictNullComparison) {
2498 1
                    if ($cellValue !== $nullValue) {
2499
                        // Set cell value
2500 1
                        $this->getCell($currentColumn . $startRow)->setValue($cellValue);
2501
                    }
2502
                } else {
2503 16
                    if ($cellValue != $nullValue) {
2504
                        // Set cell value
2505 16
                        $this->getCell($currentColumn . $startRow)->setValue($cellValue);
2506
                    }
2507
                }
2508 17
                ++$currentColumn;
2509
            }
2510 17
            ++$startRow;
2511
        }
2512
2513 17
        return $this;
2514
    }
2515
2516
    /**
2517
     * Create array from a range of cells.
2518
     *
2519
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
2520
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2521
     * @param bool $calculateFormulas Should formulas be calculated?
2522
     * @param bool $formatData Should formatting be applied to cell values?
2523
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2524
     *                               True - Return rows and columns indexed by their actual row and column IDs
2525
     *
2526
     * @return array
2527
     */
2528 2
    public function rangeToArray($pRange, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2529
    {
2530
        // Returnvalue
2531 2
        $returnValue = [];
2532
        //    Identify the range that we need to extract from the worksheet
2533 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange);
2534 2
        $minCol = Cell::stringFromColumnIndex($rangeStart[0] - 1);
2535 2
        $minRow = $rangeStart[1];
2536 2
        $maxCol = Cell::stringFromColumnIndex($rangeEnd[0] - 1);
2537 2
        $maxRow = $rangeEnd[1];
2538
2539 2
        ++$maxCol;
2540
        // Loop through rows
2541 2
        $r = -1;
2542 2
        for ($row = $minRow; $row <= $maxRow; ++$row) {
2543 2
            $rRef = ($returnCellRef) ? $row : ++$r;
2544 2
            $c = -1;
2545
            // Loop through columns in the current row
2546 2
            for ($col = $minCol; $col != $maxCol; ++$col) {
2547 2
                $cRef = ($returnCellRef) ? $col : ++$c;
2548
                //    Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
2549
                //        so we test and retrieve directly against cellCollection
2550 2
                if ($this->cellCollection->has($col . $row)) {
2551
                    // Cell exists
2552 2
                    $cell = $this->cellCollection->get($col . $row);
2553 2
                    if ($cell->getValue() !== null) {
2554 2
                        if ($cell->getValue() instanceof RichText) {
2555 1
                            $returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
2556
                        } else {
2557 2
                            if ($calculateFormulas) {
2558 2
                                $returnValue[$rRef][$cRef] = $cell->getCalculatedValue();
2559
                            } else {
2560
                                $returnValue[$rRef][$cRef] = $cell->getValue();
2561
                            }
2562
                        }
2563
2564 2
                        if ($formatData) {
2565 2
                            $style = $this->parent->getCellXfByIndex($cell->getXfIndex());
2566 2
                            $returnValue[$rRef][$cRef] = Style\NumberFormat::toFormattedString(
2567 2
                                $returnValue[$rRef][$cRef],
2568 2
                                ($style && $style->getNumberFormat()) ? $style->getNumberFormat()->getFormatCode() : Style\NumberFormat::FORMAT_GENERAL
2569
                            );
2570
                        }
2571
                    } else {
2572
                        // Cell holds a NULL
2573
                        $returnValue[$rRef][$cRef] = $nullValue;
2574
                    }
2575
                } else {
2576
                    // Cell doesn't exist
2577
                    $returnValue[$rRef][$cRef] = $nullValue;
2578
                }
2579
            }
2580
        }
2581
2582
        // Return
2583 2
        return $returnValue;
2584
    }
2585
2586
    /**
2587
     * Create array from a range of cells.
2588
     *
2589
     * @param string $pNamedRange Name of the Named Range
2590
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2591
     * @param bool $calculateFormulas Should formulas be calculated?
2592
     * @param bool $formatData Should formatting be applied to cell values?
2593
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2594
     *                                True - Return rows and columns indexed by their actual row and column IDs
2595
     *
2596
     * @throws Exception
2597
     *
2598
     * @return array
2599
     */
2600
    public function namedRangeToArray($pNamedRange, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2601
    {
2602
        $namedRange = NamedRange::resolveRange($pNamedRange, $this);
2603
        if ($namedRange !== null) {
2604
            $pWorkSheet = $namedRange->getWorksheet();
2605
            $pCellRange = $namedRange->getRange();
2606
2607
            return $pWorkSheet->rangeToArray($pCellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
2608
        }
2609
2610
        throw new Exception('Named Range ' . $pNamedRange . ' does not exist.');
2611
    }
2612
2613
    /**
2614
     * Create array from worksheet.
2615
     *
2616
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2617
     * @param bool $calculateFormulas Should formulas be calculated?
2618
     * @param bool $formatData Should formatting be applied to cell values?
2619
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2620
     *                               True - Return rows and columns indexed by their actual row and column IDs
2621
     *
2622
     * @return array
2623
     */
2624
    public function toArray($nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2625
    {
2626
        // Garbage collect...
2627
        $this->garbageCollect();
2628
2629
        //    Identify the range that we need to extract from the worksheet
2630
        $maxCol = $this->getHighestColumn();
2631
        $maxRow = $this->getHighestRow();
2632
        // Return
2633
        return $this->rangeToArray('A1:' . $maxCol . $maxRow, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
2634
    }
2635
2636
    /**
2637
     * Get row iterator.
2638
     *
2639
     * @param int $startRow The row number at which to start iterating
2640
     * @param int $endRow The row number at which to stop iterating
2641
     *
2642
     * @return Worksheet\RowIterator
2643
     */
2644 4
    public function getRowIterator($startRow = 1, $endRow = null)
2645
    {
2646 4
        return new Worksheet\RowIterator($this, $startRow, $endRow);
2647
    }
2648
2649
    /**
2650
     * Get column iterator.
2651
     *
2652
     * @param string $startColumn The column address at which to start iterating
2653
     * @param string $endColumn The column address at which to stop iterating
2654
     *
2655
     * @return Worksheet\ColumnIterator
2656
     */
2657
    public function getColumnIterator($startColumn = 'A', $endColumn = null)
2658
    {
2659
        return new Worksheet\ColumnIterator($this, $startColumn, $endColumn);
2660
    }
2661
2662
    /**
2663
     * Run PhpSpreadsheet garabage collector.
2664
     *
2665
     * @return Worksheet
2666
     */
2667 58
    public function garbageCollect()
2668
    {
2669
        // Flush cache
2670 58
        $this->cellCollection->get('A1');
2671
2672
        // Lookup highest column and highest row if cells are cleaned
2673 58
        $colRow = $this->cellCollection->getHighestRowAndColumn();
2674 58
        $highestRow = $colRow['row'];
2675 58
        $highestColumn = Cell::columnIndexFromString($colRow['column']);
2676
2677
        // Loop through column dimensions
2678 58
        foreach ($this->columnDimensions as $dimension) {
2679 26
            $highestColumn = max($highestColumn, Cell::columnIndexFromString($dimension->getColumnIndex()));
2680
        }
2681
2682
        // Loop through row dimensions
2683 58
        foreach ($this->rowDimensions as $dimension) {
2684 37
            $highestRow = max($highestRow, $dimension->getRowIndex());
2685
        }
2686
2687
        // Cache values
2688 58
        if ($highestColumn < 0) {
2689
            $this->cachedHighestColumn = 'A';
2690
        } else {
2691 58
            $this->cachedHighestColumn = Cell::stringFromColumnIndex(--$highestColumn);
2692
        }
2693 58
        $this->cachedHighestRow = $highestRow;
2694
2695
        // Return
2696 58
        return $this;
2697
    }
2698
2699
    /**
2700
     * Get hash code.
2701
     *
2702
     * @return string Hash code
2703
     */
2704 66
    public function getHashCode()
2705
    {
2706 66
        if ($this->dirty) {
2707 66
            $this->hash = md5($this->title . $this->autoFilter . ($this->protection->isProtectionEnabled() ? 't' : 'f') . __CLASS__);
2708 66
            $this->dirty = false;
2709
        }
2710
2711 66
        return $this->hash;
2712
    }
2713
2714
    /**
2715
     * Extract worksheet title from range.
2716
     *
2717
     * Example: extractSheetTitle("testSheet!A1") ==> 'A1'
2718
     * Example: extractSheetTitle("'testSheet 1'!A1", true) ==> array('testSheet 1', 'A1');
2719
     *
2720
     * @param string $pRange Range to extract title from
2721
     * @param bool $returnRange Return range? (see example)
2722
     *
2723
     * @return mixed
2724
     */
2725 1
    public static function extractSheetTitle($pRange, $returnRange = false)
2726
    {
2727
        // Sheet title included?
2728 1
        if (($sep = strpos($pRange, '!')) === false) {
2729
            return '';
2730
        }
2731
2732 1
        if ($returnRange) {
2733 1
            return [trim(substr($pRange, 0, $sep), "'"), substr($pRange, $sep + 1)];
2734
        }
2735
2736
        return substr($pRange, $sep + 1);
2737
    }
2738
2739
    /**
2740
     * Get hyperlink.
2741
     *
2742
     * @param string $pCellCoordinate Cell coordinate to get hyperlink for, eg: 'A1'
2743
     */
2744 18
    public function getHyperlink($pCellCoordinate)
2745
    {
2746
        // return hyperlink if we already have one
2747 18
        if (isset($this->hyperlinkCollection[$pCellCoordinate])) {
2748 13
            return $this->hyperlinkCollection[$pCellCoordinate];
2749
        }
2750
2751
        // else create hyperlink
2752 18
        $this->hyperlinkCollection[$pCellCoordinate] = new Cell\Hyperlink();
2753
2754 18
        return $this->hyperlinkCollection[$pCellCoordinate];
2755
    }
2756
2757
    /**
2758
     * Set hyperlink.
2759
     *
2760
     * @param string $pCellCoordinate Cell coordinate to insert hyperlink, eg: 'A1'
2761
     * @param Cell\Hyperlink|null $pHyperlink
2762
     *
2763
     * @return Worksheet
2764
     */
2765 12
    public function setHyperlink($pCellCoordinate, Cell\Hyperlink $pHyperlink = null)
2766
    {
2767 12
        if ($pHyperlink === null) {
2768 12
            unset($this->hyperlinkCollection[$pCellCoordinate]);
2769
        } else {
2770 12
            $this->hyperlinkCollection[$pCellCoordinate] = $pHyperlink;
2771
        }
2772
2773 12
        return $this;
2774
    }
2775
2776
    /**
2777
     * Hyperlink at a specific coordinate exists?
2778
     *
2779
     * @param string $pCoordinate eg: 'A1'
2780
     *
2781
     * @return bool
2782
     */
2783 6
    public function hyperlinkExists($pCoordinate)
2784
    {
2785 6
        return isset($this->hyperlinkCollection[$pCoordinate]);
2786
    }
2787
2788
    /**
2789
     * Get collection of hyperlinks.
2790
     *
2791
     * @return Cell\Hyperlink[]
2792
     */
2793 58
    public function getHyperlinkCollection()
2794
    {
2795 58
        return $this->hyperlinkCollection;
2796
    }
2797
2798
    /**
2799
     * Get data validation.
2800
     *
2801
     * @param string $pCellCoordinate Cell coordinate to get data validation for, eg: 'A1'
2802
     */
2803 2
    public function getDataValidation($pCellCoordinate)
2804
    {
2805
        // return data validation if we already have one
2806 2
        if (isset($this->dataValidationCollection[$pCellCoordinate])) {
2807
            return $this->dataValidationCollection[$pCellCoordinate];
2808
        }
2809
2810
        // else create data validation
2811 2
        $this->dataValidationCollection[$pCellCoordinate] = new Cell\DataValidation();
2812
2813 2
        return $this->dataValidationCollection[$pCellCoordinate];
2814
    }
2815
2816
    /**
2817
     * Set data validation.
2818
     *
2819
     * @param string $pCellCoordinate Cell coordinate to insert data validation, eg: 'A1'
2820
     * @param Cell\DataValidation|null $pDataValidation
2821
     *
2822
     * @return Worksheet
2823
     */
2824
    public function setDataValidation($pCellCoordinate, Cell\DataValidation $pDataValidation = null)
2825
    {
2826
        if ($pDataValidation === null) {
2827
            unset($this->dataValidationCollection[$pCellCoordinate]);
2828
        } else {
2829
            $this->dataValidationCollection[$pCellCoordinate] = $pDataValidation;
2830
        }
2831
2832
        return $this;
2833
    }
2834
2835
    /**
2836
     * Data validation at a specific coordinate exists?
2837
     *
2838
     * @param string $pCoordinate eg: 'A1'
2839
     *
2840
     * @return bool
2841
     */
2842
    public function dataValidationExists($pCoordinate)
2843
    {
2844
        return isset($this->dataValidationCollection[$pCoordinate]);
2845
    }
2846
2847
    /**
2848
     * Get collection of data validations.
2849
     *
2850
     * @return Cell\DataValidation[]
2851
     */
2852 58
    public function getDataValidationCollection()
2853
    {
2854 58
        return $this->dataValidationCollection;
2855
    }
2856
2857
    /**
2858
     * Accepts a range, returning it as a range that falls within the current highest row and column of the worksheet.
2859
     *
2860
     * @param string $range
2861
     *
2862
     * @return string Adjusted range value
2863
     */
2864
    public function shrinkRangeToFit($range)
2865
    {
2866
        $maxCol = $this->getHighestColumn();
2867
        $maxRow = $this->getHighestRow();
2868
        $maxCol = Cell::columnIndexFromString($maxCol);
2869
2870
        $rangeBlocks = explode(' ', $range);
2871
        foreach ($rangeBlocks as &$rangeSet) {
2872
            $rangeBoundaries = Cell::getRangeBoundaries($rangeSet);
2873
2874 View Code Duplication
            if (Cell::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...
2875
                $rangeBoundaries[0][0] = Cell::stringFromColumnIndex($maxCol);
2876
            }
2877
            if ($rangeBoundaries[0][1] > $maxRow) {
2878
                $rangeBoundaries[0][1] = $maxRow;
2879
            }
2880 View Code Duplication
            if (Cell::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...
2881
                $rangeBoundaries[1][0] = Cell::stringFromColumnIndex($maxCol);
2882
            }
2883
            if ($rangeBoundaries[1][1] > $maxRow) {
2884
                $rangeBoundaries[1][1] = $maxRow;
2885
            }
2886
            $rangeSet = $rangeBoundaries[0][0] . $rangeBoundaries[0][1] . ':' . $rangeBoundaries[1][0] . $rangeBoundaries[1][1];
2887
        }
2888
        unset($rangeSet);
2889
        $stRange = implode(' ', $rangeBlocks);
2890
2891
        return $stRange;
2892
    }
2893
2894
    /**
2895
     * Get tab color.
2896
     *
2897
     * @return Style\Color
2898
     */
2899 11
    public function getTabColor()
2900
    {
2901 11
        if ($this->tabColor === null) {
2902 11
            $this->tabColor = new Style\Color();
2903
        }
2904
2905 11
        return $this->tabColor;
2906
    }
2907
2908
    /**
2909
     * Reset tab color.
2910
     *
2911
     * @return Worksheet
2912
     */
2913
    public function resetTabColor()
2914
    {
2915
        $this->tabColor = null;
2916
        unset($this->tabColor);
2917
2918
        return $this;
2919
    }
2920
2921
    /**
2922
     * Tab color set?
2923
     *
2924
     * @return bool
2925
     */
2926 54
    public function isTabColorSet()
2927
    {
2928 54
        return $this->tabColor !== null;
2929
    }
2930
2931
    /**
2932
     * Copy worksheet (!= clone!).
2933
     *
2934
     * @return Worksheet
2935
     */
2936
    public function copy()
2937
    {
2938
        $copied = clone $this;
2939
2940
        return $copied;
2941
    }
2942
2943
    /**
2944
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
2945
     */
2946 1
    public function __clone()
2947
    {
2948 1
        foreach ($this as $key => $val) {
0 ignored issues
show
Bug introduced by
The expression $this of type this<PhpOffice\PhpSpreadsheet\Worksheet> is not traversable.
Loading history...
2949 1
            if ($key == 'parent') {
2950 1
                continue;
2951
            }
2952
2953 1
            if (is_object($val) || (is_array($val))) {
2954 1
                if ($key == 'cellCollection') {
2955 1
                    $newCollection = $this->cellCollection->cloneCellCollection($this);
2956 1
                    $this->cellCollection = $newCollection;
2957 1
                } elseif ($key == 'drawingCollection') {
2958 1
                    $newCollection = new ArrayObject();
2959 1
                    foreach ($this->drawingCollection as $id => $item) {
2960
                        if (is_object($item)) {
2961
                            $newCollection[$id] = clone $this->drawingCollection[$id];
2962
                        }
2963
                    }
2964 1
                    $this->drawingCollection = $newCollection;
0 ignored issues
show
Documentation Bug introduced by
It seems like $newCollection of type object<ArrayObject> is incompatible with the declared type array<integer,object<Php...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...
2965 1
                } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof Worksheet\AutoFilter)) {
2966 1
                    $newAutoFilter = clone $this->autoFilter;
2967 1
                    $this->autoFilter = $newAutoFilter;
2968 1
                    $this->autoFilter->setParent($this);
2969
                } else {
2970 1
                    $this->{$key} = unserialize(serialize($val));
2971
                }
2972
            }
2973
        }
2974 1
    }
2975
2976
    /**
2977
     * Define the code name of the sheet.
2978
     *
2979
     * @param null|string Same rule as Title minus space not allowed (but, like Excel, change silently space to underscore)
2980
     * @param null|mixed $pValue
2981
     *
2982
     * @throws Exception
2983
     *
2984
     * @return objWorksheet
2985
     */
2986 83
    public function setCodeName($pValue)
2987
    {
2988
        // Is this a 'rename' or not?
2989 83
        if ($this->getCodeName() == $pValue) {
2990
            return $this;
2991
        }
2992 83
        $pValue = str_replace(' ', '_', $pValue); //Excel does this automatically without flinching, we are doing the same
2993
        // Syntax check
2994
        // throw an exception if not valid
2995 83
        self::checkSheetCodeName($pValue);
2996
2997
        // We use the same code that setTitle to find a valid codeName else not using a space (Excel don't like) but a '_'
2998
2999 83
        if ($this->getParent()) {
3000
            // Is there already such sheet name?
3001 73
            if ($this->getParent()->sheetCodeNameExists($pValue)) {
3002
                // Use name, but append with lowest possible integer
3003
3004 25
                if (Shared\StringHelper::countCharacters($pValue) > 29) {
3005
                    $pValue = Shared\StringHelper::substring($pValue, 0, 29);
3006
                }
3007 25
                $i = 1;
3008 25 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...
3009 2
                    ++$i;
3010 2
                    if ($i == 10) {
3011
                        if (Shared\StringHelper::countCharacters($pValue) > 28) {
3012
                            $pValue = Shared\StringHelper::substring($pValue, 0, 28);
3013
                        }
3014 2
                    } elseif ($i == 100) {
3015
                        if (Shared\StringHelper::countCharacters($pValue) > 27) {
3016
                            $pValue = Shared\StringHelper::substring($pValue, 0, 27);
3017
                        }
3018
                    }
3019
                }
3020
3021 25
                $pValue = $pValue . '_' . $i; // ok, we have a valid name
3022
                //codeName is'nt used in formula : no need to call for an update
3023
                //return $this->setTitle($altTitle, $updateFormulaCellReferences);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3024
            }
3025
        }
3026
3027 83
        $this->codeName = $pValue;
3028
3029 83
        return $this;
3030
    }
3031
3032
    /**
3033
     * Return the code name of the sheet.
3034
     *
3035
     * @return null|string
3036
     */
3037 83
    public function getCodeName()
3038
    {
3039 83
        return $this->codeName;
3040
    }
3041
3042
    /**
3043
     * Sheet has a code name ?
3044
     *
3045
     * @return bool
3046
     */
3047
    public function hasCodeName()
3048
    {
3049
        return !(is_null($this->codeName));
3050
    }
3051
}
3052