Completed
Push — develop ( fb12e8...1853aa )
by Adrien
21:21 queued 06:32
created

Worksheet::setSelectedCellByColumnAndRow()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 4
ccs 0
cts 2
cp 0
crap 2
rs 10
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
     * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
0 ignored issues
show
Bug introduced by
There is no parameter named $returnCell. Was it maybe removed?

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

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

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

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

Loading history...
1167
     *
1168
     * @return Worksheet
1169
     */
1170
    public function setCellValueExplicitByColumnAndRow($pColumn, $pRow, $pValue, $pDataType)
1171
    {
1172
        $this->getCellByColumnAndRow($pColumn, $pRow)->setValueExplicit($pValue, $pDataType);
1173
1174
        return $this;
1175
    }
1176
1177
    /**
1178
     * Get cell at a specific coordinate.
1179
     *
1180
     * @param string $pCoordinate Coordinate of the cell, eg: 'A1'
1181
     * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
1182
     *                                       already exist, or a null should be returned instead
1183
     *
1184
     * @throws Exception
1185
     *
1186
     * @return null|Cell Cell that was found/created or null
1187
     */
1188 76
    public function getCell($pCoordinate, $createIfNotExists = true)
1189
    {
1190
        // Check cell collection
1191 76
        if ($this->cellCollection->has(strtoupper($pCoordinate))) {
1192 73
            return $this->cellCollection->get($pCoordinate);
1193
        }
1194
1195
        // Worksheet reference?
1196 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...
1197 1
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1198
1199 1
            return $this->parent->getSheetByName($worksheetReference[0])->getCell(strtoupper($worksheetReference[1]), $createIfNotExists);
1200
        }
1201
1202
        // Named range?
1203 75
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1204 75
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1205
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1206
            if ($namedRange !== null) {
1207
                $pCoordinate = $namedRange->getRange();
1208
1209
                return $namedRange->getWorksheet()->getCell($pCoordinate, $createIfNotExists);
1210
            }
1211
        }
1212
1213
        // Uppercase coordinate
1214 75
        $pCoordinate = strtoupper($pCoordinate);
1215
1216 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...
1217
            throw new Exception('Cell coordinate can not be a range of cells.');
1218 75
        } elseif (strpos($pCoordinate, '$') !== false) {
1219
            throw new Exception('Cell coordinate must not be absolute.');
1220
        }
1221
1222
        // Create new cell object, if required
1223 75
        return $createIfNotExists ? $this->createNewCell($pCoordinate) : null;
1224
    }
1225
1226
    /**
1227
     * Get cell at a specific coordinate by using numeric cell coordinates.
1228
     *
1229
     * @param string $pColumn Numeric column coordinate of the cell
1230
     * @param string $pRow Numeric row coordinate of the cell
1231
     * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
1232
     *                                       already exist, or a null should be returned instead
1233
     *
1234
     * @return null|Cell Cell that was found/created or null
1235
     */
1236 40
    public function getCellByColumnAndRow($pColumn, $pRow, $createIfNotExists = true)
1237
    {
1238 40
        $columnLetter = Cell::stringFromColumnIndex($pColumn);
1239 40
        $coordinate = $columnLetter . $pRow;
1240
1241 40
        if ($this->cellCollection->has($coordinate)) {
1242 39
            return $this->cellCollection->get($coordinate);
1243
        }
1244
1245
        // Create new cell object, if required
1246 23
        return $createIfNotExists ? $this->createNewCell($coordinate) : null;
1247
    }
1248
1249
    /**
1250
     * Create a new cell at the specified coordinate.
1251
     *
1252
     * @param string $pCoordinate Coordinate of the cell
1253
     *
1254
     * @return Cell Cell that was created
1255
     */
1256 77
    private function createNewCell($pCoordinate)
1257
    {
1258 77
        $cell = new Cell(null, Cell\DataType::TYPE_NULL, $this);
1259 77
        $this->cellCollection->add($pCoordinate, $cell);
1260 77
        $this->cellCollectionIsSorted = false;
1261
1262
        // Coordinates
1263 77
        $aCoordinates = Cell::coordinateFromString($pCoordinate);
1264 77
        if (Cell::columnIndexFromString($this->cachedHighestColumn) < Cell::columnIndexFromString($aCoordinates[0])) {
1265 66
            $this->cachedHighestColumn = $aCoordinates[0];
1266
        }
1267 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...
1268
1269
        // Cell needs appropriate xfIndex from dimensions records
1270
        //    but don't create dimension records if they don't already exist
1271 77
        $rowDimension = $this->getRowDimension($aCoordinates[1], false);
1272 77
        $columnDimension = $this->getColumnDimension($aCoordinates[0], false);
1273
1274 77
        if ($rowDimension !== null && $rowDimension->getXfIndex() > 0) {
1275
            // then there is a row dimension with explicit style, assign it to the cell
1276
            $cell->setXfIndex($rowDimension->getXfIndex());
1277 77
        } elseif ($columnDimension !== null && $columnDimension->getXfIndex() > 0) {
1278
            // then there is a column dimension, assign it to the cell
1279
            $cell->setXfIndex($columnDimension->getXfIndex());
1280
        }
1281
1282 77
        return $cell;
1283
    }
1284
1285
    /**
1286
     * Does the cell at a specific coordinate exist?
1287
     *
1288
     * @param string $pCoordinate Coordinate of the cell eg: 'A1'
1289
     *
1290
     * @throws Exception
1291
     *
1292
     * @return bool
1293
     */
1294 42
    public function cellExists($pCoordinate)
1295
    {
1296
        // Worksheet reference?
1297 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...
1298
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1299
1300
            return $this->parent->getSheetByName($worksheetReference[0])->cellExists(strtoupper($worksheetReference[1]));
1301
        }
1302
1303
        // Named range?
1304 42
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1305 42
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1306
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1307
            if ($namedRange !== null) {
1308
                $pCoordinate = $namedRange->getRange();
1309
                if ($this->getHashCode() != $namedRange->getWorksheet()->getHashCode()) {
1310
                    if (!$namedRange->getLocalOnly()) {
1311
                        return $namedRange->getWorksheet()->cellExists($pCoordinate);
1312
                    }
1313
                    throw new Exception('Named range ' . $namedRange->getName() . ' is not accessible from within sheet ' . $this->getTitle());
1314
                }
1315
            } else {
1316
                return false;
1317
            }
1318
        }
1319
1320
        // Uppercase coordinate
1321 42
        $pCoordinate = strtoupper($pCoordinate);
1322
1323 42
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
1324
            throw new Exception('Cell coordinate can not be a range of cells.');
1325 42
        } elseif (strpos($pCoordinate, '$') !== false) {
1326
            throw new Exception('Cell coordinate must not be absolute.');
1327
        }
1328
            // Coordinates
1329 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...
1330
1331
            // Cell exists?
1332 42
            return $this->cellCollection->has($pCoordinate);
1333
    }
1334
1335
    /**
1336
     * Cell at a specific coordinate by using numeric cell coordinates exists?
1337
     *
1338
     * @param string $pColumn Numeric column coordinate of the cell (A = 0)
1339
     * @param string $pRow Numeric row coordinate of the cell
1340
     *
1341
     * @return bool
1342
     */
1343 6
    public function cellExistsByColumnAndRow($pColumn, $pRow)
1344
    {
1345 6
        return $this->cellExists(Cell::stringFromColumnIndex($pColumn) . $pRow);
1346
    }
1347
1348
    /**
1349
     * Get row dimension at a specific row.
1350
     *
1351
     * @param int $pRow Numeric index of the row
1352
     * @param mixed $create
1353
     *
1354
     * @return Worksheet\RowDimension
1355
     */
1356 77
    public function getRowDimension($pRow, $create = true)
1357
    {
1358
        // Found
1359 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...
1360
1361
        // Get row dimension
1362 77
        if (!isset($this->rowDimensions[$pRow])) {
1363 77
            if (!$create) {
1364 76
                return null;
1365
            }
1366 58
            $this->rowDimensions[$pRow] = new Worksheet\RowDimension($pRow);
1367
1368 58
            $this->cachedHighestRow = max($this->cachedHighestRow, $pRow);
1369
        }
1370
1371 58
        return $this->rowDimensions[$pRow];
1372
    }
1373
1374
    /**
1375
     * Get column dimension at a specific column.
1376
     *
1377
     * @param string $pColumn String index of the column eg: 'A'
1378
     * @param mixed $create
1379
     *
1380
     * @return Worksheet\ColumnDimension
1381
     */
1382 77
    public function getColumnDimension($pColumn, $create = true)
1383
    {
1384
        // Uppercase coordinate
1385 77
        $pColumn = strtoupper($pColumn);
1386
1387
        // Fetch dimensions
1388 77
        if (!isset($this->columnDimensions[$pColumn])) {
1389 77
            if (!$create) {
1390 76
                return null;
1391
            }
1392 29
            $this->columnDimensions[$pColumn] = new Worksheet\ColumnDimension($pColumn);
1393
1394 29
            if (Cell::columnIndexFromString($this->cachedHighestColumn) < Cell::columnIndexFromString($pColumn)) {
1395 12
                $this->cachedHighestColumn = $pColumn;
1396
            }
1397
        }
1398
1399 29
        return $this->columnDimensions[$pColumn];
1400
    }
1401
1402
    /**
1403
     * Get column dimension at a specific column by using numeric cell coordinates.
1404
     *
1405
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
1406
     *
1407
     * @return Worksheet\ColumnDimension
1408
     */
1409 3
    public function getColumnDimensionByColumn($pColumn)
1410
    {
1411 3
        return $this->getColumnDimension(Cell::stringFromColumnIndex($pColumn));
1412
    }
1413
1414
    /**
1415
     * Get styles.
1416
     *
1417
     * @return Style[]
1418
     */
1419
    public function getStyles()
1420
    {
1421
        return $this->styles;
1422
    }
1423
1424
    /**
1425
     * Get style for cell.
1426
     *
1427
     * @param string $pCellCoordinate Cell coordinate (or range) to get style for, eg: 'A1'
1428
     *
1429
     * @throws Exception
1430
     *
1431
     * @return Style
1432
     */
1433 39
    public function getStyle($pCellCoordinate)
1434
    {
1435
        // set this sheet as active
1436 39
        $this->parent->setActiveSheetIndex($this->parent->getIndex($this));
1437
1438
        // set cell coordinate as active
1439 39
        $this->setSelectedCells(strtoupper($pCellCoordinate));
1440
1441 39
        return $this->parent->getCellXfSupervisor();
1442
    }
1443
1444
    /**
1445
     * Get conditional styles for a cell.
1446
     *
1447
     * @param string $pCoordinate eg: 'A1'
1448
     *
1449
     * @return Style\Conditional[]
1450
     */
1451 2
    public function getConditionalStyles($pCoordinate)
1452
    {
1453 2
        $pCoordinate = strtoupper($pCoordinate);
1454 2
        if (!isset($this->conditionalStylesCollection[$pCoordinate])) {
1455 2
            $this->conditionalStylesCollection[$pCoordinate] = [];
1456
        }
1457
1458 2
        return $this->conditionalStylesCollection[$pCoordinate];
1459
    }
1460
1461
    /**
1462
     * Do conditional styles exist for this cell?
1463
     *
1464
     * @param string $pCoordinate eg: 'A1'
1465
     *
1466
     * @return bool
1467
     */
1468 13
    public function conditionalStylesExists($pCoordinate)
1469
    {
1470 13
        if (isset($this->conditionalStylesCollection[strtoupper($pCoordinate)])) {
1471
            return true;
1472
        }
1473
1474 13
        return false;
1475
    }
1476
1477
    /**
1478
     * Removes conditional styles for a cell.
1479
     *
1480
     * @param string $pCoordinate eg: 'A1'
1481
     *
1482
     * @return Worksheet
1483
     */
1484 14
    public function removeConditionalStyles($pCoordinate)
1485
    {
1486 14
        unset($this->conditionalStylesCollection[strtoupper($pCoordinate)]);
1487
1488 14
        return $this;
1489
    }
1490
1491
    /**
1492
     * Get collection of conditional styles.
1493
     *
1494
     * @return array
1495
     */
1496 58
    public function getConditionalStylesCollection()
1497
    {
1498 58
        return $this->conditionalStylesCollection;
1499
    }
1500
1501
    /**
1502
     * Set conditional styles.
1503
     *
1504
     * @param string $pCoordinate eg: 'A1'
1505
     * @param $pValue Style\Conditional[]
1506
     *
1507
     * @return Worksheet
1508
     */
1509 2
    public function setConditionalStyles($pCoordinate, $pValue)
1510
    {
1511 2
        $this->conditionalStylesCollection[strtoupper($pCoordinate)] = $pValue;
1512
1513 2
        return $this;
1514
    }
1515
1516
    /**
1517
     * Get style for cell by using numeric cell coordinates.
1518
     *
1519
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
1520
     * @param int $pRow Numeric row coordinate of the cell
1521
     * @param int pColumn2 Numeric column coordinate of the range cell (A = 0)
1522
     * @param int pRow2 Numeric row coordinate of the range cell
1523
     * @param null|mixed $pColumn2
1524
     * @param null|mixed $pRow2
1525
     *
1526
     * @return Style
1527
     */
1528
    public function getStyleByColumnAndRow($pColumn, $pRow, $pColumn2 = null, $pRow2 = null)
1529
    {
1530
        if (!is_null($pColumn2) && !is_null($pRow2)) {
1531
            $cellRange = Cell::stringFromColumnIndex($pColumn) . $pRow . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1532
1533
            return $this->getStyle($cellRange);
1534
        }
1535
1536
        return $this->getStyle(Cell::stringFromColumnIndex($pColumn) . $pRow);
1537
    }
1538
1539
    /**
1540
     * Duplicate cell style to a range of cells.
1541
     *
1542
     * Please note that this will overwrite existing cell styles for cells in range!
1543
     *
1544
     * @param Style $pCellStyle Cell style to duplicate
1545
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
1546
     *
1547
     * @throws Exception
1548
     *
1549
     * @return Worksheet
1550
     */
1551 2
    public function duplicateStyle(Style $pCellStyle, $pRange)
1552
    {
1553
        // make sure we have a real style and not supervisor
1554 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...
1555
1556
        // Add the style to the workbook if necessary
1557 2
        $workbook = $this->parent;
1558 2
        if ($existingStyle = $this->parent->getCellXfByHashCode($pCellStyle->getHashCode())) {
1559
            // there is already such cell Xf in our collection
1560 1
            $xfIndex = $existingStyle->getIndex();
1561
        } else {
1562
            // we don't have such a cell Xf, need to add
1563 2
            $workbook->addCellXf($pCellStyle);
1564 2
            $xfIndex = $pCellStyle->getIndex();
1565
        }
1566
1567
        // Calculate range outer borders
1568 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange . ':' . $pRange);
1569
1570
        // Make sure we can loop upwards on rows and columns
1571 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...
1572
            $tmp = $rangeStart;
1573
            $rangeStart = $rangeEnd;
1574
            $rangeEnd = $tmp;
1575
        }
1576
1577
        // Loop through cells and apply styles
1578 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...
1579 2
            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
1580 2
                $this->getCell(Cell::stringFromColumnIndex($col - 1) . $row)->setXfIndex($xfIndex);
1581
            }
1582
        }
1583
1584 2
        return $this;
1585
    }
1586
1587
    /**
1588
     * Duplicate conditional style to a range of cells.
1589
     *
1590
     * Please note that this will overwrite existing cell styles for cells in range!
1591
     *
1592
     * @param Style\Conditional[] $pCellStyle Cell style to duplicate
1593
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
1594
     *
1595
     * @throws Exception
1596
     *
1597
     * @return Worksheet
1598
     */
1599 2
    public function duplicateConditionalStyle(array $pCellStyle, $pRange = '')
1600
    {
1601 2
        foreach ($pCellStyle as $cellStyle) {
1602 2
            if (!($cellStyle instanceof Style\Conditional)) {
1603 2
                throw new Exception('Style is not a conditional style');
1604
            }
1605
        }
1606
1607
        // Calculate range outer borders
1608 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange . ':' . $pRange);
1609
1610
        // Make sure we can loop upwards on rows and columns
1611 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...
1612
            $tmp = $rangeStart;
1613
            $rangeStart = $rangeEnd;
1614
            $rangeEnd = $tmp;
1615
        }
1616
1617
        // Loop through cells and apply styles
1618 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...
1619 2
            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
1620 2
                $this->setConditionalStyles(Cell::stringFromColumnIndex($col - 1) . $row, $pCellStyle);
1621
            }
1622
        }
1623
1624 2
        return $this;
1625
    }
1626
1627
    /**
1628
     * Set break on a cell.
1629
     *
1630
     * @param string $pCoordinate Cell coordinate (e.g. A1)
1631
     * @param int $pBreak Break type (type of Worksheet::BREAK_*)
1632
     *
1633
     * @throws Exception
1634
     *
1635
     * @return Worksheet
1636
     */
1637 1
    public function setBreak($pCoordinate, $pBreak)
1638
    {
1639
        // Uppercase coordinate
1640 1
        $pCoordinate = strtoupper($pCoordinate);
1641
1642 1
        if ($pCoordinate != '') {
1643 1
            if ($pBreak == self::BREAK_NONE) {
1644
                if (isset($this->breaks[$pCoordinate])) {
1645
                    unset($this->breaks[$pCoordinate]);
1646
                }
1647
            } else {
1648 1
                $this->breaks[$pCoordinate] = $pBreak;
1649
            }
1650
        } else {
1651
            throw new Exception('No cell coordinate specified.');
1652
        }
1653
1654 1
        return $this;
1655
    }
1656
1657
    /**
1658
     * Set break on a cell by using numeric cell coordinates.
1659
     *
1660
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
1661
     * @param int $pRow Numeric row coordinate of the cell
1662
     * @param int $pBreak Break type (type of Worksheet::BREAK_*)
1663
     *
1664
     * @return Worksheet
1665
     */
1666
    public function setBreakByColumnAndRow($pColumn, $pRow, $pBreak)
1667
    {
1668
        return $this->setBreak(Cell::stringFromColumnIndex($pColumn) . $pRow, $pBreak);
1669
    }
1670
1671
    /**
1672
     * Get breaks.
1673
     *
1674
     * @return array[]
1675
     */
1676 62
    public function getBreaks()
1677
    {
1678 62
        return $this->breaks;
1679
    }
1680
1681
    /**
1682
     * Set merge on a cell range.
1683
     *
1684
     * @param string $pRange Cell range (e.g. A1:E1)
1685
     *
1686
     * @throws Exception
1687
     *
1688
     * @return Worksheet
1689
     */
1690 17
    public function mergeCells($pRange)
1691
    {
1692
        // Uppercase coordinate
1693 17
        $pRange = strtoupper($pRange);
1694
1695 17
        if (strpos($pRange, ':') !== false) {
1696 17
            $this->mergeCells[$pRange] = $pRange;
1697
1698
            // make sure cells are created
1699
1700
            // get the cells in the range
1701 17
            $aReferences = Cell::extractAllCellReferencesInRange($pRange);
1702
1703
            // create upper left cell if it does not already exist
1704 17
            $upperLeft = $aReferences[0];
1705 17
            if (!$this->cellExists($upperLeft)) {
1706 13
                $this->getCell($upperLeft)->setValueExplicit(null, Cell\DataType::TYPE_NULL);
1707
            }
1708
1709
            // Blank out the rest of the cells in the range (if they exist)
1710 17
            $count = count($aReferences);
1711 17
            for ($i = 1; $i < $count; ++$i) {
1712 17
                if ($this->cellExists($aReferences[$i])) {
1713 4
                    $this->getCell($aReferences[$i])->setValueExplicit(null, Cell\DataType::TYPE_NULL);
1714
                }
1715
            }
1716
        } else {
1717
            throw new Exception('Merge must be set on a range of cells.');
1718
        }
1719
1720 17
        return $this;
1721
    }
1722
1723
    /**
1724
     * Set merge on a cell range by using numeric cell coordinates.
1725
     *
1726
     * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0)
1727
     * @param int $pRow1 Numeric row coordinate of the first cell
1728
     * @param int $pColumn2 Numeric column coordinate of the last cell (A = 0)
1729
     * @param int $pRow2 Numeric row coordinate of the last cell
1730
     *
1731
     * @throws Exception
1732
     *
1733
     * @return Worksheet
1734
     */
1735
    public function mergeCellsByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2)
1736
    {
1737
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1738
1739
        return $this->mergeCells($cellRange);
1740
    }
1741
1742
    /**
1743
     * Remove merge on a cell range.
1744
     *
1745
     * @param string $pRange Cell range (e.g. A1:E1)
1746
     *
1747
     * @throws Exception
1748
     *
1749
     * @return Worksheet
1750
     */
1751 12
    public function unmergeCells($pRange)
1752
    {
1753
        // Uppercase coordinate
1754 12
        $pRange = strtoupper($pRange);
1755
1756 12
        if (strpos($pRange, ':') !== false) {
1757 12
            if (isset($this->mergeCells[$pRange])) {
1758 12
                unset($this->mergeCells[$pRange]);
1759
            } else {
1760 12
                throw new Exception('Cell range ' . $pRange . ' not known as merged.');
1761
            }
1762
        } else {
1763
            throw new Exception('Merge can only be removed from a range of cells.');
1764
        }
1765
1766 12
        return $this;
1767
    }
1768
1769
    /**
1770
     * Remove merge on a cell range by using numeric cell coordinates.
1771
     *
1772
     * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0)
1773
     * @param int $pRow1 Numeric row coordinate of the first cell
1774
     * @param int $pColumn2 Numeric column coordinate of the last cell (A = 0)
1775
     * @param int $pRow2 Numeric row coordinate of the last cell
1776
     *
1777
     * @throws Exception
1778
     *
1779
     * @return Worksheet
1780
     */
1781
    public function unmergeCellsByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2)
1782
    {
1783
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1784
1785
        return $this->unmergeCells($cellRange);
1786
    }
1787
1788
    /**
1789
     * Get merge cells array.
1790
     *
1791
     * @return array[]
1792
     */
1793 62
    public function getMergeCells()
1794
    {
1795 62
        return $this->mergeCells;
1796
    }
1797
1798
    /**
1799
     * Set merge cells array for the entire sheet. Use instead mergeCells() to merge
1800
     * a single cell range.
1801
     *
1802
     * @param array
1803
     * @param mixed $pValue
1804
     */
1805 15
    public function setMergeCells(array $pValue)
1806
    {
1807 15
        $this->mergeCells = $pValue;
1808
1809 15
        return $this;
1810
    }
1811
1812
    /**
1813
     * Set protection on a cell range.
1814
     *
1815
     * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
1816
     * @param string $pPassword Password to unlock the protection
1817
     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
1818
     *
1819
     * @throws Exception
1820
     *
1821
     * @return Worksheet
1822
     */
1823 12
    public function protectCells($pRange, $pPassword, $pAlreadyHashed = false)
1824
    {
1825
        // Uppercase coordinate
1826 12
        $pRange = strtoupper($pRange);
1827
1828 12
        if (!$pAlreadyHashed) {
1829 12
            $pPassword = Shared\PasswordHasher::hashPassword($pPassword);
1830
        }
1831 12
        $this->protectedCells[$pRange] = $pPassword;
1832
1833 12
        return $this;
1834
    }
1835
1836
    /**
1837
     * Set protection on a cell range by using numeric cell coordinates.
1838
     *
1839
     * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0)
1840
     * @param int $pRow1 Numeric row coordinate of the first cell
1841
     * @param int $pColumn2 Numeric column coordinate of the last cell (A = 0)
1842
     * @param int $pRow2 Numeric row coordinate of the last cell
1843
     * @param string $pPassword Password to unlock the protection
1844
     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
1845
     *
1846
     * @throws Exception
1847
     *
1848
     * @return Worksheet
1849
     */
1850
    public function protectCellsByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2, $pPassword, $pAlreadyHashed = false)
1851
    {
1852
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1853
1854
        return $this->protectCells($cellRange, $pPassword, $pAlreadyHashed);
1855
    }
1856
1857
    /**
1858
     * Remove protection on a cell range.
1859
     *
1860
     * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
1861
     *
1862
     * @throws Exception
1863
     *
1864
     * @return Worksheet
1865
     */
1866 12
    public function unprotectCells($pRange)
1867
    {
1868
        // Uppercase coordinate
1869 12
        $pRange = strtoupper($pRange);
1870
1871 12
        if (isset($this->protectedCells[$pRange])) {
1872 12
            unset($this->protectedCells[$pRange]);
1873
        } else {
1874
            throw new Exception('Cell range ' . $pRange . ' not known as protected.');
1875
        }
1876
1877 12
        return $this;
1878
    }
1879
1880
    /**
1881
     * Remove protection on a cell range by using numeric cell coordinates.
1882
     *
1883
     * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0)
1884
     * @param int $pRow1 Numeric row coordinate of the first cell
1885
     * @param int $pColumn2 Numeric column coordinate of the last cell (A = 0)
1886
     * @param int $pRow2 Numeric row coordinate of the last cell
1887
     * @param string $pPassword Password to unlock the protection
1888
     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
1889
     *
1890
     * @throws Exception
1891
     *
1892
     * @return Worksheet
1893
     */
1894
    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...
1895
    {
1896
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1897
1898
        return $this->unprotectCells($cellRange);
1899
    }
1900
1901
    /**
1902
     * Get protected cells.
1903
     *
1904
     * @return array[]
1905
     */
1906 62
    public function getProtectedCells()
1907
    {
1908 62
        return $this->protectedCells;
1909
    }
1910
1911
    /**
1912
     * Get Autofilter.
1913
     *
1914
     * @return Worksheet\AutoFilter
1915
     */
1916 63
    public function getAutoFilter()
1917
    {
1918 63
        return $this->autoFilter;
1919
    }
1920
1921
    /**
1922
     * Set AutoFilter.
1923
     *
1924
     * @param Worksheet\AutoFilter|string $pValue
1925
     *            A simple string containing a Cell range like 'A1:E10' is permitted for backward compatibility
1926
     *
1927
     * @throws Exception
1928
     *
1929
     * @return Worksheet
1930
     */
1931 4
    public function setAutoFilter($pValue)
1932
    {
1933 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...
1934 4
        if (is_string($pValue)) {
1935 4
            $this->autoFilter->setRange($pValue);
1936
        } elseif (is_object($pValue) && ($pValue instanceof Worksheet\AutoFilter)) {
1937
            $this->autoFilter = $pValue;
1938
        }
1939
1940 4
        return $this;
1941
    }
1942
1943
    /**
1944
     * Set Autofilter Range by using numeric cell coordinates.
1945
     *
1946
     * @param int $pColumn1 Numeric column coordinate of the first cell (A = 0)
1947
     * @param int $pRow1 Numeric row coordinate of the first cell
1948
     * @param int $pColumn2 Numeric column coordinate of the second cell (A = 0)
1949
     * @param int $pRow2 Numeric row coordinate of the second cell
1950
     *
1951
     * @throws Exception
1952
     *
1953
     * @return Worksheet
1954
     */
1955
    public function setAutoFilterByColumnAndRow($pColumn1, $pRow1, $pColumn2, $pRow2)
1956
    {
1957
        return $this->setAutoFilter(
1958
            Cell::stringFromColumnIndex($pColumn1) . $pRow1
1959
            . ':' .
1960
            Cell::stringFromColumnIndex($pColumn2) . $pRow2
1961
        );
1962
    }
1963
1964
    /**
1965
     * Remove autofilter.
1966
     *
1967
     * @return Worksheet
1968
     */
1969
    public function removeAutoFilter()
1970
    {
1971
        $this->autoFilter->setRange(null);
1972
1973
        return $this;
1974
    }
1975
1976
    /**
1977
     * Get Freeze Pane.
1978
     *
1979
     * @return string
1980
     */
1981 62
    public function getFreezePane()
1982
    {
1983 62
        return $this->freezePane;
1984
    }
1985
1986
    /**
1987
     * Freeze Pane.
1988
     *
1989
     * @param string $pCell Cell (i.e. A2)
1990
     *                                    Examples:
1991
     *                                        A2 will freeze the rows above cell A2 (i.e row 1)
1992
     *                                        B1 will freeze the columns to the left of cell B1 (i.e column A)
1993
     *                                        B2 will freeze the rows above and to the left of cell A2
1994
     *                                            (i.e row 1 and column A)
1995
     *
1996
     * @throws Exception
1997
     *
1998
     * @return Worksheet
1999
     */
2000 4
    public function freezePane($pCell)
2001
    {
2002
        // Uppercase coordinate
2003 4
        $pCell = strtoupper($pCell);
2004 4
        if (strpos($pCell, ':') === false && strpos($pCell, ',') === false) {
2005 4
            $this->freezePane = $pCell;
2006
        } else {
2007
            throw new Exception('Freeze pane can not be set on a range of cells.');
2008
        }
2009
2010 4
        return $this;
2011
    }
2012
2013
    /**
2014
     * Freeze Pane by using numeric cell coordinates.
2015
     *
2016
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
2017
     * @param int $pRow Numeric row coordinate of the cell
2018
     *
2019
     * @throws Exception
2020
     *
2021
     * @return Worksheet
2022
     */
2023
    public function freezePaneByColumnAndRow($pColumn, $pRow)
2024
    {
2025
        return $this->freezePane(Cell::stringFromColumnIndex($pColumn) . $pRow);
2026
    }
2027
2028
    /**
2029
     * Unfreeze Pane.
2030
     *
2031
     * @return Worksheet
2032
     */
2033
    public function unfreezePane()
2034
    {
2035
        return $this->freezePane('');
2036
    }
2037
2038
    /**
2039
     * Insert a new row, updating all possible related data.
2040
     *
2041
     * @param int $pBefore Insert before this one
2042
     * @param int $pNumRows Number of rows to insert
2043
     *
2044
     * @throws Exception
2045
     *
2046
     * @return Worksheet
2047
     */
2048 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...
2049
    {
2050 13
        if ($pBefore >= 1) {
2051 13
            $objReferenceHelper = ReferenceHelper::getInstance();
2052 13
            $objReferenceHelper->insertNewBefore('A' . $pBefore, 0, $pNumRows, $this);
2053
        } else {
2054
            throw new Exception('Rows can only be inserted before at least row 1.');
2055
        }
2056
2057 13
        return $this;
2058
    }
2059
2060
    /**
2061
     * Insert a new column, updating all possible related data.
2062
     *
2063
     * @param int $pBefore Insert before this one, eg: 'A'
2064
     * @param int $pNumCols Number of columns to insert
2065
     *
2066
     * @throws Exception
2067
     *
2068
     * @return Worksheet
2069
     */
2070 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...
2071
    {
2072 12
        if (!is_numeric($pBefore)) {
2073 12
            $objReferenceHelper = ReferenceHelper::getInstance();
2074 12
            $objReferenceHelper->insertNewBefore($pBefore . '1', $pNumCols, 0, $this);
2075
        } else {
2076
            throw new Exception('Column references should not be numeric.');
2077
        }
2078
2079 12
        return $this;
2080
    }
2081
2082
    /**
2083
     * Insert a new column, updating all possible related data.
2084
     *
2085
     * @param int $pBefore Insert before this one (numeric column coordinate of the cell, A = 0)
2086
     * @param int $pNumCols Number of columns to insert
2087
     *
2088
     * @throws Exception
2089
     *
2090
     * @return Worksheet
2091
     */
2092
    public function insertNewColumnBeforeByIndex($pBefore, $pNumCols = 1)
2093
    {
2094
        if ($pBefore >= 0) {
2095
            return $this->insertNewColumnBefore(Cell::stringFromColumnIndex($pBefore), $pNumCols);
2096
        }
2097
        throw new Exception('Columns can only be inserted before at least column A (0).');
2098
    }
2099
2100
    /**
2101
     * Delete a row, updating all possible related data.
2102
     *
2103
     * @param int $pRow Remove starting with this one
2104
     * @param int $pNumRows Number of rows to remove
2105
     *
2106
     * @throws Exception
2107
     *
2108
     * @return Worksheet
2109
     */
2110 15
    public function removeRow($pRow, $pNumRows = 1)
2111
    {
2112 15
        if ($pRow >= 1) {
2113 15
            $highestRow = $this->getHighestDataRow();
2114 15
            $objReferenceHelper = ReferenceHelper::getInstance();
2115 15
            $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this);
2116 15
            for ($r = 0; $r < $pNumRows; ++$r) {
2117 15
                $this->getCellCollection()->removeRow($highestRow);
2118 15
                --$highestRow;
2119
            }
2120
        } else {
2121
            throw new Exception('Rows to be deleted should at least start from row 1.');
2122
        }
2123
2124 15
        return $this;
2125
    }
2126
2127
    /**
2128
     * Remove a column, updating all possible related data.
2129
     *
2130
     * @param string $pColumn Remove starting with this one, eg: 'A'
2131
     * @param int $pNumCols Number of columns to remove
2132
     *
2133
     * @throws Exception
2134
     *
2135
     * @return Worksheet
2136
     */
2137 12
    public function removeColumn($pColumn, $pNumCols = 1)
2138
    {
2139 12
        if (!is_numeric($pColumn)) {
2140 12
            $highestColumn = $this->getHighestDataColumn();
2141 12
            $pColumn = Cell::stringFromColumnIndex(Cell::columnIndexFromString($pColumn) - 1 + $pNumCols);
2142 12
            $objReferenceHelper = ReferenceHelper::getInstance();
2143 12
            $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this);
2144 12
            for ($c = 0; $c < $pNumCols; ++$c) {
2145 12
                $this->getCellCollection()->removeColumn($highestColumn);
2146 12
                $highestColumn = Cell::stringFromColumnIndex(Cell::columnIndexFromString($highestColumn) - 2);
2147
            }
2148
        } else {
2149
            throw new Exception('Column references should not be numeric.');
2150
        }
2151
2152 12
        return $this;
2153
    }
2154
2155
    /**
2156
     * Remove a column, updating all possible related data.
2157
     *
2158
     * @param int $pColumn Remove starting with this one (numeric column coordinate of the cell A = 0)
2159
     * @param int $pNumCols Number of columns to remove
2160
     *
2161
     * @throws Exception
2162
     *
2163
     * @return Worksheet
2164
     */
2165
    public function removeColumnByIndex($pColumn, $pNumCols = 1)
2166
    {
2167
        if ($pColumn >= 0) {
2168
            return $this->removeColumn(Cell::stringFromColumnIndex($pColumn), $pNumCols);
2169
        }
2170
        throw new Exception('Columns to be deleted should at least start from column 0');
2171
    }
2172
2173
    /**
2174
     * Show gridlines?
2175
     *
2176
     * @return bool
2177
     */
2178 62
    public function getShowGridlines()
2179
    {
2180 62
        return $this->showGridlines;
2181
    }
2182
2183
    /**
2184
     * Set show gridlines.
2185
     *
2186
     * @param bool $pValue Show gridlines (true/false)
2187
     *
2188
     * @return Worksheet
2189
     */
2190 12
    public function setShowGridlines($pValue)
2191
    {
2192 12
        $this->showGridlines = $pValue;
2193
2194 12
        return $this;
2195
    }
2196
2197
    /**
2198
     * Print gridlines?
2199
     *
2200
     * @return bool
2201
     */
2202 58
    public function getPrintGridlines()
2203
    {
2204 58
        return $this->printGridlines;
2205
    }
2206
2207
    /**
2208
     * Set print gridlines.
2209
     *
2210
     * @param bool $pValue Print gridlines (true/false)
2211
     *
2212
     * @return Worksheet
2213
     */
2214 4
    public function setPrintGridlines($pValue)
2215
    {
2216 4
        $this->printGridlines = $pValue;
2217
2218 4
        return $this;
2219
    }
2220
2221
    /**
2222
     * Show row and column headers?
2223
     *
2224
     * @return bool
2225
     */
2226 58
    public function getShowRowColHeaders()
2227
    {
2228 58
        return $this->showRowColHeaders;
2229
    }
2230
2231
    /**
2232
     * Set show row and column headers.
2233
     *
2234
     * @param bool $pValue Show row and column headers (true/false)
2235
     *
2236
     * @return Worksheet
2237
     */
2238 9
    public function setShowRowColHeaders($pValue)
2239
    {
2240 9
        $this->showRowColHeaders = $pValue;
2241
2242 9
        return $this;
2243
    }
2244
2245
    /**
2246
     * Show summary below? (Row/Column outlining).
2247
     *
2248
     * @return bool
2249
     */
2250 58
    public function getShowSummaryBelow()
2251
    {
2252 58
        return $this->showSummaryBelow;
2253
    }
2254
2255
    /**
2256
     * Set show summary below.
2257
     *
2258
     * @param bool $pValue Show summary below (true/false)
2259
     *
2260
     * @return Worksheet
2261
     */
2262 9
    public function setShowSummaryBelow($pValue)
2263
    {
2264 9
        $this->showSummaryBelow = $pValue;
2265
2266 9
        return $this;
2267
    }
2268
2269
    /**
2270
     * Show summary right? (Row/Column outlining).
2271
     *
2272
     * @return bool
2273
     */
2274 58
    public function getShowSummaryRight()
2275
    {
2276 58
        return $this->showSummaryRight;
2277
    }
2278
2279
    /**
2280
     * Set show summary right.
2281
     *
2282
     * @param bool $pValue Show summary right (true/false)
2283
     *
2284
     * @return Worksheet
2285
     */
2286 9
    public function setShowSummaryRight($pValue)
2287
    {
2288 9
        $this->showSummaryRight = $pValue;
2289
2290 9
        return $this;
2291
    }
2292
2293
    /**
2294
     * Get comments.
2295
     *
2296
     * @return Comment[]
2297
     */
2298 63
    public function getComments()
2299
    {
2300 63
        return $this->comments;
2301
    }
2302
2303
    /**
2304
     * Set comments array for the entire sheet.
2305
     *
2306
     * @param array of Comment
2307
     * @param mixed $pValue
2308
     *
2309
     * @return Worksheet
2310
     */
2311 15
    public function setComments(array $pValue)
2312
    {
2313 15
        $this->comments = $pValue;
2314
2315 15
        return $this;
2316
    }
2317
2318
    /**
2319
     * Get comment for cell.
2320
     *
2321
     * @param string $pCellCoordinate Cell coordinate to get comment for, eg: 'A1'
2322
     *
2323
     * @throws Exception
2324
     *
2325
     * @return Comment
2326
     */
2327 20
    public function getComment($pCellCoordinate)
2328
    {
2329
        // Uppercase coordinate
2330 20
        $pCellCoordinate = strtoupper($pCellCoordinate);
2331
2332 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...
2333
            throw new Exception('Cell coordinate string can not be a range of cells.');
2334 20
        } elseif (strpos($pCellCoordinate, '$') !== false) {
2335
            throw new Exception('Cell coordinate string must not be absolute.');
2336 20
        } elseif ($pCellCoordinate == '') {
2337
            throw new Exception('Cell coordinate can not be zero-length string.');
2338
        }
2339
2340
        // Check if we already have a comment for this cell.
2341 20
        if (isset($this->comments[$pCellCoordinate])) {
2342 13
            return $this->comments[$pCellCoordinate];
2343
        }
2344
2345
        // If not, create a new comment.
2346 20
        $newComment = new Comment();
2347 20
        $this->comments[$pCellCoordinate] = $newComment;
2348
2349 20
        return $newComment;
2350
    }
2351
2352
    /**
2353
     * Get comment for cell by using numeric cell coordinates.
2354
     *
2355
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
2356
     * @param int $pRow Numeric row coordinate of the cell
2357
     *
2358
     * @return Comment
2359
     */
2360 2
    public function getCommentByColumnAndRow($pColumn, $pRow)
2361
    {
2362 2
        return $this->getComment(Cell::stringFromColumnIndex($pColumn) . $pRow);
2363
    }
2364
2365
    /**
2366
     * Get active cell.
2367
     *
2368
     * @return string Example: 'A1'
2369
     */
2370 58
    public function getActiveCell()
2371
    {
2372 58
        return $this->activeCell;
2373
    }
2374
2375
    /**
2376
     * Get selected cells.
2377
     *
2378
     * @return string
2379
     */
2380 55
    public function getSelectedCells()
2381
    {
2382 55
        return $this->selectedCells;
2383
    }
2384
2385
    /**
2386
     * Selected cell.
2387
     *
2388
     * @param string $pCoordinate Cell (i.e. A1)
2389
     *
2390
     * @return Worksheet
2391
     */
2392
    public function setSelectedCell($pCoordinate)
2393
    {
2394
        return $this->setSelectedCells($pCoordinate);
2395
    }
2396
2397
    /**
2398
     * Select a range of cells.
2399
     *
2400
     * @param string $pCoordinate Cell range, examples: 'A1', 'B2:G5', 'A:C', '3:6'
2401
     *
2402
     * @throws Exception
2403
     *
2404
     * @return Worksheet
2405
     */
2406 48
    public function setSelectedCells($pCoordinate)
2407
    {
2408
        // Uppercase coordinate
2409 48
        $pCoordinate = strtoupper($pCoordinate);
2410
2411
        // Convert 'A' to 'A:A'
2412 48
        $pCoordinate = preg_replace('/^([A-Z]+)$/', '${1}:${1}', $pCoordinate);
2413
2414
        // Convert '1' to '1:1'
2415 48
        $pCoordinate = preg_replace('/^([0-9]+)$/', '${1}:${1}', $pCoordinate);
2416
2417
        // Convert 'A:C' to 'A1:C1048576'
2418 48
        $pCoordinate = preg_replace('/^([A-Z]+):([A-Z]+)$/', '${1}1:${2}1048576', $pCoordinate);
2419
2420
        // Convert '1:3' to 'A1:XFD3'
2421 48
        $pCoordinate = preg_replace('/^([0-9]+):([0-9]+)$/', 'A${1}:XFD${2}', $pCoordinate);
2422
2423 48
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
2424 26
            list($first) = Cell::splitRange($pCoordinate);
2425 26
            $this->activeCell = $first[0];
2426
        } else {
2427 41
            $this->activeCell = $pCoordinate;
2428
        }
2429 48
        $this->selectedCells = $pCoordinate;
2430
2431 48
        return $this;
2432
    }
2433
2434
    /**
2435
     * Selected cell by using numeric cell coordinates.
2436
     *
2437
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
2438
     * @param int $pRow Numeric row coordinate of the cell
2439
     *
2440
     * @throws Exception
2441
     *
2442
     * @return Worksheet
2443
     */
2444
    public function setSelectedCellByColumnAndRow($pColumn, $pRow)
2445
    {
2446
        return $this->setSelectedCells(Cell::stringFromColumnIndex($pColumn) . $pRow);
2447
    }
2448
2449
    /**
2450
     * Get right-to-left.
2451
     *
2452
     * @return bool
2453
     */
2454 58
    public function getRightToLeft()
2455
    {
2456 58
        return $this->rightToLeft;
2457
    }
2458
2459
    /**
2460
     * Set right-to-left.
2461
     *
2462
     * @param bool $value Right-to-left true/false
2463
     *
2464
     * @return Worksheet
2465
     */
2466 4
    public function setRightToLeft($value)
2467
    {
2468 4
        $this->rightToLeft = $value;
2469
2470 4
        return $this;
2471
    }
2472
2473
    /**
2474
     * Fill worksheet from values in array.
2475
     *
2476
     * @param array $source Source array
2477
     * @param mixed $nullValue Value in source array that stands for blank cell
2478
     * @param string $startCell Insert array starting from this cell address as the top left coordinate
2479
     * @param bool $strictNullComparison Apply strict comparison when testing for null values in the array
2480
     *
2481
     * @throws Exception
2482
     *
2483
     * @return Worksheet
2484
     */
2485 17
    public function fromArray(array $source, $nullValue = null, $startCell = 'A1', $strictNullComparison = false)
2486
    {
2487
        //    Convert a 1-D array to 2-D (for ease of looping)
2488 17
        if (!is_array(end($source))) {
2489 3
            $source = [$source];
2490
        }
2491
2492
        // start coordinate
2493 17
        list($startColumn, $startRow) = Cell::coordinateFromString($startCell);
2494
2495
        // Loop through $source
2496 17
        foreach ($source as $rowData) {
2497 17
            $currentColumn = $startColumn;
2498 17
            foreach ($rowData as $cellValue) {
2499 17
                if ($strictNullComparison) {
2500 1
                    if ($cellValue !== $nullValue) {
2501
                        // Set cell value
2502 1
                        $this->getCell($currentColumn . $startRow)->setValue($cellValue);
2503
                    }
2504
                } else {
2505 16
                    if ($cellValue != $nullValue) {
2506
                        // Set cell value
2507 16
                        $this->getCell($currentColumn . $startRow)->setValue($cellValue);
2508
                    }
2509
                }
2510 17
                ++$currentColumn;
2511
            }
2512 17
            ++$startRow;
2513
        }
2514
2515 17
        return $this;
2516
    }
2517
2518
    /**
2519
     * Create array from a range of cells.
2520
     *
2521
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
2522
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2523
     * @param bool $calculateFormulas Should formulas be calculated?
2524
     * @param bool $formatData Should formatting be applied to cell values?
2525
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2526
     *                               True - Return rows and columns indexed by their actual row and column IDs
2527
     *
2528
     * @return array
2529
     */
2530 3
    public function rangeToArray($pRange, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2531
    {
2532
        // Returnvalue
2533 3
        $returnValue = [];
2534
        //    Identify the range that we need to extract from the worksheet
2535 3
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange);
2536 3
        $minCol = Cell::stringFromColumnIndex($rangeStart[0] - 1);
2537 3
        $minRow = $rangeStart[1];
2538 3
        $maxCol = Cell::stringFromColumnIndex($rangeEnd[0] - 1);
2539 3
        $maxRow = $rangeEnd[1];
2540
2541 3
        ++$maxCol;
2542
        // Loop through rows
2543 3
        $r = -1;
2544 3
        for ($row = $minRow; $row <= $maxRow; ++$row) {
2545 3
            $rRef = ($returnCellRef) ? $row : ++$r;
2546 3
            $c = -1;
2547
            // Loop through columns in the current row
2548 3
            for ($col = $minCol; $col != $maxCol; ++$col) {
2549 3
                $cRef = ($returnCellRef) ? $col : ++$c;
2550
                //    Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
2551
                //        so we test and retrieve directly against cellCollection
2552 3
                if ($this->cellCollection->has($col . $row)) {
2553
                    // Cell exists
2554 3
                    $cell = $this->cellCollection->get($col . $row);
2555 3
                    if ($cell->getValue() !== null) {
2556 3
                        if ($cell->getValue() instanceof RichText) {
2557 2
                            $returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
2558
                        } else {
2559 3
                            if ($calculateFormulas) {
2560 3
                                $returnValue[$rRef][$cRef] = $cell->getCalculatedValue();
2561
                            } else {
2562
                                $returnValue[$rRef][$cRef] = $cell->getValue();
2563
                            }
2564
                        }
2565
2566 3
                        if ($formatData) {
2567 3
                            $style = $this->parent->getCellXfByIndex($cell->getXfIndex());
2568 3
                            $returnValue[$rRef][$cRef] = Style\NumberFormat::toFormattedString(
2569 3
                                $returnValue[$rRef][$cRef],
2570 3
                                ($style && $style->getNumberFormat()) ? $style->getNumberFormat()->getFormatCode() : Style\NumberFormat::FORMAT_GENERAL
2571
                            );
2572
                        }
2573
                    } else {
2574
                        // Cell holds a NULL
2575 3
                        $returnValue[$rRef][$cRef] = $nullValue;
2576
                    }
2577
                } else {
2578
                    // Cell doesn't exist
2579 1
                    $returnValue[$rRef][$cRef] = $nullValue;
2580
                }
2581
            }
2582
        }
2583
2584
        // Return
2585 3
        return $returnValue;
2586
    }
2587
2588
    /**
2589
     * Create array from a range of cells.
2590
     *
2591
     * @param string $pNamedRange Name of the Named Range
2592
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2593
     * @param bool $calculateFormulas Should formulas be calculated?
2594
     * @param bool $formatData Should formatting be applied to cell values?
2595
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2596
     *                                True - Return rows and columns indexed by their actual row and column IDs
2597
     *
2598
     * @throws Exception
2599
     *
2600
     * @return array
2601
     */
2602
    public function namedRangeToArray($pNamedRange, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2603
    {
2604
        $namedRange = NamedRange::resolveRange($pNamedRange, $this);
2605
        if ($namedRange !== null) {
2606
            $pWorkSheet = $namedRange->getWorksheet();
2607
            $pCellRange = $namedRange->getRange();
2608
2609
            return $pWorkSheet->rangeToArray($pCellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
2610
        }
2611
2612
        throw new Exception('Named Range ' . $pNamedRange . ' does not exist.');
2613
    }
2614
2615
    /**
2616
     * Create array from worksheet.
2617
     *
2618
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2619
     * @param bool $calculateFormulas Should formulas be calculated?
2620
     * @param bool $formatData Should formatting be applied to cell values?
2621
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2622
     *                               True - Return rows and columns indexed by their actual row and column IDs
2623
     *
2624
     * @return array
2625
     */
2626
    public function toArray($nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2627
    {
2628
        // Garbage collect...
2629
        $this->garbageCollect();
2630
2631
        //    Identify the range that we need to extract from the worksheet
2632
        $maxCol = $this->getHighestColumn();
2633
        $maxRow = $this->getHighestRow();
2634
        // Return
2635
        return $this->rangeToArray('A1:' . $maxCol . $maxRow, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
2636
    }
2637
2638
    /**
2639
     * Get row iterator.
2640
     *
2641
     * @param int $startRow The row number at which to start iterating
2642
     * @param int $endRow The row number at which to stop iterating
2643
     *
2644
     * @return Worksheet\RowIterator
2645
     */
2646 4
    public function getRowIterator($startRow = 1, $endRow = null)
2647
    {
2648 4
        return new Worksheet\RowIterator($this, $startRow, $endRow);
2649
    }
2650
2651
    /**
2652
     * Get column iterator.
2653
     *
2654
     * @param string $startColumn The column address at which to start iterating
2655
     * @param string $endColumn The column address at which to stop iterating
2656
     *
2657
     * @return Worksheet\ColumnIterator
2658
     */
2659
    public function getColumnIterator($startColumn = 'A', $endColumn = null)
2660
    {
2661
        return new Worksheet\ColumnIterator($this, $startColumn, $endColumn);
2662
    }
2663
2664
    /**
2665
     * Run PhpSpreadsheet garabage collector.
2666
     *
2667
     * @return Worksheet
2668
     */
2669 62
    public function garbageCollect()
2670
    {
2671
        // Flush cache
2672 62
        $this->cellCollection->get('A1');
2673
2674
        // Lookup highest column and highest row if cells are cleaned
2675 62
        $colRow = $this->cellCollection->getHighestRowAndColumn();
2676 62
        $highestRow = $colRow['row'];
2677 62
        $highestColumn = Cell::columnIndexFromString($colRow['column']);
2678
2679
        // Loop through column dimensions
2680 62
        foreach ($this->columnDimensions as $dimension) {
2681 27
            $highestColumn = max($highestColumn, Cell::columnIndexFromString($dimension->getColumnIndex()));
2682
        }
2683
2684
        // Loop through row dimensions
2685 62
        foreach ($this->rowDimensions as $dimension) {
2686 39
            $highestRow = max($highestRow, $dimension->getRowIndex());
2687
        }
2688
2689
        // Cache values
2690 62
        if ($highestColumn < 0) {
2691
            $this->cachedHighestColumn = 'A';
2692
        } else {
2693 62
            $this->cachedHighestColumn = Cell::stringFromColumnIndex(--$highestColumn);
2694
        }
2695 62
        $this->cachedHighestRow = $highestRow;
2696
2697
        // Return
2698 62
        return $this;
2699
    }
2700
2701
    /**
2702
     * Get hash code.
2703
     *
2704
     * @return string Hash code
2705
     */
2706 70
    public function getHashCode()
2707
    {
2708 70
        if ($this->dirty) {
2709 70
            $this->hash = md5($this->title . $this->autoFilter . ($this->protection->isProtectionEnabled() ? 't' : 'f') . __CLASS__);
2710 70
            $this->dirty = false;
2711
        }
2712
2713 70
        return $this->hash;
2714
    }
2715
2716
    /**
2717
     * Extract worksheet title from range.
2718
     *
2719
     * Example: extractSheetTitle("testSheet!A1") ==> 'A1'
2720
     * Example: extractSheetTitle("'testSheet 1'!A1", true) ==> array('testSheet 1', 'A1');
2721
     *
2722
     * @param string $pRange Range to extract title from
2723
     * @param bool $returnRange Return range? (see example)
2724
     *
2725
     * @return mixed
2726
     */
2727 1
    public static function extractSheetTitle($pRange, $returnRange = false)
2728
    {
2729
        // Sheet title included?
2730 1
        if (($sep = strpos($pRange, '!')) === false) {
2731
            return '';
2732
        }
2733
2734 1
        if ($returnRange) {
2735 1
            return [trim(substr($pRange, 0, $sep), "'"), substr($pRange, $sep + 1)];
2736
        }
2737
2738
        return substr($pRange, $sep + 1);
2739
    }
2740
2741
    /**
2742
     * Get hyperlink.
2743
     *
2744
     * @param string $pCellCoordinate Cell coordinate to get hyperlink for, eg: 'A1'
2745
     */
2746 19
    public function getHyperlink($pCellCoordinate)
2747
    {
2748
        // return hyperlink if we already have one
2749 19
        if (isset($this->hyperlinkCollection[$pCellCoordinate])) {
2750 14
            return $this->hyperlinkCollection[$pCellCoordinate];
2751
        }
2752
2753
        // else create hyperlink
2754 19
        $this->hyperlinkCollection[$pCellCoordinate] = new Cell\Hyperlink();
2755
2756 19
        return $this->hyperlinkCollection[$pCellCoordinate];
2757
    }
2758
2759
    /**
2760
     * Set hyperlink.
2761
     *
2762
     * @param string $pCellCoordinate Cell coordinate to insert hyperlink, eg: 'A1'
2763
     * @param Cell\Hyperlink|null $pHyperlink
2764
     *
2765
     * @return Worksheet
2766
     */
2767 13
    public function setHyperlink($pCellCoordinate, Cell\Hyperlink $pHyperlink = null)
2768
    {
2769 13
        if ($pHyperlink === null) {
2770 13
            unset($this->hyperlinkCollection[$pCellCoordinate]);
2771
        } else {
2772 13
            $this->hyperlinkCollection[$pCellCoordinate] = $pHyperlink;
2773
        }
2774
2775 13
        return $this;
2776
    }
2777
2778
    /**
2779
     * Hyperlink at a specific coordinate exists?
2780
     *
2781
     * @param string $pCoordinate eg: 'A1'
2782
     *
2783
     * @return bool
2784
     */
2785 6
    public function hyperlinkExists($pCoordinate)
2786
    {
2787 6
        return isset($this->hyperlinkCollection[$pCoordinate]);
2788
    }
2789
2790
    /**
2791
     * Get collection of hyperlinks.
2792
     *
2793
     * @return Cell\Hyperlink[]
2794
     */
2795 62
    public function getHyperlinkCollection()
2796
    {
2797 62
        return $this->hyperlinkCollection;
2798
    }
2799
2800
    /**
2801
     * Get data validation.
2802
     *
2803
     * @param string $pCellCoordinate Cell coordinate to get data validation for, eg: 'A1'
2804
     */
2805 2
    public function getDataValidation($pCellCoordinate)
2806
    {
2807
        // return data validation if we already have one
2808 2
        if (isset($this->dataValidationCollection[$pCellCoordinate])) {
2809
            return $this->dataValidationCollection[$pCellCoordinate];
2810
        }
2811
2812
        // else create data validation
2813 2
        $this->dataValidationCollection[$pCellCoordinate] = new Cell\DataValidation();
2814
2815 2
        return $this->dataValidationCollection[$pCellCoordinate];
2816
    }
2817
2818
    /**
2819
     * Set data validation.
2820
     *
2821
     * @param string $pCellCoordinate Cell coordinate to insert data validation, eg: 'A1'
2822
     * @param Cell\DataValidation|null $pDataValidation
2823
     *
2824
     * @return Worksheet
2825
     */
2826
    public function setDataValidation($pCellCoordinate, Cell\DataValidation $pDataValidation = null)
2827
    {
2828
        if ($pDataValidation === null) {
2829
            unset($this->dataValidationCollection[$pCellCoordinate]);
2830
        } else {
2831
            $this->dataValidationCollection[$pCellCoordinate] = $pDataValidation;
2832
        }
2833
2834
        return $this;
2835
    }
2836
2837
    /**
2838
     * Data validation at a specific coordinate exists?
2839
     *
2840
     * @param string $pCoordinate eg: 'A1'
2841
     *
2842
     * @return bool
2843
     */
2844
    public function dataValidationExists($pCoordinate)
2845
    {
2846
        return isset($this->dataValidationCollection[$pCoordinate]);
2847
    }
2848
2849
    /**
2850
     * Get collection of data validations.
2851
     *
2852
     * @return Cell\DataValidation[]
2853
     */
2854 62
    public function getDataValidationCollection()
2855
    {
2856 62
        return $this->dataValidationCollection;
2857
    }
2858
2859
    /**
2860
     * Accepts a range, returning it as a range that falls within the current highest row and column of the worksheet.
2861
     *
2862
     * @param string $range
2863
     *
2864
     * @return string Adjusted range value
2865
     */
2866
    public function shrinkRangeToFit($range)
2867
    {
2868
        $maxCol = $this->getHighestColumn();
2869
        $maxRow = $this->getHighestRow();
2870
        $maxCol = Cell::columnIndexFromString($maxCol);
2871
2872
        $rangeBlocks = explode(' ', $range);
2873
        foreach ($rangeBlocks as &$rangeSet) {
2874
            $rangeBoundaries = Cell::getRangeBoundaries($rangeSet);
2875
2876 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...
2877
                $rangeBoundaries[0][0] = Cell::stringFromColumnIndex($maxCol);
2878
            }
2879
            if ($rangeBoundaries[0][1] > $maxRow) {
2880
                $rangeBoundaries[0][1] = $maxRow;
2881
            }
2882 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...
2883
                $rangeBoundaries[1][0] = Cell::stringFromColumnIndex($maxCol);
2884
            }
2885
            if ($rangeBoundaries[1][1] > $maxRow) {
2886
                $rangeBoundaries[1][1] = $maxRow;
2887
            }
2888
            $rangeSet = $rangeBoundaries[0][0] . $rangeBoundaries[0][1] . ':' . $rangeBoundaries[1][0] . $rangeBoundaries[1][1];
2889
        }
2890
        unset($rangeSet);
2891
        $stRange = implode(' ', $rangeBlocks);
2892
2893
        return $stRange;
2894
    }
2895
2896
    /**
2897
     * Get tab color.
2898
     *
2899
     * @return Style\Color
2900
     */
2901 12
    public function getTabColor()
2902
    {
2903 12
        if ($this->tabColor === null) {
2904 12
            $this->tabColor = new Style\Color();
2905
        }
2906
2907 12
        return $this->tabColor;
2908
    }
2909
2910
    /**
2911
     * Reset tab color.
2912
     *
2913
     * @return Worksheet
2914
     */
2915
    public function resetTabColor()
2916
    {
2917
        $this->tabColor = null;
2918
        unset($this->tabColor);
2919
2920
        return $this;
2921
    }
2922
2923
    /**
2924
     * Tab color set?
2925
     *
2926
     * @return bool
2927
     */
2928 58
    public function isTabColorSet()
2929
    {
2930 58
        return $this->tabColor !== null;
2931
    }
2932
2933
    /**
2934
     * Copy worksheet (!= clone!).
2935
     *
2936
     * @return Worksheet
2937
     */
2938
    public function copy()
2939
    {
2940
        $copied = clone $this;
2941
2942
        return $copied;
2943
    }
2944
2945
    /**
2946
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
2947
     */
2948 1
    public function __clone()
2949
    {
2950 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...
2951 1
            if ($key == 'parent') {
2952 1
                continue;
2953
            }
2954
2955 1
            if (is_object($val) || (is_array($val))) {
2956 1
                if ($key == 'cellCollection') {
2957 1
                    $newCollection = $this->cellCollection->cloneCellCollection($this);
2958 1
                    $this->cellCollection = $newCollection;
2959 1
                } elseif ($key == 'drawingCollection') {
2960 1
                    $newCollection = new ArrayObject();
2961 1
                    foreach ($this->drawingCollection as $id => $item) {
2962
                        if (is_object($item)) {
2963
                            $newCollection[$id] = clone $this->drawingCollection[$id];
2964
                        }
2965
                    }
2966 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...
2967 1
                } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof Worksheet\AutoFilter)) {
2968 1
                    $newAutoFilter = clone $this->autoFilter;
2969 1
                    $this->autoFilter = $newAutoFilter;
2970 1
                    $this->autoFilter->setParent($this);
2971
                } else {
2972 1
                    $this->{$key} = unserialize(serialize($val));
2973
                }
2974
            }
2975
        }
2976 1
    }
2977
2978
    /**
2979
     * Define the code name of the sheet.
2980
     *
2981
     * @param string $pValue Same rule as Title minus space not allowed (but, like Excel, change
2982
     *                       silently space to underscore)
2983
     * @param bool $validate False to skip validation of new title. WARNING: This should only be set
2984
     *                       at parse time (by Readers), where titles can be assumed to be valid.
2985
     *
2986
     * @throws Exception
2987
     *
2988
     * @return objWorksheet
2989
     */
2990 95
    public function setCodeName($pValue, $validate = true)
2991
    {
2992
        // Is this a 'rename' or not?
2993 95
        if ($this->getCodeName() == $pValue) {
2994
            return $this;
2995
        }
2996
2997 95
        if ($validate) {
2998 95
            $pValue = str_replace(' ', '_', $pValue); //Excel does this automatically without flinching, we are doing the same
2999
3000
            // Syntax check
3001
            // throw an exception if not valid
3002 95
            self::checkSheetCodeName($pValue);
3003
3004
            // We use the same code that setTitle to find a valid codeName else not using a space (Excel don't like) but a '_'
3005
3006 95
            if ($this->getParent()) {
3007
                // Is there already such sheet name?
3008 79
                if ($this->getParent()->sheetCodeNameExists($pValue)) {
3009
                    // Use name, but append with lowest possible integer
3010
3011 27
                    if (Shared\StringHelper::countCharacters($pValue) > 29) {
3012
                        $pValue = Shared\StringHelper::substring($pValue, 0, 29);
3013
                    }
3014 27
                    $i = 1;
3015 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...
3016 2
                        ++$i;
3017 2
                        if ($i == 10) {
3018
                            if (Shared\StringHelper::countCharacters($pValue) > 28) {
3019
                                $pValue = Shared\StringHelper::substring($pValue, 0, 28);
3020
                            }
3021 2
                        } elseif ($i == 100) {
3022
                            if (Shared\StringHelper::countCharacters($pValue) > 27) {
3023
                                $pValue = Shared\StringHelper::substring($pValue, 0, 27);
3024
                            }
3025
                        }
3026
                    }
3027
3028 27
                    $pValue = $pValue . '_' . $i; // ok, we have a valid name
3029
                }
3030
            }
3031
        }
3032
3033 95
        $this->codeName = $pValue;
3034
3035 95
        return $this;
3036
    }
3037
3038
    /**
3039
     * Return the code name of the sheet.
3040
     *
3041
     * @return null|string
3042
     */
3043 95
    public function getCodeName()
3044
    {
3045 95
        return $this->codeName;
3046
    }
3047
3048
    /**
3049
     * Sheet has a code name ?
3050
     *
3051
     * @return bool
3052
     */
3053
    public function hasCodeName()
3054
    {
3055
        return !(is_null($this->codeName));
3056
    }
3057
}
3058