Completed
Push — master ( ce1e83...7d4dc7 )
by Adrien
37:08 queued 29:31
created

Worksheet::setCodeName()   C

Complexity

Conditions 11
Paths 6

Size

Total Lines 47
Code Lines 22

Duplication

Lines 12
Ratio 25.53 %

Code Coverage

Tests 16
CRAP Score 13.4538

Importance

Changes 0
Metric Value
cc 11
eloc 22
nc 6
nop 2
dl 12
loc 47
ccs 16
cts 22
cp 0.7272
crap 13.4538
rs 5.2653
c 0
b 0
f 0

How to fix   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 95
    public function __construct(Spreadsheet $parent = null, $pTitle = 'Worksheet')
337
    {
338
        // Set parent and title
339 95
        $this->parent = $parent;
340 95
        $this->setTitle($pTitle, false);
341
        // setTitle can change $pTitle
342 95
        $this->setCodeName($this->getTitle());
343 95
        $this->setSheetState(self::SHEETSTATE_VISIBLE);
344
345 95
        $this->cellCollection = CellsFactory::getInstance($this);
346
        // Set page setup
347 95
        $this->pageSetup = new Worksheet\PageSetup();
348
        // Set page margins
349 95
        $this->pageMargins = new Worksheet\PageMargins();
350
        // Set page header/footer
351 95
        $this->headerFooter = new Worksheet\HeaderFooter();
352
        // Set sheet view
353 95
        $this->sheetView = new Worksheet\SheetView();
354
        // Drawing collection
355 95
        $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 95
        $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 95
        $this->protection = new Worksheet\Protection();
360
        // Default row dimension
361 95
        $this->defaultRowDimension = new Worksheet\RowDimension(null);
362
        // Default column dimension
363 95
        $this->defaultColumnDimension = new Worksheet\ColumnDimension(null);
364 95
        $this->autoFilter = new Worksheet\AutoFilter(null, $this);
365 95
    }
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 77
    public function getCellCollection()
397
    {
398 77
        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 95
    private static function checkSheetCodeName($pValue)
421
    {
422 95
        $CharCount = Shared\StringHelper::countCharacters($pValue);
423 95
        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 95
        if ((str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) ||
428 95
            (Shared\StringHelper::substring($pValue, -1, 1) == '\'') ||
429 95
            (Shared\StringHelper::substring($pValue, 0, 1) == '\'')) {
430 1
            throw new Exception('Invalid character found in sheet code name');
431
        }
432
433
        // Maximum 31 characters allowed for sheet title
434 95
        if ($CharCount > 31) {
435 1
            throw new Exception('Maximum 31 characters allowed in sheet code name.');
436
        }
437
438 95
        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 95
    private static function checkSheetTitle($pValue)
451
    {
452
        // Some of the printable ASCII characters are invalid:  * : / \ ? [ ]
453 95
        if (str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) {
454 1
            throw new Exception('Invalid character found in sheet title');
455
        }
456
457
        // Maximum 31 characters allowed for sheet title
458 95
        if (Shared\StringHelper::countCharacters($pValue) > 31) {
459 1
            throw new Exception('Maximum 31 characters allowed in sheet title.');
460
        }
461
462 95
        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 65
    public function getCoordinates($sorted = true)
473
    {
474 65
        if ($this->cellCollection == null) {
475
            return [];
476
        }
477
478 65
        if ($sorted) {
479 63
            return $this->cellCollection->getSortedCoordinates();
480
        }
481
482 64
        return $this->cellCollection->getCoordinates();
483
    }
484
485
    /**
486
     * Get collection of row dimensions.
487
     *
488
     * @return Worksheet\RowDimension[]
489
     */
490 62
    public function getRowDimensions()
491
    {
492 62
        return $this->rowDimensions;
493
    }
494
495
    /**
496
     * Get default row dimension.
497
     *
498
     * @return Worksheet\RowDimension
499
     */
500 62
    public function getDefaultRowDimension()
501
    {
502 62
        return $this->defaultRowDimension;
503
    }
504
505
    /**
506
     * Get collection of column dimensions.
507
     *
508
     * @return Worksheet\ColumnDimension[]
509
     */
510 62
    public function getColumnDimensions()
511
    {
512 62
        return $this->columnDimensions;
513
    }
514
515
    /**
516
     * Get default column dimension.
517
     *
518
     * @return Worksheet\ColumnDimension
519
     */
520 58
    public function getDefaultColumnDimension()
521
    {
522 58
        return $this->defaultColumnDimension;
523
    }
524
525
    /**
526
     * Get collection of drawings.
527
     *
528
     * @return Worksheet\BaseDrawing[]
529
     */
530 62
    public function getDrawingCollection()
531
    {
532 62
        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 15
    public function refreshColumnDimensions()
648
    {
649 15
        $currentColumnDimensions = $this->getColumnDimensions();
650 15
        $newColumnDimensions = [];
651
652 15
        foreach ($currentColumnDimensions as $objColumnDimension) {
653 15
            $newColumnDimensions[$objColumnDimension->getColumnIndex()] = $objColumnDimension;
654
        }
655
656 15
        $this->columnDimensions = $newColumnDimensions;
657
658 15
        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 61
    public function calculateWorksheetDimension()
686
    {
687
        // Return
688 61
        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 47
    public function calculateColumnWidths()
708
    {
709
        // initialize $autoSizes array
710 47
        $autoSizes = [];
711 47
        foreach ($this->getColumnDimensions() as $colDimension) {
712 26
            if ($colDimension->getAutoSize()) {
713 26
                $autoSizes[$colDimension->getColumnIndex()] = -1;
714
            }
715
        }
716
717
        // There is only something to do if there are some auto-size columns
718 47
        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 47
        return $this;
778
    }
779
780
    /**
781
     * Get parent.
782
     *
783
     * @return Spreadsheet
784
     */
785 95
    public function getParent()
786
    {
787 95
        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 95
    public function getTitle()
820
    {
821 95
        return $this->title;
822
    }
823
824
    /**
825
     * Set title.
826
     *
827
     * @param string $pValue String containing the dimension of this worksheet
828
     * @param bool $updateFormulaCellReferences 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
     * @param bool $validate False to skip validation of new title. WARNING: This should only be set
834
     *                       at parse time (by Readers), where titles can be assumed to be valid.
835
     *
836
     * @return Worksheet
837
     */
838 95
    public function setTitle($pValue, $updateFormulaCellReferences = true, $validate = true)
839
    {
840
        // Is this a 'rename' or not?
841 95
        if ($this->getTitle() == $pValue) {
842 4
            return $this;
843
        }
844
845
        // Old title
846 95
        $oldTitle = $this->getTitle();
847
848 95
        if ($validate) {
849
            // Syntax check
850 95
            self::checkSheetTitle($pValue);
851
852 95
            if ($this->parent) {
853
                // Is there already such sheet name?
854 79
                if ($this->parent->sheetNameExists($pValue)) {
855
                    // Use name, but append with lowest possible integer
856
857 5
                    if (Shared\StringHelper::countCharacters($pValue) > 29) {
858
                        $pValue = Shared\StringHelper::substring($pValue, 0, 29);
859
                    }
860 5
                    $i = 1;
861 5 View Code Duplication
                    while ($this->parent->sheetNameExists($pValue . ' ' . $i)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

Loading history...
1196 1
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1197
1198 1
            return $this->parent->getSheetByName($worksheetReference[0])->getCell(strtoupper($worksheetReference[1]), $createIfNotExists);
1199
        }
1200
1201
        // Named range?
1202 75
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1203 75
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1204
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1205
            if ($namedRange !== null) {
1206
                $pCoordinate = $namedRange->getRange();
1207
1208
                return $namedRange->getWorksheet()->getCell($pCoordinate, $createIfNotExists);
1209
            }
1210
        }
1211
1212
        // Uppercase coordinate
1213 75
        $pCoordinate = strtoupper($pCoordinate);
1214
1215 75 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...
1216
            throw new Exception('Cell coordinate can not be a range of cells.');
1217 75
        } elseif (strpos($pCoordinate, '$') !== false) {
1218
            throw new Exception('Cell coordinate must not be absolute.');
1219
        }
1220
1221
        // Create new cell object, if required
1222 75
        return $createIfNotExists ? $this->createNewCell($pCoordinate) : null;
1223
    }
1224
1225
    /**
1226
     * Get cell at a specific coordinate by using numeric cell coordinates.
1227
     *
1228
     * @param string $pColumn Numeric column coordinate of the cell
1229
     * @param string $pRow Numeric row coordinate of the cell
1230
     * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
1231
     *                                       already exist, or a null should be returned instead
1232
     *
1233
     * @return null|Cell Cell that was found/created or null
1234
     */
1235 40
    public function getCellByColumnAndRow($pColumn, $pRow, $createIfNotExists = true)
1236
    {
1237 40
        $columnLetter = Cell::stringFromColumnIndex($pColumn);
1238 40
        $coordinate = $columnLetter . $pRow;
1239
1240 40
        if ($this->cellCollection->has($coordinate)) {
1241 39
            return $this->cellCollection->get($coordinate);
1242
        }
1243
1244
        // Create new cell object, if required
1245 23
        return $createIfNotExists ? $this->createNewCell($coordinate) : null;
1246
    }
1247
1248
    /**
1249
     * Create a new cell at the specified coordinate.
1250
     *
1251
     * @param string $pCoordinate Coordinate of the cell
1252
     *
1253
     * @return Cell Cell that was created
1254
     */
1255 77
    private function createNewCell($pCoordinate)
1256
    {
1257 77
        $cell = new Cell(null, Cell\DataType::TYPE_NULL, $this);
1258 77
        $this->cellCollection->add($pCoordinate, $cell);
1259 77
        $this->cellCollectionIsSorted = false;
1260
1261
        // Coordinates
1262 77
        $aCoordinates = Cell::coordinateFromString($pCoordinate);
1263 77
        if (Cell::columnIndexFromString($this->cachedHighestColumn) < Cell::columnIndexFromString($aCoordinates[0])) {
1264 66
            $this->cachedHighestColumn = $aCoordinates[0];
1265
        }
1266 77
        $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...
1267
1268
        // Cell needs appropriate xfIndex from dimensions records
1269
        //    but don't create dimension records if they don't already exist
1270 77
        $rowDimension = $this->getRowDimension($aCoordinates[1], false);
1271 77
        $columnDimension = $this->getColumnDimension($aCoordinates[0], false);
1272
1273 77
        if ($rowDimension !== null && $rowDimension->getXfIndex() > 0) {
1274
            // then there is a row dimension with explicit style, assign it to the cell
1275
            $cell->setXfIndex($rowDimension->getXfIndex());
1276 77
        } elseif ($columnDimension !== null && $columnDimension->getXfIndex() > 0) {
1277
            // then there is a column dimension, assign it to the cell
1278
            $cell->setXfIndex($columnDimension->getXfIndex());
1279
        }
1280
1281 77
        return $cell;
1282
    }
1283
1284
    /**
1285
     * Does the cell at a specific coordinate exist?
1286
     *
1287
     * @param string $pCoordinate Coordinate of the cell eg: 'A1'
1288
     *
1289
     * @throws Exception
1290
     *
1291
     * @return bool
1292
     */
1293 42
    public function cellExists($pCoordinate)
1294
    {
1295
        // Worksheet reference?
1296 42 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...
1297
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1298
1299
            return $this->parent->getSheetByName($worksheetReference[0])->cellExists(strtoupper($worksheetReference[1]));
1300
        }
1301
1302
        // Named range?
1303 42
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1304 42
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1305
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1306
            if ($namedRange !== null) {
1307
                $pCoordinate = $namedRange->getRange();
1308
                if ($this->getHashCode() != $namedRange->getWorksheet()->getHashCode()) {
1309
                    if (!$namedRange->getLocalOnly()) {
1310
                        return $namedRange->getWorksheet()->cellExists($pCoordinate);
1311
                    }
1312
                    throw new Exception('Named range ' . $namedRange->getName() . ' is not accessible from within sheet ' . $this->getTitle());
1313
                }
1314
            } else {
1315
                return false;
1316
            }
1317
        }
1318
1319
        // Uppercase coordinate
1320 42
        $pCoordinate = strtoupper($pCoordinate);
1321
1322 42
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
1323
            throw new Exception('Cell coordinate can not be a range of cells.');
1324 42
        } elseif (strpos($pCoordinate, '$') !== false) {
1325
            throw new Exception('Cell coordinate must not be absolute.');
1326
        }
1327
            // Coordinates
1328 42
            $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...
1329
1330
            // Cell exists?
1331 42
            return $this->cellCollection->has($pCoordinate);
1332
    }
1333
1334
    /**
1335
     * Cell at a specific coordinate by using numeric cell coordinates exists?
1336
     *
1337
     * @param string $pColumn Numeric column coordinate of the cell (A = 0)
1338
     * @param string $pRow Numeric row coordinate of the cell
1339
     *
1340
     * @return bool
1341
     */
1342 6
    public function cellExistsByColumnAndRow($pColumn, $pRow)
1343
    {
1344 6
        return $this->cellExists(Cell::stringFromColumnIndex($pColumn) . $pRow);
1345
    }
1346
1347
    /**
1348
     * Get row dimension at a specific row.
1349
     *
1350
     * @param int $pRow Numeric index of the row
1351
     * @param mixed $create
1352
     *
1353
     * @return Worksheet\RowDimension
1354
     */
1355 77
    public function getRowDimension($pRow, $create = true)
1356
    {
1357
        // Found
1358 77
        $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...
1359
1360
        // Get row dimension
1361 77
        if (!isset($this->rowDimensions[$pRow])) {
1362 77
            if (!$create) {
1363 76
                return null;
1364
            }
1365 58
            $this->rowDimensions[$pRow] = new Worksheet\RowDimension($pRow);
1366
1367 58
            $this->cachedHighestRow = max($this->cachedHighestRow, $pRow);
1368
        }
1369
1370 58
        return $this->rowDimensions[$pRow];
1371
    }
1372
1373
    /**
1374
     * Get column dimension at a specific column.
1375
     *
1376
     * @param string $pColumn String index of the column eg: 'A'
1377
     * @param mixed $create
1378
     *
1379
     * @return Worksheet\ColumnDimension
1380
     */
1381 77
    public function getColumnDimension($pColumn, $create = true)
1382
    {
1383
        // Uppercase coordinate
1384 77
        $pColumn = strtoupper($pColumn);
1385
1386
        // Fetch dimensions
1387 77
        if (!isset($this->columnDimensions[$pColumn])) {
1388 77
            if (!$create) {
1389 76
                return null;
1390
            }
1391 29
            $this->columnDimensions[$pColumn] = new Worksheet\ColumnDimension($pColumn);
1392
1393 29
            if (Cell::columnIndexFromString($this->cachedHighestColumn) < Cell::columnIndexFromString($pColumn)) {
1394 12
                $this->cachedHighestColumn = $pColumn;
1395
            }
1396
        }
1397
1398 29
        return $this->columnDimensions[$pColumn];
1399
    }
1400
1401
    /**
1402
     * Get column dimension at a specific column by using numeric cell coordinates.
1403
     *
1404
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
1405
     *
1406
     * @return Worksheet\ColumnDimension
1407
     */
1408 3
    public function getColumnDimensionByColumn($pColumn)
1409
    {
1410 3
        return $this->getColumnDimension(Cell::stringFromColumnIndex($pColumn));
1411
    }
1412
1413
    /**
1414
     * Get styles.
1415
     *
1416
     * @return Style[]
1417
     */
1418
    public function getStyles()
1419
    {
1420
        return $this->styles;
1421
    }
1422
1423
    /**
1424
     * Get style for cell.
1425
     *
1426
     * @param string $pCellCoordinate Cell coordinate (or range) to get style for, eg: 'A1'
1427
     *
1428
     * @throws Exception
1429
     *
1430
     * @return Style
1431
     */
1432 39
    public function getStyle($pCellCoordinate)
1433
    {
1434
        // set this sheet as active
1435 39
        $this->parent->setActiveSheetIndex($this->parent->getIndex($this));
1436
1437
        // set cell coordinate as active
1438 39
        $this->setSelectedCells(strtoupper($pCellCoordinate));
1439
1440 39
        return $this->parent->getCellXfSupervisor();
1441
    }
1442
1443
    /**
1444
     * Get conditional styles for a cell.
1445
     *
1446
     * @param string $pCoordinate eg: 'A1'
1447
     *
1448
     * @return Style\Conditional[]
1449
     */
1450 2
    public function getConditionalStyles($pCoordinate)
1451
    {
1452 2
        $pCoordinate = strtoupper($pCoordinate);
1453 2
        if (!isset($this->conditionalStylesCollection[$pCoordinate])) {
1454 2
            $this->conditionalStylesCollection[$pCoordinate] = [];
1455
        }
1456
1457 2
        return $this->conditionalStylesCollection[$pCoordinate];
1458
    }
1459
1460
    /**
1461
     * Do conditional styles exist for this cell?
1462
     *
1463
     * @param string $pCoordinate eg: 'A1'
1464
     *
1465
     * @return bool
1466
     */
1467 13
    public function conditionalStylesExists($pCoordinate)
1468
    {
1469 13
        if (isset($this->conditionalStylesCollection[strtoupper($pCoordinate)])) {
1470
            return true;
1471
        }
1472
1473 13
        return false;
1474
    }
1475
1476
    /**
1477
     * Removes conditional styles for a cell.
1478
     *
1479
     * @param string $pCoordinate eg: 'A1'
1480
     *
1481
     * @return Worksheet
1482
     */
1483 14
    public function removeConditionalStyles($pCoordinate)
1484
    {
1485 14
        unset($this->conditionalStylesCollection[strtoupper($pCoordinate)]);
1486
1487 14
        return $this;
1488
    }
1489
1490
    /**
1491
     * Get collection of conditional styles.
1492
     *
1493
     * @return array
1494
     */
1495 58
    public function getConditionalStylesCollection()
1496
    {
1497 58
        return $this->conditionalStylesCollection;
1498
    }
1499
1500
    /**
1501
     * Set conditional styles.
1502
     *
1503
     * @param string $pCoordinate eg: 'A1'
1504
     * @param $pValue Style\Conditional[]
1505
     *
1506
     * @return Worksheet
1507
     */
1508 2
    public function setConditionalStyles($pCoordinate, $pValue)
1509
    {
1510 2
        $this->conditionalStylesCollection[strtoupper($pCoordinate)] = $pValue;
1511
1512 2
        return $this;
1513
    }
1514
1515
    /**
1516
     * Get style for cell by using numeric cell coordinates.
1517
     *
1518
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
1519
     * @param int $pRow Numeric row coordinate of the cell
1520
     * @param int pColumn2 Numeric column coordinate of the range cell (A = 0)
1521
     * @param int pRow2 Numeric row coordinate of the range cell
1522
     * @param null|mixed $pColumn2
1523
     * @param null|mixed $pRow2
1524
     *
1525
     * @return Style
1526
     */
1527
    public function getStyleByColumnAndRow($pColumn, $pRow, $pColumn2 = null, $pRow2 = null)
1528
    {
1529
        if (!is_null($pColumn2) && !is_null($pRow2)) {
1530
            $cellRange = Cell::stringFromColumnIndex($pColumn) . $pRow . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1531
1532
            return $this->getStyle($cellRange);
1533
        }
1534
1535
        return $this->getStyle(Cell::stringFromColumnIndex($pColumn) . $pRow);
1536
    }
1537
1538
    /**
1539
     * Duplicate cell style to a range of cells.
1540
     *
1541
     * Please note that this will overwrite existing cell styles for cells in range!
1542
     *
1543
     * @param Style $pCellStyle Cell style to duplicate
1544
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
1545
     *
1546
     * @throws Exception
1547
     *
1548
     * @return Worksheet
1549
     */
1550 2
    public function duplicateStyle(Style $pCellStyle, $pRange)
1551
    {
1552
        // make sure we have a real style and not supervisor
1553 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...
1554
1555
        // Add the style to the workbook if necessary
1556 2
        $workbook = $this->parent;
1557 2
        if ($existingStyle = $this->parent->getCellXfByHashCode($pCellStyle->getHashCode())) {
1558
            // there is already such cell Xf in our collection
1559 1
            $xfIndex = $existingStyle->getIndex();
1560
        } else {
1561
            // we don't have such a cell Xf, need to add
1562 2
            $workbook->addCellXf($pCellStyle);
1563 2
            $xfIndex = $pCellStyle->getIndex();
1564
        }
1565
1566
        // Calculate range outer borders
1567 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange . ':' . $pRange);
1568
1569
        // Make sure we can loop upwards on rows and columns
1570 2 View Code Duplication
        if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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